diff --git a/CMakeLists.txt b/CMakeLists.txt index 22289d512966c445fc01e4e7fc6a35aa173c1027..4f706950e45b5ef870cbadb559720eb631c6f169 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,11 +15,14 @@ add_subdirectory(relational_store) add_subdirectory(data_share) #add_subdirectory(kv_store) add_subdirectory(preferences) +add_subdirectory(data_object) +add_subdirectory(mock/distributeddb) set(CMAKE_CXX_FLAGS "-std=c++1y -fno-rtti -fvisibility=default -D_GNU_SOURCE") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -fPIC -fpic -ffunction-sections -D_GLIBC_MOCK") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-as-needed -ldl") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wimplicit-fallthrough") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=0") add_subdirectory(googletest) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/utils_native/base/include) @@ -29,7 +32,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/mock/innerkits/ipc/ipc_single/in include_directories(${CMAKE_CURRENT_SOURCE_DIR}/mock/innerkits/ipc/libdbinder/include) set(MAIN_BINARY ${PROJECT_NAME}) -set(links secure mock relational_store data_share preferences jsoncpp crypto) +set(links secure mock relational_store data_share preferences jsoncpp crypto libs data_object) set(links secure mock) add_executable(${MAIN_BINARY} dllmain.cpp) target_link_libraries(${MAIN_BINARY} ${links}) diff --git a/data_object/CMakeLists.txt b/data_object/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b557cf18a485e7f7ea04b604217bb33397d1d5cf --- /dev/null +++ b/data_object/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.10.2) +project(data_object) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_FLAGS "-std=c++1y -fno-rtti -fvisibility=default -D_GNU_SOURCE") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -fPIC -fpic -ffunction-sections -D_GLIBC_MOCK") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-as-needed -ldl") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=0") + +set(MOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../mock) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/innerkitsimpl/src/adaptor data_object_src) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/innerkitsimpl/src/communicator data_object_src) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/jskitsimpl/src/adaptor data_object_src) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/jskitsimpl/src/common data_object_src) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/innerkitsimpl/include/adaptor) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/innerkitsimpl/include/common) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/innerkitsimpl/include/communicator) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/jskitsimpl/include/adaptor ) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/jskitsimpl/include/common ) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces/jskits/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces/innerkits/include) +include(${MOCK_DIR}/include/CMakeLists.txt OPTIONAL) + +set(links secure mock libs) +add_library(data_object SHARED ${data_object_src}) +target_link_libraries(data_object ${links}) \ No newline at end of file diff --git a/data_object/LICENSE b/data_object/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4947287f7b5ccb5d1e8b7b2d3aa5d89f322c160d --- /dev/null +++ b/data_object/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/data_object/README_zh.md b/data_object/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..b5e548f882f7befd08faed84f6294daa475bbef2 --- /dev/null +++ b/data_object/README_zh.md @@ -0,0 +1,80 @@ +# 分布式数据对象 +## 简介 +分布式数据对象管理框架是一款面向对象的内存数据管理框架,向应用开发者提供内存对象的创建、查询、删除、修改、订阅等基本数据对象的管理能力,同时具备分布式能力,满足超级终端场景下,相同应用多设备间的数据对象协同需求。 + +分布式数据对象提供JS接口,让开发者能以使用本地对象的方式使用分布式对象。分布式数据对象支持的数据类型包括数字型、字符型、布尔型等基本类型,同时也支持数组、基本类型嵌套等复杂类型。 + +## 约束 + +• 不同设备间只有相同bundleName的应用才能直接同步 + +• 不建议创建过多分布式对象,每个分布式对象将占用100-150KB内存 + +• 每个对象大小不超过500KB + +• 支持JS接口间的互通,与其他语言不互通 + +• 如对复杂类型的数据进行修改,仅支持修改根属性,暂不支持下级属性修改 + +## 目录 + +``` +//foundation/distributeddatamgr/data_object/ +├── frameworks # 框架层代码 +│ ├── innerkitsimpl # 内部接口实现 +│ │ ├── include +│ │ ├── src +│ │ └── test # C++测试用例 +│ └── jskitsimpl # JS API实现 +│ ├── include +│ ├── src +│ └── test # js测试用例 +├── interfaces # 接口代码 +│ ├── innerkits # 内部接口声明 +│ └── jskits # js接口声明 +├── picture # 资源图库 +└── samples # 开发实例 +``` + +## 接口说明 + +### 引用分布式对象头文件 + +```js +import distributedObject from '@ohos.data.distributedDataObject' +``` + +### 接口 + +| 接口名称 | 描述 | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| function createDistributedObject(source: object): DistributedObject; | 创建分布式对象
source中指定分布式对象中的属性
返回值是创建出的分布式对象,接口见DistrubutedObject | +| function genSessionId(): string; | 随机创建sessionId
返回值是随机创建的sessionId | + +### DistrubutedObject + +| 接口名称 | 描述 | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| setSessionId(sessionId?: string): boolean; | 设置同步的sessionId,可信组网中有多个设备时,多个设备间的对象如果设置为同一个sessionId,就能自动同步
sessionId是不同设备间组网的标识,将sessionId设置为" "或不设置均可退出组网
返回值是操作结果,true表示设置sessionId成功 | +| on(type: 'change', callback: Callback<{ sessionId: string, fields: Array }>): void; | 监听对象数据的变更
type固定为'change'
callback是变更时触发的回调,回调参数sessionId标识变更对象的sessionId,fields标识对象变更的属性名 | +| off(type: 'change', callback?: Callback<{ sessionId: string, fields: Array } | 删除数据对象变更的监听
type固定为'change'
callback为可选参数,不设置表示删除该对象所有变更监听 | +| on(type: 'status', callback: Callback<{ sessionId: string, networkId: string, status: 'online' \| 'offline' }>): void | 监听数据对象上下线的变更
type固定为'status'
callback是变更时触发的回调,回调参数sessionId标识变更对象的sessionId,networkId标识对象设备的networkId,status标识对象为'online'(上线)或'offline'(下线)的状态 | +| off(type: 'status', callback?: Callback<{ sessionId: string, deviceId: string, status: 'online' \| 'offline' }>): void | 删除数据对象上下线变更的监听
type固定为'change'
callback为可选参数,不设置表示删除该数据对象所有上下线监听 | +| save(deviceId: string, callback: AsyncCallback<SaveSuccessResponse>): void | 持久化保存数据对象
deviceId为设备Id,用户自定义。例如"local"。
| +| revokeSave(callback: AsyncCallback): void | 撤回已保存的数据对象。
type固定为'change'
callback为撤回已保存的数据对象回调 | + + + +## 开发实例 + +针对分布式数据对象,有以下开发实例可供参考: + +[备忘录应用](https://gitee.com/openharmony/distributeddatamgr_objectstore/tree/master/samples/distributedNotepad) + +分布式数据对象在备忘录应用中,通过分布式数据对象框架,当用户在某一端设备上新增备忘录事件,修改编辑事件标题和内容以及清空事件列表时,产生的数据变更结果均可以同步刷新显现在可信组网内其他设备上。 + +## 相关仓 +- [分布式数据对象开发指导](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/database/database-distributedobject-guidelines.md) +- [分布式数据对象API文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-data-distributedobject.md) +- [distributeddatamgr\_datamgr](https://gitee.com/openharmony/distributeddatamgr_datamgr) +- [third\_party\_sqlite](https://gitee.com/openharmony/third_party_sqlite) diff --git a/data_object/bundle.json b/data_object/bundle.json new file mode 100644 index 0000000000000000000000000000000000000000..f0598a92a47fbb911ab93c4a2423f1ef1b6b5fad --- /dev/null +++ b/data_object/bundle.json @@ -0,0 +1,87 @@ +{ + "name": "@ohos/distributeddatamgr_data_object", + "version": "", + "description": "The distributed data object management framework is an object-oriented in-memory data management framework", + "homePage": "https://gitee.com/openharmony", + "license": "Apache V2", + "repository": "https://gitee.com/openharmony/distributeddatamgr_data_object ", + "domain": "os", + "language": "", + "publishAs": "code-segment", + "private": false, + "scripts": {}, + "tags": [ + "foundation" + ], + "envs": [], + "dirs": [], + "author": { + "name": "", + "email": "", + "url": "" + }, + "contributors": [ + { + "name": "", + "email": "", + "url": "" + } + ], + "segment": { + "destPath": "foundation/distributeddatamgr/data_object" + }, + "component": { + "name": "data_object", + "subsystem": "distributeddatamgr", + "syscap": [ + "SystemCapability.DistributedDataManager.DataObject.DistributedObject" + ], + "features": [], + "adapted_system_type": [ + "standard" + ], + "rom": "", + "ram": "", + "deps": { + "components": [ + "ability_base", + "ability_runtime", + "hitrace_native", + "dsoftbus", + "distributeddatamgr", + "napi", + "common", + "samgr", + "ipc", + "hiviewdfx_hilog_native", + "libuv", + "utils_base", + "access_token" + ], + "third_party": [] + }, + "build": { + "sub_component": [ + "//foundation/distributeddatamgr/data_object/interfaces/jskits:build_module" + ], + "inner_kits": [ + { + "name": "//foundation/distributeddatamgr/data_object/interfaces/innerkits:distributeddataobject_impl", + "header": { + "header_files": [ + "distributed_object.h", + "distributed_objectstore.h", + "objectstore_errors.h" + ], + "header_base": "//foundation/distributeddatamgr/data_object/interfaces/innerkits" + } + } + ], + "test": [ + "//foundation/distributeddatamgr/data_object/frameworks/innerkitsimpl/test/unittest:unittest", + "//foundation/distributeddatamgr/data_object/frameworks/jskitsimpl/test/unittest:unittest", + "//foundation/distributeddatamgr/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer:fuzztest" + ] + } + } +} \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/include/adaptor/client_adaptor.h b/data_object/frameworks/innerkitsimpl/include/adaptor/client_adaptor.h new file mode 100644 index 0000000000000000000000000000000000000000..86a331d092441216f58ad32e550a2ee7f22653e0 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/adaptor/client_adaptor.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBJECT_CLIENT_ADAPTOR_H +#define OBJECT_CLIENT_ADAPTOR_H + +#include "object_service_proxy.h" +#include "ikvstore_data_service.h" +namespace OHOS::ObjectStore { +class ClientAdaptor { +public: + static sptr GetObjectService(); +private: + static constexpr int32_t DISTRIBUTED_KV_DATA_SERVICE_ABILITY_ID = 1301; + static constexpr int32_t GET_SA_RETRY_TIMES = 3; + static constexpr int32_t RETRY_INTERVAL = 1; + static sptr GetDistributedDataManager(); +}; +} // namespace OHOS::ObjectStore + +#endif // OBJECT_CLIENT_ADAPTOR_H diff --git a/data_object/frameworks/innerkitsimpl/include/adaptor/distributed_object_impl.h b/data_object/frameworks/innerkitsimpl/include/adaptor/distributed_object_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..ad2137658267c839a48b44f0d720d48a2cc4cd89 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/adaptor/distributed_object_impl.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTED_OBJECT_IMPL_H +#define DISTRIBUTED_OBJECT_IMPL_H +#include + +#include "distributed_object.h" +#include "flat_object_store.h" + +namespace OHOS::ObjectStore { +class DistributedObjectImpl : public DistributedObject { +public: + DistributedObjectImpl(const std::string &sessionId, FlatObjectStore *flatObjectStore); + ~DistributedObjectImpl(); + uint32_t PutDouble(const std::string &key, double value) override; + uint32_t PutBoolean(const std::string &key, bool value) override; + uint32_t PutString(const std::string &key, const std::string &value) override; + uint32_t GetDouble(const std::string &key, double &value) override; + uint32_t GetBoolean(const std::string &key, bool &value) override; + uint32_t GetString(const std::string &key, std::string &value) override; + uint32_t PutComplex(const std::string &key, const std::vector &value) override; + uint32_t GetComplex(const std::string &key, std::vector &value) override; + std::string &GetSessionId() override; + uint32_t Save(const std::string &deviceId) override; + uint32_t RevokeSave() override; + uint32_t GetType(const std::string &key, Type &type) override; + +private: + std::string sessionId_; + FlatObjectStore *flatObjectStore_ = nullptr; +}; +} // namespace OHOS::ObjectStore + +#endif // DISTRIBUTED_OBJECT_IMPL_H diff --git a/data_object/frameworks/innerkitsimpl/include/adaptor/distributed_objectstore_impl.h b/data_object/frameworks/innerkitsimpl/include/adaptor/distributed_objectstore_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..d9e9945249f69bc2cb09bbd774fd06a1105f3003 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/adaptor/distributed_objectstore_impl.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTED_OBJECTSTORE_IMPL_H +#define DISTRIBUTED_OBJECTSTORE_IMPL_H + +#include + +#include + +#include "distributed_objectstore.h" + +namespace OHOS::ObjectStore { +class WatcherProxy; +enum SyncStatus { + SYNC_START, + SYNCING, + SYNC_SUCCESS, + SYNC_FAIL, +}; +class DistributedObjectStoreImpl : public DistributedObjectStore { +public: + DistributedObjectStoreImpl(FlatObjectStore *flatObjectStore); + ~DistributedObjectStoreImpl() override; + uint32_t Get(const std::string &sessionId, DistributedObject **object) override; + DistributedObject *CreateObject(const std::string &sessionId) override; + uint32_t DeleteObject(const std::string &sessionId) override; + uint32_t Watch(DistributedObject *object, std::shared_ptr watcher) override; + uint32_t UnWatch(DistributedObject *object) override; + uint32_t SetStatusNotifier(std::shared_ptr notifier) override; + void TriggerSync() override; + void TriggerRestore(std::function notifier) override; + +private: + DistributedObject *CacheObject(const std::string &sessionId, FlatObjectStore *flatObjectStore); + void RemoveCacheObject(const std::string &sessionId); + FlatObjectStore *flatObjectStore_ = nullptr; + std::map> watchers_; + std::shared_mutex dataMutex_{}; + std::vector objects_{}; +}; +class StatusNotifierProxy : public StatusWatcher { +public: + virtual ~StatusNotifierProxy(); + StatusNotifierProxy(const std::shared_ptr ¬ifier); + void OnChanged( + const std::string &sessionId, const std::string &networkId, const std::string &onlineStatus) override; + +private: + std::shared_ptr notifier; +}; +class WatcherProxy : public FlatObjectWatcher { +public: + WatcherProxy(const std::shared_ptr objectWatcher, const std::string &sessionId); + void OnChanged(const std::string &sessionid, const std::vector &changedData) override; + +private: + std::shared_ptr objectWatcher_; +}; +} // namespace OHOS::ObjectStore + +#endif // DISTRIBUTED_OBJECTSTORE_H diff --git a/data_object/frameworks/innerkitsimpl/include/adaptor/flat_object_storage_engine.h b/data_object/frameworks/innerkitsimpl/include/adaptor/flat_object_storage_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..17e033c994e1a518be9e65a349a1d6f402779944 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/adaptor/flat_object_storage_engine.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLAT_OBJECT_STORAGE_ENGINE_H +#define FLAT_OBJECT_STORAGE_ENGINE_H + +#include +#include +#include +#include + +#include "kv_store_delegate_manager.h" +#include "object_storage_engine.h" + +namespace OHOS::ObjectStore { +class FlatObjectStorageEngine : public ObjectStorageEngine { +public: + FlatObjectStorageEngine() = default; + ~FlatObjectStorageEngine() override; + uint32_t Open(const std::string &bundleName) override; + uint32_t Close() override; + uint32_t DeleteTable(const std::string &key) override; + uint32_t CreateTable(const std::string &key) override; + uint32_t GetTable(const std::string &key, std::map &result) override; + uint32_t UpdateItem(const std::string &key, const std::string &itemKey, Value &value) override; + uint32_t UpdateItems(const std::string &key, const std::map> &data) override; + uint32_t GetItem(const std::string &key, const std::string &itemKey, Value &value) override; + uint32_t GetItems(const std::string &key, std::map> &data) override; + uint32_t RegisterObserver(const std::string &key, std::shared_ptr watcher) override; + uint32_t UnRegisterObserver(const std::string &key) override; + uint32_t SetStatusNotifier(std::shared_ptr watcher) override; + uint32_t SyncAllData(const std::string &sessionId, const std::vector &deviceIds, + const std::function &)> &onComplete); + bool isOpened_ = false; + +private: + std::mutex operationMutex_{}; + std::shared_ptr storeManager_; + std::map delegates_; + std::map> observerMap_; + std::shared_ptr statusWatcher_ = nullptr; +}; +} // namespace OHOS::ObjectStore +#endif diff --git a/data_object/frameworks/innerkitsimpl/include/adaptor/flat_object_store.h b/data_object/frameworks/innerkitsimpl/include/adaptor/flat_object_store.h new file mode 100644 index 0000000000000000000000000000000000000000..794f261799a02a726129fdbb328db2d6c8dfc012 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/adaptor/flat_object_store.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLAT_OBJECT_STORE_H +#define FLAT_OBJECT_STORE_H + +#include +#include + +#include "bytes.h" +#include "flat_object_storage_engine.h" +#include "condition_lock.h" + +namespace OHOS::ObjectStore { +class FlatObjectWatcher : public TableWatcher { +public: + FlatObjectWatcher(const std::string &sessionId) : TableWatcher(sessionId) + { + } + void OnChanged(const std::string &sessionid, const std::vector &changedData) override; +}; + +class CacheManager { +public: + CacheManager(); + uint32_t Save(const std::string &bundleName, const std::string &sessionId, const std::string &deviceId, + const std::map> &objectData); + uint32_t RevokeSave(const std::string &bundleName, const std::string &sessionId); + int32_t ResumeObject(const std::string &bundleName, const std::string &sessionId, + std::function> &data)> &callback); +private: + int32_t SaveObject(const std::string &bundleName, const std::string &sessionId, + const std::string &deviceId, const std::map> &objectData, + const std::function &)> &callback); + int32_t RevokeSaveObject( + const std::string &bundleName, const std::string &sessionId, std::function &callback); + std::mutex mutex_; +}; + +class FlatObjectStore { +public: + explicit FlatObjectStore(const std::string &bundleName); + ~FlatObjectStore(); + uint32_t CreateObject(const std::string &sessionId); + uint32_t Delete(const std::string &objectId); + uint32_t Watch(const std::string &objectId, std::shared_ptr watcher); + uint32_t UnWatch(const std::string &objectId); + uint32_t Put(const std::string &sessionId, const std::string &key, std::vector value); + uint32_t Get(std::string &sessionId, const std::string &key, Bytes &value); + uint32_t SetStatusNotifier(std::shared_ptr sharedPtr); + uint32_t SyncAllData(const std::string &sessionId, + const std::function &)> &onComplete); + uint32_t Save(const std::string &sessionId, const std::string &deviceId); + uint32_t RevokeSave(const std::string &sessionId); + +private: + std::shared_ptr storageEngine_; + CacheManager *cacheManager_; + std::string bundleName_; +}; +} // namespace OHOS::ObjectStore + +#endif // FLAT_OBJECT_STORE_H diff --git a/data_object/frameworks/innerkitsimpl/include/adaptor/object_callback.h b/data_object/frameworks/innerkitsimpl/include/adaptor/object_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..25918118638052d82a77732ebce9bf754ac2ae71 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/adaptor/object_callback.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBJECT_CALLBACK_H +#define OBJECT_CALLBACK_H + +#include "iobject_callback.h" + +namespace OHOS { +namespace ObjectStore { +using namespace DistributedObject; +class ObjectSaveCallback : public ObjectSaveCallbackStub { +public: + ObjectSaveCallback(const std::function &)> &callback); + void Completed(const std::map &results) override; + +private: + const std::function &)> callback_; +}; + +class ObjectRevokeSaveCallback : public ObjectRevokeSaveCallbackStub { +public: + ObjectRevokeSaveCallback(const std::function &callback); + void Completed(int32_t) override; + +private: + const std::function callback_; +}; + +class ObjectRetrieveCallback : public ObjectRetrieveCallbackStub { +public: + ObjectRetrieveCallback(const std::function> &)> &callback); + void Completed(const std::map> &results) override; + +private: + const std::function> &)> callback_; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // OBJECT_CALLBACK_H diff --git a/data_object/frameworks/innerkitsimpl/include/adaptor/object_service.h b/data_object/frameworks/innerkitsimpl/include/adaptor/object_service.h new file mode 100644 index 0000000000000000000000000000000000000000..bc2b54a6441d833a7904cea2b52819ceb5363ab9 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/adaptor/object_service.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTED_OBJECT_SERVICE_H +#define DISTRIBUTED_OBJECT_SERVICE_H + +#include +#include +#include + +#include "iobject_callback.h" +namespace OHOS::ObjectStore { +class ObjectService { +public: + virtual int32_t ObjectStoreSave(const std::string &bundleName, const std::string &sessionId, + const std::string &deviceId, const std::map> &data, + sptr callback) = 0; + virtual int32_t ObjectStoreRetrieve( + const std::string &bundleName, const std::string &sessionId, sptr callback) = 0; + virtual int32_t ObjectStoreRevokeSave( + const std::string &bundleName, const std::string &sessionId, sptr callback) = 0; +}; +} // namespace OHOS::ObjectStore +#endif diff --git a/data_object/frameworks/innerkitsimpl/include/adaptor/object_storage_engine.h b/data_object/frameworks/innerkitsimpl/include/adaptor/object_storage_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..74932e5911fa885987200f291900bf48d56d3ab5 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/adaptor/object_storage_engine.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBJECT_STORAGE_ENGINE_H +#define OBJECT_STORAGE_ENGINE_H + +#include +#include +#include + +#include "kv_store_observer.h" +#include "watcher.h" + +namespace OHOS::ObjectStore { +using Key = std::vector; +using Value = std::vector; +using Field = std::vector; + +class TableWatcher : public Watcher { +public: + TableWatcher(const std::string &sessionId) : Watcher(sessionId) + { + } + void OnChanged(const std::string &sessionid, const std::vector &changedData) override; +}; + +class StatusWatcher { +public: + virtual void OnChanged( + const std::string &sessionId, const std::string &networkId, const std::string &onlineStatus) = 0; +}; + +class ObjectStorageEngine { +public: + ObjectStorageEngine(const ObjectStorageEngine &) = delete; + ObjectStorageEngine &operator=(const ObjectStorageEngine &) = delete; + ObjectStorageEngine(ObjectStorageEngine &&) = delete; + ObjectStorageEngine &operator=(ObjectStorageEngine &&) = delete; + ObjectStorageEngine() = default; + virtual ~ObjectStorageEngine() = default; + virtual uint32_t Open(const std::string &bundleName) = 0; + virtual uint32_t Close() = 0; + virtual uint32_t DeleteTable(const std::string &key) = 0; + virtual uint32_t CreateTable(const std::string &key) = 0; + virtual uint32_t GetTable(const std::string &key, std::map &result) = 0; + virtual uint32_t UpdateItem(const std::string &key, const std::string &itemKey, Value &value) = 0; + virtual uint32_t UpdateItems(const std::string &key, const std::map> &data) = 0; + virtual uint32_t GetItem(const std::string &key, const std::string &itemKey, Value &value) = 0; + virtual uint32_t GetItems(const std::string &key, std::map> &data) = 0; + virtual uint32_t RegisterObserver(const std::string &key, std::shared_ptr watcher) = 0; + virtual uint32_t UnRegisterObserver(const std::string &key) = 0; + virtual uint32_t SetStatusNotifier(std::shared_ptr watcher) = 0; +}; +} // namespace OHOS::ObjectStore +#endif \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/include/adaptor/watcher.h b/data_object/frameworks/innerkitsimpl/include/adaptor/watcher.h new file mode 100644 index 0000000000000000000000000000000000000000..cd3ad64622e33ff95406fc8853cb1fd59970f15a --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/adaptor/watcher.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WATCHER_H +#define WATCHER_H + +#include + +#include "kv_store_delegate_manager.h" +#include "logger.h" + +namespace OHOS::ObjectStore { +class Watcher : public DistributedDB::KvStoreObserver { +public: + Watcher(const std::string &sessionId); + virtual ~Watcher() = default; + virtual void OnChanged(const std::string &sessionid, const std::vector &changedData) = 0; + + void OnChange(const DistributedDB::KvStoreChangedData &data) override; + +private: + std::string sessionId_; +}; +} // namespace OHOS::ObjectStore + +#endif // WATCHER_H \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/include/common/bytes.h b/data_object/frameworks/innerkitsimpl/include/common/bytes.h new file mode 100644 index 0000000000000000000000000000000000000000..11f9490322aedba4556d65bc28168d85164dc564 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/common/bytes.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BYTES_H +#define BYTES_H + +#include +#include + +namespace OHOS::ObjectStore { +using Bytes = std::vector; +static const char *FIELDS_PREFIX = "p_"; +static const int32_t FIELDS_PREFIX_LEN = 2; +} // namespace OHOS::ObjectStore + +#endif // BYTES_H diff --git a/data_object/frameworks/innerkitsimpl/include/common/condition_lock.h b/data_object/frameworks/innerkitsimpl/include/common/condition_lock.h new file mode 100644 index 0000000000000000000000000000000000000000..16d720c43d098229cc29453b466a26432f795ae9 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/common/condition_lock.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CONDITION_LOCK_H +#define CONDITION_LOCK_H + +#include +#include + +namespace OHOS::ObjectStore { +template +class ConditionLock { +public: + explicit ConditionLock() {} + ~ConditionLock() {} +public: + void Notify(const T &data) + { + std::lock_guard lock(mutex_); + data_ = data; + isSet_ = true; + cv_.notify_one(); + } + + T Wait() + { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this]() { return isSet_; }); + T data = data_; + cv_.notify_one(); + return data; + } + + void Clear() + { + std::lock_guard lock(mutex_); + isSet_ = false; + cv_.notify_one(); + } + +private: + bool isSet_ = false; + T data_; + std::mutex mutex_; + std::condition_variable cv_; +}; +} // namespace OHOS::ObjectStore + +#endif // CONDITION_LOCK_H diff --git a/data_object/frameworks/innerkitsimpl/include/common/logger.h b/data_object/frameworks/innerkitsimpl/include/common/logger.h new file mode 100644 index 0000000000000000000000000000000000000000..20b4ade4d33211c80598656555dff98c99bce9ef --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/common/logger.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBJECT_STORE_LOGGER_H +#define OBJECT_STORE_LOGGER_H +#include +#ifdef HILOG_ENABLE +#include "hilog/log.h" +namespace OHOS::ObjectStore { +static const OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD001652, "ObjectStore-x" }; + +#define LOG_DEBUG(fmt, ...) \ + ((void)OHOS::HiviewDFX::HiLog::Debug( \ + LABEL, "%{public}d: %{public}s " fmt " ", __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define LOG_INFO(fmt, ...) \ + ((void)OHOS::HiviewDFX::HiLog::Info( \ + LABEL, "%{public}d: %{public}s " fmt " ", __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define LOG_WARN(fmt, ...) \ + ((void)OHOS::HiviewDFX::HiLog::Warn( \ + LABEL, "%{public}d: %{public}s " fmt " ", __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define LOG_ERROR(fmt, ...) \ + ((void)OHOS::HiviewDFX::HiLog::Error( \ + LABEL, "%{public}d: %{public}s " fmt " ", __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define LOG_FATAL(fmt, ...) \ + ((void)OHOS::HiviewDFX::HiLog::Fatal( \ + LABEL, "%{public}d: %{public}s " fmt " ", __LINE__, __FUNCTION__, ##__VA_ARGS__)) +} // namespace OHOS::ObjectStore +#else +#include +#include + +#define LOG_DEBUG(fmt, ...) \ + printf("[D][ObjectStore]%s:%d %s: " fmt "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) +#define LOG_ERROR(fmt, ...) \ + printf("[E][ObjectStore]%s:%d %s: " fmt "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) +#define LOG_INFO(fmt, ...) \ + printf("[I][ObjectStore]%s:%d %s: " fmt "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) +#define LOG_WARN(fmt, ...) \ + printf("[W][ObjectStore]%s:%d %s: " fmt "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) +#endif // #ifdef HILOG_ENABLE +#endif // OBJECT_STORE_LOGGER_H diff --git a/data_object/frameworks/innerkitsimpl/include/common/macro.h b/data_object/frameworks/innerkitsimpl/include/common/macro.h new file mode 100644 index 0000000000000000000000000000000000000000..0939b0d02dbf1594469a3339ace32d19fbfc8d6b --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/common/macro.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MACRO_H +#define MACRO_H + +namespace OHOS::ObjectStore { +#define DISABLE_COPY_AND_MOVE(ClassName) \ + ClassName(const ClassName &) = delete; \ + ClassName(ClassName &&) = delete; \ + ClassName &operator=(const ClassName &) = delete; \ + ClassName &operator=(ClassName &&) = delete +} // namespace OHOS::ObjectStore +#endif // MACRO_H \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/include/common/object_utils.h b/data_object/frameworks/innerkitsimpl/include/common/object_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..f113729834062c18e0a8611c900c2d63b9c07dc9 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/common/object_utils.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OBJECT_UTILS_H +#define OBJECT_UTILS_H + +#include + +namespace OHOS::ObjectStore { +class ObjectUtils final { +public: + ObjectUtils() = delete; + ~ObjectUtils() = delete; + + static std::string GenObjectIdPrefix( + const std::string &host, const std::string &user, const std::string &bundle, const std::string &store); + + static std::string GetObjectHost(const std::string &objectId); + + static std::string GetObjectStoreName(const std::string &objectId); +}; +} // namespace OHOS::ObjectStore + +#endif \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/include/common/string_utils.h b/data_object/frameworks/innerkitsimpl/include/common/string_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..de81a4febcf2ef4955f620c7d91c27e638e09e85 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/common/string_utils.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STRING_UTILS_H +#define STRING_UTILS_H + +#include +#include +#include +#include + +#include "bytes.h" +#include "distributed_object.h" +#include "logger.h" +#include "objectstore_errors.h" + +namespace OHOS::ObjectStore { +class StringUtils final { +public: + StringUtils() = delete; + ~StringUtils() = delete; + static std::vector StrToBytes(const std::string &src) + { + std::vector dst; + dst.resize(src.size()); + dst.assign(src.begin(), src.end()); + return dst; + } + + static std::string BytesToStr(const std::vector src) + { + std::string result; + Bytes rstStr(src.begin(), src.end()); + result.assign(reinterpret_cast(rstStr.data()), rstStr.size()); + return result; + } + static uint32_t BytesToStrWithType(Bytes input, std::string &str) + { + uint32_t len = input.end() - input.begin(); + if (len <= sizeof(Type)) { + LOG_ERROR("StringUtils:BytesToStrWithType get input len err."); + return ERR_DATA_LEN; + } + std::vector::const_iterator first = input.begin() + sizeof(Type); + std::vector::const_iterator end = input.end(); + Bytes rstStr(first, end); + str.assign(reinterpret_cast(rstStr.data()), rstStr.size()); + return SUCCESS; + } +}; +} // namespace OHOS::ObjectStore +#endif // STRING_UTILS_H diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/app_data_change_listener.h b/data_object/frameworks/innerkitsimpl/include/communicator/app_data_change_listener.h new file mode 100644 index 0000000000000000000000000000000000000000..8a49723a60e657caf903e01b579aeca50ca51daf --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/app_data_change_listener.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef APP_DISTRIBUTEDDATA_INCLUDE_DATA_CHANGE_LISTENER_H +#define APP_DISTRIBUTEDDATA_INCLUDE_DATA_CHANGE_LISTENER_H + +#include "app_types.h" +#include "visibility.h" +namespace OHOS { +namespace ObjectStore { +class AppDataChangeListener { +public: + KVSTORE_API AppDataChangeListener() = default; + KVSTORE_API virtual ~AppDataChangeListener(){}; + + KVSTORE_API virtual void OnMessage( + const DeviceInfo &info, const uint8_t *ptr, const int size, const PipeInfo &pipeInfo) const = 0; +}; +} // namespace ObjectStore +} // namespace OHOS +#endif // APP_DISTRIBUTEDDATA_INCLUDE_DATA_CHANGE_LISTENER_H diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/app_device_handler.h b/data_object/frameworks/innerkitsimpl/include/communicator/app_device_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..99c72dae8aa1908dbb662eaaec665163546945b3 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/app_device_handler.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDATAFWK_SRC_DEVICE_HANDLER_H +#define DISTRIBUTEDDATAFWK_SRC_DEVICE_HANDLER_H +#include "softbus_adapter.h" + +namespace OHOS { +namespace ObjectStore { +class AppDeviceHandler { +public: + ~AppDeviceHandler(); + explicit AppDeviceHandler(); + void Init(); + + // add DeviceChangeListener to watch device change; + Status StartWatchDeviceChange(const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo); + // stop DeviceChangeListener to watch device change; + Status StopWatchDeviceChange(const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo); + + DeviceInfo GetLocalDevice(); + std::vector GetDeviceList() const; + + std::string GetUdidByNodeId(const std::string &nodeId) const; + // get local device node information; + DeviceInfo GetLocalBasicInfo() const; + // get all remote connected device's node information; + std::vector GetRemoteNodesBasicInfo() const; + +private: + std::shared_ptr softbusAdapter_{}; +}; +} // namespace ObjectStore +} // namespace OHOS +#endif // DISTRIBUTEDDATAFWK_SRC_DEVICE_HANDLER_H diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/app_device_status_change_listener.h b/data_object/frameworks/innerkitsimpl/include/communicator/app_device_status_change_listener.h new file mode 100644 index 0000000000000000000000000000000000000000..257a5e4f6b5703fea4dfb8bb2f9ffe8076be702d --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/app_device_status_change_listener.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef APP_DEVICE_STATUS_CHANGE_LISTENER_H +#define APP_DEVICE_STATUS_CHANGE_LISTENER_H + +#include "app_types.h" + +namespace OHOS { +namespace ObjectStore { +enum class ChangeLevelType { + HIGH, + LOW, + MIN, +}; +class AppDeviceStatusChangeListener { +public: + KVSTORE_API virtual ~AppDeviceStatusChangeListener(){}; + KVSTORE_API virtual void OnDeviceChanged(const DeviceInfo &info, const DeviceChangeType &type) const = 0; + KVSTORE_API virtual ChangeLevelType GetChangeLevelType() const + { + return ChangeLevelType::LOW; + } +}; +} // namespace ObjectStore +} // namespace OHOS + +#endif // APP_DEVICE_STATUS_CHANGE_LISTENER_H diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/app_pipe_handler.h b/data_object/frameworks/innerkitsimpl/include/communicator/app_pipe_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..f7e43909999c82e6deddd96c1f9ce91904945898 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/app_pipe_handler.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDATAFWK_SRC_PIPE_HANDLER_H +#define DISTRIBUTEDDATAFWK_SRC_PIPE_HANDLER_H + +#include +#include +#include +#include + +#include "app_data_change_listener.h" +#include "app_types.h" +#include "logger.h" +#include "softbus_adapter.h" + +namespace OHOS { +namespace ObjectStore { +class AppPipeHandler { +public: + ~AppPipeHandler(); + explicit AppPipeHandler(const PipeInfo &pipeInfo); + + // add DataChangeListener to watch data change; + Status StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo); + // stop DataChangeListener to watch data change; + Status StopWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo); + // Send data to other device, function will be called back after sent to notify send result. + Status SendData( + const PipeInfo &pipeInfo, const DeviceId &deviceId, const uint8_t *ptr, int size, const MessageInfo &info); + bool IsSameStartedOnPeer(const struct PipeInfo &pipeInfo, const struct DeviceId &peer); + + int CreateSessionServer(const std::string &sessionName) const; + + int RemoveSessionServer(const std::string &sessionName) const; + +private: + PipeInfo pipeInfo_; + std::shared_ptr softbusAdapter_{}; +}; +} // namespace ObjectStore +} // namespace OHOS +#endif /* DISTRIBUTEDDATAFWK_SRC_PIPE_HANDLER_H */ diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/app_pipe_mgr.h b/data_object/frameworks/innerkitsimpl/include/communicator/app_pipe_mgr.h new file mode 100644 index 0000000000000000000000000000000000000000..493e7075f8fb12dc6655e4decea37ac9fa0c710d --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/app_pipe_mgr.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDATAMGR_APP_PIPE_MGR_H +#define DISTRIBUTEDDATAMGR_APP_PIPE_MGR_H + +#include +#include + +#include "app_data_change_listener.h" +#include "app_pipe_handler.h" +#include "app_types.h" +#include "logger.h" + +namespace OHOS { +namespace ObjectStore { +class AppPipeMgr { +public: + explicit AppPipeMgr() + { + } + ~AppPipeMgr() + { + } + // add DataChangeListener to watch data change; + Status StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo); + + // stop DataChangeListener to watch data change; + Status StopWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo); + + // Send data to other device, function will be called back after sent to notify send result. + Status SendData( + const PipeInfo &pipeInfo, const DeviceId &deviceId, const uint8_t *ptr, int size, const MessageInfo &info); + // start server + Status Start(const PipeInfo &pipeInfo); + // stop server + Status Stop(const PipeInfo &pipeInfo); + + bool IsSameStartedOnPeer(const struct PipeInfo &pipeInfo, const struct DeviceId &peer); + +private: + std::mutex dataBusMapMutex_{}; + std::map> dataBusMap_{}; +}; +} // namespace ObjectStore +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_APP_PIPE_MGR_H diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/app_types.h b/data_object/frameworks/innerkitsimpl/include/communicator/app_types.h new file mode 100644 index 0000000000000000000000000000000000000000..f1c1267a4b4fa7bb224f7fe942c0a879ba24e14e --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/app_types.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef APP_DISTRIBUTED_KVSTORE_APP_TYPES_H +#define APP_DISTRIBUTED_KVSTORE_APP_TYPES_H + +#include + +#include +#include +#include + +#include "visibility.h" + +namespace OHOS { +namespace ObjectStore { +struct PipeInfo { + std::string pipeId; +}; + +struct DeviceInfo { + std::string deviceId; + std::string deviceName; + std::string deviceType; +}; + +enum class MessageType { + DEFAULT = 0, +}; + +struct MessageInfo { + MessageType msgType; +}; + +enum class DeviceChangeType { + DEVICE_OFFLINE = 0, + DEVICE_ONLINE = 1, +}; + +struct DeviceId { + std::string deviceId; +}; + +// app_distributed_data_manager using sub error code 0 +constexpr ErrCode APP_DISTRIBUTEDDATAMGR_ERR_OFFSET = ErrCodeOffset(SUBSYS_DISTRIBUTEDDATAMNG, 0); + +enum class Status { + SUCCESS = ERR_OK, + ERROR = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET, + INVALID_ARGUMENT = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 1, + ILLEGAL_STATE = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 2, + KEY_NOT_FOUND = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 7, + REPEATED_REGISTER = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 14, + CREATE_SESSION_ERROR = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 15, +}; +} // namespace ObjectStore +} // namespace OHOS +#endif // APP_DISTRIBUTED_KVSTORE_TYPES_H diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/ark_communication_provider.h b/data_object/frameworks/innerkitsimpl/include/communicator/ark_communication_provider.h new file mode 100644 index 0000000000000000000000000000000000000000..a6189ddea02cf8c4152d5beea13259208de5f0f1 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/ark_communication_provider.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDATAFWK_ARK_COMMUNICATION_PROVIDER_H +#define DISTRIBUTEDDATAFWK_ARK_COMMUNICATION_PROVIDER_H + +#include "app_device_handler.h" +#include "app_pipe_mgr.h" +#include "communication_provider_impl.h" +#include "nocopyable.h" + +namespace OHOS { +namespace ObjectStore { +class ArkCommunicationProvider : public CommunicationProviderImpl { +public: + static CommunicationProvider &Init(); + + ~ArkCommunicationProvider() override{}; + +private: + DISALLOW_COPY_AND_MOVE(ArkCommunicationProvider); + ArkCommunicationProvider(); + AppPipeMgr appPipeMgrImpl_{}; + AppDeviceHandler appDeviceHandlerImpl_{}; + bool isInited = false; +}; +} // namespace ObjectStore +} // namespace OHOS +#endif // DISTRIBUTEDDATAFWK_ARK_COMMUNICATION_PROVIDER_H diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/communication_provider.h b/data_object/frameworks/innerkitsimpl/include/communicator/communication_provider.h new file mode 100644 index 0000000000000000000000000000000000000000..70fb9b96cb2cda7d88f96f6af8eecdc4dd0054f8 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/communication_provider.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDATA_COMMUNICATION_PROVIDER_H +#define DISTRIBUTEDDATA_COMMUNICATION_PROVIDER_H + +#include +#include + +#include "app_data_change_listener.h" +#include "app_device_status_change_listener.h" +#include "app_types.h" +#include "visibility.h" +namespace OHOS { +namespace ObjectStore { +class CommunicationProvider { +public: + // constructor + KVSTORE_API CommunicationProvider(){}; + + // destructor + KVSTORE_API virtual ~CommunicationProvider(){}; + + // add DeviceChangeListener to watch device change + KVSTORE_API + virtual Status StartWatchDeviceChange(const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo) = 0; + + // stop DeviceChangeListener to watch device change + KVSTORE_API + virtual Status StopWatchDeviceChange(const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo) = 0; + + // add DataChangeListener to watch data change + KVSTORE_API + virtual Status StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) = 0; + + // stop DataChangeListener to watch data change + KVSTORE_API virtual Status StopWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) = 0; + + // Send data to other device, function will be called back after sent to notify send result + KVSTORE_API + virtual Status SendData(const PipeInfo &pipeInfo, const DeviceId &deviceId, const uint8_t *ptr, int size, + const MessageInfo &info = { MessageType::DEFAULT }) = 0; + + // Get online deviceList + KVSTORE_API virtual std::vector GetDeviceList() const = 0; + + // Get local device information + KVSTORE_API virtual DeviceInfo GetLocalDevice() const = 0; + + // start one server to listen data from other devices; + KVSTORE_API virtual Status Start(const PipeInfo &pipeInfo) = 0; + + // stop server + KVSTORE_API virtual Status Stop(const PipeInfo &pipeInfo) = 0; + + // user should use this method to get instance of CommunicationProvider; + KVSTORE_API static CommunicationProvider &GetInstance(); + + // check peer device pipeInfo Process + KVSTORE_API virtual bool IsSameStartedOnPeer(const PipeInfo &pipeInfo, const DeviceId &peer) const = 0; +}; +} // namespace ObjectStore +} // namespace OHOS +#endif // DISTRIBUTEDDATA_COMMUNICATION_PROVIDER_H diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/communication_provider_impl.h b/data_object/frameworks/innerkitsimpl/include/communicator/communication_provider_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..ec22e0884cd797d029d586168e213eda106a3ea2 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/communication_provider_impl.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDATA_SRC_COMMUNICATION_PROVIDER_IMPL_H +#define DISTRIBUTEDDATA_SRC_COMMUNICATION_PROVIDER_IMPL_H + +#include + +#include "app_device_handler.h" +#include "app_pipe_mgr.h" +#include "communication_provider.h" + +namespace OHOS { +namespace ObjectStore { +class CommunicationProviderImpl : public CommunicationProvider { +public: + CommunicationProviderImpl(AppPipeMgr &appPipeMgr, AppDeviceHandler &deviceHandler); + + virtual ~CommunicationProviderImpl(); + + // add DeviceChangeListener to watch device change; + Status StartWatchDeviceChange(const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo) override; + + // stop watching device change; + Status StopWatchDeviceChange(const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo) override; + + // add DataChangeListener to watch data change; + Status StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) override; + + // stop watching data change; + Status StopWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) override; + + // Send data to other device, function will be called back after sent to notify send result. + Status SendData(const PipeInfo &pipeInfo, const DeviceId &deviceId, const uint8_t *ptr, int size, + const MessageInfo &info) override; + + // Get online deviceList + std::vector GetDeviceList() const override; + + // Get local device information + DeviceInfo GetLocalDevice() const override; + + // start 1 server to listen data from other devices; + Status Start(const PipeInfo &pipeInfo) override; + + // stop server + Status Stop(const PipeInfo &pipeInfo) override; + + bool IsSameStartedOnPeer(const PipeInfo &pipeInfo, const DeviceId &peer) const override; + +protected: + virtual Status Initialize(); + + static std::mutex mutex_; + +private: + AppPipeMgr &appPipeMgr_; + AppDeviceHandler &appDeviceHandler_; +}; +} // namespace ObjectStore +} // namespace OHOS +#endif /* DISTRIBUTEDDATA_SRC_COMMUNICATION_PROVIDER_IMPL_H */ diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/process_communicator_impl.h b/data_object/frameworks/innerkitsimpl/include/communicator/process_communicator_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..65a9280202d3cf2c4c820764298baf0e81643943 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/process_communicator_impl.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PROCESS_COMMUNICATOR_IMPL_H +#define PROCESS_COMMUNICATOR_IMPL_H + +#include + +#include "communication_provider.h" +#include "iprocess_communicator.h" + +namespace OHOS { +namespace ObjectStore { +class ProcessCommunicatorImpl + : public DistributedDB::IProcessCommunicator + , private AppDataChangeListener + , private AppDeviceStatusChangeListener { +public: + using DBStatus = DistributedDB::DBStatus; + using OnDeviceChange = DistributedDB::OnDeviceChange; + using OnDataReceive = DistributedDB::OnDataReceive; + using DeviceInfos = DistributedDB::DeviceInfos; + KVSTORE_API ProcessCommunicatorImpl(); + KVSTORE_API ~ProcessCommunicatorImpl() override; + + KVSTORE_API DBStatus Start(const std::string &processLabel) override; + KVSTORE_API DBStatus Stop() override; + + KVSTORE_API DBStatus RegOnDeviceChange(const OnDeviceChange &callback) override; + KVSTORE_API DBStatus RegOnDataReceive(const OnDataReceive &callback) override; + + KVSTORE_API DBStatus SendData(const DeviceInfos &dstDevInfo, const uint8_t *data, uint32_t length) override; + KVSTORE_API uint32_t GetMtuSize() override; + KVSTORE_API uint32_t GetMtuSize(const DeviceInfos &devInfo) override; + KVSTORE_API DeviceInfos GetLocalDeviceInfos() override; + KVSTORE_API std::vector GetRemoteOnlineDeviceInfosList() override; + KVSTORE_API bool IsSameProcessLabelStartedOnPeerDevice(const DeviceInfos &peerDevInfo) override; + +private: + void OnMessage(const DeviceInfo &info, const uint8_t *ptr, const int size, const PipeInfo &pipeInfo) const override; + void OnDeviceChanged(const DeviceInfo &info, const DeviceChangeType &type) const override; + + std::string thisProcessLabel_; + OnDeviceChange onDeviceChangeHandler_; + OnDataReceive onDataReceiveHandler_; + mutable std::mutex onDeviceChangeMutex_; + mutable std::mutex onDataReceiveMutex_; + + static constexpr uint32_t MTU_SIZE = 4096 * 1024; // the max transmission unit size(4M - 80B) + static constexpr uint32_t MTU_SIZE_WATCH = 81920; // the max transmission unit size(80K) + static constexpr const char *SMART_WATCH_TYPE = "SMART_WATCH"; + static constexpr const char *CHILDREN_WATCH_TYPE = "CHILDREN_WATCH"; +}; +} // namespace ObjectStore +} // namespace OHOS +#endif // PROCESS_COMMUNICATOR_IMPL_H diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/softbus_adapter.h b/data_object/frameworks/innerkitsimpl/include/communicator/softbus_adapter.h new file mode 100644 index 0000000000000000000000000000000000000000..e91d39bc5882345504700ead1360830842bf9e55 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/softbus_adapter.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDATAFWK_SRC_SOFTBUS_ADAPTER_H +#define DISTRIBUTEDDATAFWK_SRC_SOFTBUS_ADAPTER_H +#include +#include +#include +#include + +#include "app_data_change_listener.h" +#include "app_device_status_change_listener.h" +#include "app_types.h" +#include "session.h" +#include "softbus_bus_center.h" +#include "condition_lock.h" + +namespace OHOS { +namespace ObjectStore { +class SoftBusAdapter { +public: + SoftBusAdapter(); + ~SoftBusAdapter(); + static std::shared_ptr GetInstance(); + + void Init(); + // add DeviceChangeListener to watch device change; + Status StartWatchDeviceChange(const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo); + // stop DeviceChangeListener to watch device change; + Status StopWatchDeviceChange(const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo); + void NotifyAll(const DeviceInfo &deviceInfo, const DeviceChangeType &type); + DeviceInfo GetLocalDevice(); + std::vector GetDeviceList() const; + std::string GetUdidByNodeId(const std::string &nodeId) const; + // get local device node information; + DeviceInfo GetLocalBasicInfo() const; + // get all remote connected device's node information; + std::vector GetRemoteNodesBasicInfo() const; + static std::string ToBeAnonymous(const std::string &name); + + // add DataChangeListener to watch data change; + Status StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo); + + // stop DataChangeListener to watch data change; + Status StopWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo); + + // Send data to other device, function will be called back after sent to notify send result. + Status SendData( + const PipeInfo &pipeInfo, const DeviceId &deviceId, const uint8_t *ptr, int size, const MessageInfo &info); + + bool IsSameStartedOnPeer(const struct PipeInfo &pipeInfo, const struct DeviceId &peer); + + void SetMessageTransFlag(const PipeInfo &pipeInfo, bool flag); + + int CreateSessionServerAdapter(const std::string &sessionName); + + int RemoveSessionServerAdapter(const std::string &sessionName) const; + + void UpdateRelationship(const std::string &networkid, const DeviceChangeType &type); + + void InsertSession(const std::string &sessionName); + + void DeleteSession(const std::string &sessionName); + + void NotifyDataListeners(const uint8_t *ptr, const int size, const std::string &deviceId, const PipeInfo &pipeInfo); + + std::string ToNodeID(const std::string &nodeId) const; + + int32_t GetSessionStatus(int32_t sessionId); + + void OnSessionOpen(int32_t sessionId, int32_t status); + + void OnSessionClose(int32_t sessionId); + +private: + std::shared_ptr> GetSemaphore (int32_t sessinId); + mutable std::mutex networkMutex_{}; + mutable std::map networkId2Udid_{}; + DeviceInfo localInfo_{}; + static std::shared_ptr instance_; + std::mutex deviceChangeMutex_; + std::set listeners_{}; + std::mutex dataChangeMutex_{}; + std::map dataChangeListeners_{}; + std::mutex busSessionMutex_{}; + std::map busSessionMap_{}; + bool flag_ = true; // only for br flag + INodeStateCb nodeStateCb_{}; + ISessionListener sessionListener_{}; + std::mutex statusMutex_ {}; + std::map>> sessionsStatus_; +}; +} // namespace ObjectStore +} // namespace OHOS +#endif /* DISTRIBUTEDDATAFWK_SRC_SOFTBUS_ADAPTER_H */ \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/include/communicator/visibility.h b/data_object/frameworks/innerkitsimpl/include/communicator/visibility.h new file mode 100644 index 0000000000000000000000000000000000000000..0b836f21114f1f04c0d580e4f418e69619d20509 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/include/communicator/visibility.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KVSTORE_API +#ifdef _WIN32 +#ifdef DB_DLL_EXPORT +#define KVSTORE_API __declspec(dllexport) +#else +#define KVSTORE_API +#endif +#else +#define KVSTORE_API __attribute__((visibility("default"))) +#endif +#endif diff --git a/data_object/frameworks/innerkitsimpl/src/adaptor/client_adaptor.cpp b/data_object/frameworks/innerkitsimpl/src/adaptor/client_adaptor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f984b70131b93dda10e6dd8d26df1e894be48d37 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/adaptor/client_adaptor.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "client_adaptor.h" + +#include + +#include "logger.h" +#include "iservice_registry.h" + +namespace OHOS::ObjectStore { +sptr ClientAdaptor::GetObjectService() +{ + static sptr distributedDataMgr; + if (distributedDataMgr == nullptr) { + distributedDataMgr = GetDistributedDataManager(); + } + if (distributedDataMgr == nullptr) { + LOG_ERROR("get distributed data manager failed"); + return nullptr; + } + + auto remote = distributedDataMgr->GetObjectService(); + if (remote == nullptr) { + LOG_ERROR("get object service failed"); + return nullptr; + } + return iface_cast(remote); +} + +sptr ClientAdaptor::GetDistributedDataManager() +{ + int retry = 0; + while (++retry <= GET_SA_RETRY_TIMES) { + auto manager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (manager == nullptr) { + LOG_ERROR("get system ability manager failed"); + return nullptr; + } + LOG_INFO("get distributed data manager %{public}d", retry); + auto remoteObject = manager->CheckSystemAbility(DISTRIBUTED_KV_DATA_SERVICE_ABILITY_ID); + if (remoteObject == nullptr) { + std::this_thread::sleep_for(std::chrono::seconds(RETRY_INTERVAL)); + continue; + } + LOG_INFO("get distributed data manager success"); + return iface_cast(remoteObject); + } + + LOG_ERROR("get distributed data manager failed"); + return nullptr; +} +} \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/src/adaptor/distributed_object_impl.cpp b/data_object/frameworks/innerkitsimpl/src/adaptor/distributed_object_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1cd4d378aa0d6ce392de67807c18c66d6a00e08e --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/adaptor/distributed_object_impl.cpp @@ -0,0 +1,220 @@ +/* +* Copyright (c) 2022 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "distributed_object_impl.h" + +#include "dds_trace.h" +#include "objectstore_errors.h" +#include "string_utils.h" + +namespace OHOS::ObjectStore { +DistributedObjectImpl::~DistributedObjectImpl() +{ +} + +void PutNum(void *val, uint32_t offset, uint32_t valLen, Bytes &data) +{ + uint32_t len = valLen + offset; + if (len > sizeof(data.front()) * data.size()) { + data.resize(len); + } + + for (uint32_t i = 0; i < valLen; i++) { + // 8 bit = 1 byte + data[offset + i] = *(static_cast(val)) >> ((valLen - i - 1) * 8); + } +} + +uint32_t GetNum(Bytes &data, uint32_t offset, void *val, uint32_t valLen) +{ + uint8_t *value = (uint8_t *)val; + uint32_t len = offset + valLen; + uint32_t dataLen = data.size(); + if (dataLen < len) { + LOG_ERROR("DistributedObjectImpl:GetNum data.size() %{public}d, offset %{public}d, valLen %{public}d", dataLen, + offset, valLen); + return ERR_DATA_LEN; + } + for (uint32_t i = 0; i < valLen; i++) { + value[i] = data[len - 1 - i]; + } + return SUCCESS; +} + +uint32_t DistributedObjectImpl::PutDouble(const std::string &key, double value) +{ + DistributedDataDfx::DdsTrace trace(std::string("DistributedObjectImpl::") + std::string(__FUNCTION__), + DistributedDataDfx::TraceSwitch::BYTRACE_ON | DistributedDataDfx::TraceSwitch::TRACE_CHAIN_ON); + Bytes data; + Type type = Type::TYPE_DOUBLE; + PutNum(&type, 0, sizeof(type), data); + PutNum(&value, sizeof(type), sizeof(value), data); + uint32_t status = flatObjectStore_->Put(sessionId_, FIELDS_PREFIX + key, data); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl::PutDouble setField err %{public}d", status); + } + return status; +} + +uint32_t DistributedObjectImpl::PutBoolean(const std::string &key, bool value) +{ + DistributedDataDfx::DdsTrace trace(std::string("DistributedObjectImpl::") + std::string(__FUNCTION__), + DistributedDataDfx::TraceSwitch::BYTRACE_ON | DistributedDataDfx::TraceSwitch::TRACE_CHAIN_ON); + Bytes data; + Type type = Type::TYPE_BOOLEAN; + PutNum(&type, 0, sizeof(type), data); + PutNum(&value, sizeof(type), sizeof(value), data); + uint32_t status = flatObjectStore_->Put(sessionId_, FIELDS_PREFIX + key, data); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl::PutBoolean setField err %{public}d", status); + } + return status; +} + +uint32_t DistributedObjectImpl::PutString(const std::string &key, const std::string &value) +{ + DistributedDataDfx::DdsTrace trace(std::string("DistributedObjectImpl::") + std::string(__FUNCTION__), + DistributedDataDfx::TraceSwitch::BYTRACE_ON | DistributedDataDfx::TraceSwitch::TRACE_CHAIN_ON); + Bytes data; + Type type = Type::TYPE_STRING; + PutNum(&type, 0, sizeof(type), data); + Bytes dst = StringUtils::StrToBytes(value); + data.insert(data.end(), dst.begin(), dst.end()); + uint32_t status = flatObjectStore_->Put(sessionId_, FIELDS_PREFIX + key, data); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl::PutString setField err %{public}d", status); + } + return status; +} + +uint32_t DistributedObjectImpl::GetDouble(const std::string &key, double &value) +{ + Bytes data; + Bytes keyBytes = StringUtils::StrToBytes(key); + uint32_t status = flatObjectStore_->Get(sessionId_, FIELDS_PREFIX + key, data); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl:GetDouble field not exist. %{public}d %{public}s", status, key.c_str()); + return status; + } + status = GetNum(data, sizeof(Type), &value, sizeof(value)); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl::GetDouble getNum err. %{public}d", status); + } + return status; +} + +uint32_t DistributedObjectImpl::GetBoolean(const std::string &key, bool &value) +{ + Bytes data; + Bytes keyBytes = StringUtils::StrToBytes(key); + uint32_t status = flatObjectStore_->Get(sessionId_, FIELDS_PREFIX + key, data); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl:GetBoolean field not exist. %{public}d %{public}s", status, key.c_str()); + return status; + } + status = GetNum(data, sizeof(Type), &value, sizeof(value)); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl::GetBoolean getNum err. %{public}d", status); + return status; + } + return SUCCESS; +} + +uint32_t DistributedObjectImpl::GetString(const std::string &key, std::string &value) +{ + Bytes data; + uint32_t status = flatObjectStore_->Get(sessionId_, FIELDS_PREFIX + key, data); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl:GetString field not exist. %{public}d %{public}s", status, key.c_str()); + return status; + } + status = StringUtils::BytesToStrWithType(data, value); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl::GetString dataToVal err. %{public}d", status); + } + return status; +} + +uint32_t DistributedObjectImpl::GetType(const std::string &key, Type &type) +{ + Bytes data; + uint32_t status = flatObjectStore_->Get(sessionId_, FIELDS_PREFIX + key, data); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl:GetString field not exist. %{public}d %{public}s", status, key.c_str()); + return status; + } + status = GetNum(data, 0, &type, sizeof(type)); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl::GetBoolean getNum err. %{public}d", status); + return status; + } + return SUCCESS; +} + +std::string &DistributedObjectImpl::GetSessionId() +{ + return sessionId_; +} + +DistributedObjectImpl::DistributedObjectImpl(const std::string &sessionId, FlatObjectStore *flatObjectStore) + : sessionId_(sessionId), flatObjectStore_(flatObjectStore) +{ +} + +uint32_t DistributedObjectImpl::PutComplex(const std::string &key, const std::vector &value) +{ + DistributedDataDfx::DdsTrace trace(std::string("DistributedObjectImpl::") + std::string(__FUNCTION__), + DistributedDataDfx::TraceSwitch::BYTRACE_ON | DistributedDataDfx::TraceSwitch::TRACE_CHAIN_ON); + Bytes data; + Type type = Type::TYPE_COMPLEX; + PutNum(&type, 0, sizeof(type), data); + data.insert(data.end(), value.begin(), value.end()); + uint32_t status = flatObjectStore_->Put(sessionId_, FIELDS_PREFIX + key, data); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl::PutBoolean setField err %{public}d", status); + } + return status; +} + +uint32_t DistributedObjectImpl::GetComplex(const std::string &key, std::vector &value) +{ + uint32_t status = flatObjectStore_->Get(sessionId_, FIELDS_PREFIX + key, value); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl:GetString field not exist. %{public}d %{public}s", status, key.c_str()); + return status; + } + return status; +} + +uint32_t DistributedObjectImpl::Save(const std::string &deviceId) +{ + uint32_t status = flatObjectStore_->Save(sessionId_, deviceId); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl:Save failed. status = %{public}d", status); + return status; + } + return status; +} + +uint32_t DistributedObjectImpl::RevokeSave() +{ + uint32_t status = flatObjectStore_->RevokeSave(sessionId_); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectImpl:RevokeSave failed. status = %{public}d", status); + return status; + } + return status; +} +} // namespace OHOS::ObjectStore \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/src/adaptor/distributed_object_store_impl.cpp b/data_object/frameworks/innerkitsimpl/src/adaptor/distributed_object_store_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea12d8e4fe22917d1ee2f367495f7eddf8c9eb0f --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/adaptor/distributed_object_store_impl.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "dds_trace.h" +#include "distributed_object_impl.h" +#include "distributed_objectstore_impl.h" +#include "objectstore_errors.h" +#include "softbus_adapter.h" +#include "string_utils.h" + +namespace OHOS::ObjectStore { +DistributedObjectStoreImpl::DistributedObjectStoreImpl(FlatObjectStore *flatObjectStore) + : flatObjectStore_(flatObjectStore) +{ +} + +DistributedObjectStoreImpl::~DistributedObjectStoreImpl() +{ + delete flatObjectStore_; +} + +DistributedObject *DistributedObjectStoreImpl::CacheObject( + const std::string &sessionId, FlatObjectStore *flatObjectStore) +{ + DistributedObjectImpl *object = new (std::nothrow) DistributedObjectImpl(sessionId, flatObjectStore); + if (object == nullptr) { + return nullptr; + } + std::unique_lock cacheLock(dataMutex_); + objects_.push_back(object); + return object; +} + +void DistributedObjectStoreImpl::RemoveCacheObject(const std::string &sessionId) +{ + std::unique_lock cacheLock(dataMutex_); + auto iter = objects_.begin(); + while (iter != objects_.end()) { + if ((*iter)->GetSessionId() == sessionId) { + delete *iter; + iter = objects_.erase(iter); + } else { + iter++; + } + } + return; +} + +DistributedObject *DistributedObjectStoreImpl::CreateObject(const std::string &sessionId) +{ + DistributedDataDfx::DdsTrace trace(std::string("DistributedObjectImpl::") + std::string(__FUNCTION__), + DistributedDataDfx::TraceSwitch::TRACE_CHAIN_ON); + if (flatObjectStore_ == nullptr) { + LOG_ERROR("DistributedObjectStoreImpl::CreateObject store not opened!"); + return nullptr; + } + uint32_t status = flatObjectStore_->CreateObject(sessionId); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectStoreImpl::CreateObject CreateTable err %{public}d", status); + return nullptr; + } + return CacheObject(sessionId, flatObjectStore_); +} + +uint32_t DistributedObjectStoreImpl::DeleteObject(const std::string &sessionId) +{ + DistributedDataDfx::DdsTrace trace(std::string("DistributedObjectImpl::") + std::string(__FUNCTION__), + DistributedDataDfx::TraceSwitch::TRACE_CHAIN_ON); + if (flatObjectStore_ == nullptr) { + LOG_ERROR("DistributedObjectStoreImpl::Sync object err "); + return ERR_NULL_OBJECTSTORE; + } + uint32_t status = flatObjectStore_->Delete(sessionId); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectStoreImpl::DeleteObject store delete err %{public}d", status); + return status; + } + RemoveCacheObject(sessionId); + return SUCCESS; +} + +uint32_t DistributedObjectStoreImpl::Get(const std::string &sessionId, DistributedObject **object) +{ + auto iter = objects_.begin(); + while (iter != objects_.end()) { + if ((*iter)->GetSessionId() == sessionId) { + *object = *iter; + return SUCCESS; + } + iter++; + } + LOG_ERROR("DistributedObjectStoreImpl::Get object err, no object"); + return ERR_GET_OBJECT; +} + +uint32_t DistributedObjectStoreImpl::Watch(DistributedObject *object, std::shared_ptr watcher) +{ + if (object == nullptr) { + LOG_ERROR("DistributedObjectStoreImpl::Sync object err "); + return ERR_NULL_OBJECT; + } + if (flatObjectStore_ == nullptr) { + LOG_ERROR("DistributedObjectStoreImpl::Sync object err "); + return ERR_NULL_OBJECTSTORE; + } + if (watchers_.count(object) != 0) { + LOG_ERROR("DistributedObjectStoreImpl::Watch already gets object"); + return ERR_EXIST; + } + std::shared_ptr watcherProxy = std::make_shared(watcher, object->GetSessionId()); + uint32_t status = flatObjectStore_->Watch(object->GetSessionId(), watcherProxy); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectStoreImpl::Watch failed %{public}d", status); + return status; + } + watchers_.insert_or_assign(object, watcherProxy); + LOG_INFO("DistributedObjectStoreImpl:Watch object success."); + return SUCCESS; +} + +uint32_t DistributedObjectStoreImpl::UnWatch(DistributedObject *object) +{ + if (object == nullptr) { + LOG_ERROR("DistributedObjectStoreImpl::Sync object err "); + return ERR_NULL_OBJECT; + } + if (flatObjectStore_ == nullptr) { + LOG_ERROR("DistributedObjectStoreImpl::Sync object err "); + return ERR_NULL_OBJECTSTORE; + } + uint32_t status = flatObjectStore_->UnWatch(object->GetSessionId()); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectStoreImpl::Watch failed %{public}d", status); + return status; + } + watchers_.erase(object); + LOG_INFO("DistributedObjectStoreImpl:UnWatch object success."); + return SUCCESS; +} + +void DistributedObjectStoreImpl::TriggerSync() +{ +} + +void DistributedObjectStoreImpl::TriggerRestore(std::function notifier) +{ + std::thread th = std::thread([=]() { + bool isFinished; + int16_t i; + constexpr static int16_t MAX_RETRY_SIZE = 5000; + std::map syncStatus; + for (auto &item : objects_) { + syncStatus[item->GetSessionId()] = SYNC_START; + } + for (i = 0; i < MAX_RETRY_SIZE; i++) { + { + std::unique_lock cacheLock(dataMutex_); + for (auto &item : objects_) { + if (syncStatus[item->GetSessionId()] != SYNC_SUCCESS + && syncStatus[item->GetSessionId()] != SYNCING) { + auto onComplete = [this, item, &syncStatus]( + const std::map &devices) { + LOG_INFO("%{public}s pull data", item->GetSessionId().c_str()); + std::unique_lock cacheLock(dataMutex_); + SyncStatus result = SYNC_SUCCESS; + for (auto device : devices) { + if (device.second != DistributedDB::OK) { + result = SYNC_FAIL; + LOG_ERROR("%{public}s pull data fail %{public}d in device %{public}s", + item->GetSessionId().c_str(), device.second, + SoftBusAdapter::GetInstance()->ToNodeID(device.first).c_str()); + } + } + LOG_INFO("%{public}s pull data success", item->GetSessionId().c_str()); + syncStatus[item->GetSessionId()] = result; + }; + LOG_INFO("start sync %{public}s", item->GetSessionId().c_str()); + uint32_t result = flatObjectStore_->SyncAllData(item->GetSessionId(), onComplete); + if (result == SUCCESS) { + syncStatus[item->GetSessionId()] = SYNCING; + } else if (result == ERR_SINGLE_DEVICE) { + // single device, do not retry + syncStatus[item->GetSessionId()] = SYNC_SUCCESS; + } + } + } + } + + isFinished = true; + for (auto &item : syncStatus) { + if (item.second != SYNC_SUCCESS) { + LOG_INFO("%{public}s not ready", item.first.c_str()); + isFinished = false; + break; + } + } + if (!isFinished) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + LOG_WARN("restore result"); + notifier(); + LOG_WARN("notify end"); + }); + th.detach(); + return; +} +uint32_t DistributedObjectStoreImpl::SetStatusNotifier(std::shared_ptr notifier) +{ + if (flatObjectStore_ == nullptr) { + LOG_ERROR("DistributedObjectStoreImpl::Sync object err "); + return ERR_NULL_OBJECTSTORE; + } + std::shared_ptr watcherProxy = std::make_shared(notifier); + uint32_t status = flatObjectStore_->SetStatusNotifier(watcherProxy); + if (status != SUCCESS) { + LOG_ERROR("DistributedObjectStoreImpl::Watch failed %{public}d", status); + } + return status; +} + +WatcherProxy::WatcherProxy(const std::shared_ptr objectWatcher, const std::string &sessionId) + : FlatObjectWatcher(sessionId), objectWatcher_(objectWatcher) +{ +} + +void WatcherProxy::OnChanged(const std::string &sessionid, const std::vector &changedData) +{ + objectWatcher_->OnChanged(sessionid, changedData); +} + +DistributedObjectStore *DistributedObjectStore::GetInstance(const std::string &bundleName) +{ + static std::mutex instLock_; + static DistributedObjectStore *instPtr = nullptr; + if (instPtr == nullptr) { + std::lock_guard lock(instLock_); + if (instPtr == nullptr && !bundleName.empty()) { + LOG_INFO("new objectstore %{public}s", bundleName.c_str()); + FlatObjectStore *flatObjectStore = new (std::nothrow) FlatObjectStore(bundleName); + if (flatObjectStore == nullptr) { + LOG_ERROR("no memory for FlatObjectStore malloc!"); + return nullptr; + } + // Use instMemory to make sure this singleton not free before other object. + // This operation needn't to malloc memory, we needn't to check nullptr. + instPtr = new DistributedObjectStoreImpl(flatObjectStore); + } + } + return instPtr; +} + +void StatusNotifierProxy::OnChanged( + const std::string &sessionId, const std::string &networkId, const std::string &onlineStatus) +{ + if (notifier != nullptr) { + notifier->OnChanged(sessionId, networkId, onlineStatus); + } +} + +StatusNotifierProxy::StatusNotifierProxy(const std::shared_ptr ¬ifier) : notifier(notifier) +{ +} + +StatusNotifierProxy::~StatusNotifierProxy() +{ + LOG_ERROR("destroy"); + notifier = nullptr; +} +} // namespace OHOS::ObjectStore diff --git a/data_object/frameworks/innerkitsimpl/src/adaptor/flat_object_storage_engine.cpp b/data_object/frameworks/innerkitsimpl/src/adaptor/flat_object_storage_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bb5ceed307be72ad2845423b8aee74152a69aa2c --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/adaptor/flat_object_storage_engine.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "flat_object_storage_engine.h" + +#include "logger.h" +#include "objectstore_errors.h" +#include "process_communicator_impl.h" +#include "securec.h" +#include "softbus_adapter.h" +#include "string_utils.h" +#include "types_export.h" + +namespace OHOS::ObjectStore { +FlatObjectStorageEngine::~FlatObjectStorageEngine() +{ + if (!isOpened_) { + return; + } + storeManager_ = nullptr; + LOG_INFO("FlatObjectStorageEngine::~FlatObjectStorageEngine Crash! end"); +} + +uint32_t FlatObjectStorageEngine::Open(const std::string &bundleName) +{ + if (isOpened_) { + LOG_INFO("FlatObjectDatabase: No need to reopen it"); + return SUCCESS; + } + auto status = DistributedDB::KvStoreDelegateManager::SetProcessLabel("objectstoreDB", bundleName); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR("delegate SetProcessLabel failed: %{public}d.", static_cast(status)); + return ERR_DB_SET_PROCESS; + } + + auto communicator = std::make_shared(); + auto commStatus = DistributedDB::KvStoreDelegateManager::SetProcessCommunicator(communicator); + if (commStatus != DistributedDB::DBStatus::OK) { + LOG_ERROR("set distributed db communicator failed."); + return ERR_DB_SET_PROCESS; + } + storeManager_ = std::make_shared(bundleName, "default"); + if (storeManager_ == nullptr) { + LOG_ERROR("FlatObjectStorageEngine::make shared fail"); + return ERR_NOMEM; + } + + DistributedDB::KvStoreConfig config; + config.dataDir = "/data/log"; + storeManager_->SetKvStoreConfig(config); + isOpened_ = true; + LOG_INFO("FlatObjectDatabase::Open Succeed"); + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::Close() +{ + if (!isOpened_) { + LOG_INFO("FlatObjectStorageEngine::Close has been closed!"); + return SUCCESS; + } + std::lock_guard lock(operationMutex_); + storeManager_ = nullptr; + isOpened_ = false; + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::CreateTable(const std::string &key) +{ + if (!isOpened_) { + return ERR_DB_NOT_INIT; + } + { + std::lock_guard lock(operationMutex_); + if (delegates_.count(key) != 0) { + LOG_ERROR("FlatObjectStorageEngine::CreateTable %{public}s already created", key.c_str()); + return ERR_EXIST; + } + } + DistributedDB::KvStoreNbDelegate *kvStore = nullptr; + DistributedDB::DBStatus status; + DistributedDB::KvStoreNbDelegate::Option option = { true, true, + false }; // createIfNecessary, isMemoryDb, isEncryptedDb + LOG_INFO("start create table"); + storeManager_->GetKvStore(key, option, + [&status, &kvStore](DistributedDB::DBStatus dbStatus, DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) { + status = dbStatus; + kvStore = kvStoreNbDelegate; + LOG_INFO("create table result %{public}d", status); + }); + bool autoSync = true; + DistributedDB::PragmaData data = static_cast(&autoSync); + LOG_INFO("start Pragma"); + status = kvStore->Pragma(DistributedDB::AUTO_SYNC, data); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR("FlatObjectStorageEngine::CreateTable %{public}s getkvstore fail[%{public}d]", key.c_str(), status); + return ERR_DB_GETKV_FAIL; + } + LOG_INFO("create table %{public}s success", key.c_str()); + { + std::lock_guard lock(operationMutex_); + delegates_.insert_or_assign(key, kvStore); + } + + auto onComplete = [key, this](const std::map &devices) { + LOG_INFO("complete"); + for (auto item : devices) { + LOG_INFO("%{public}s pull data result %{public}d in device %{public}s", key.c_str(), item.second, + SoftBusAdapter::GetInstance()->ToNodeID(item.first).c_str()); + } + if (statusWatcher_ != nullptr) { + for (auto item : devices) { + statusWatcher_->OnChanged(key, SoftBusAdapter::GetInstance()->ToNodeID(item.first), + item.second == DistributedDB::OK ? "online" : "offline"); + } + } + }; + std::vector devices = SoftBusAdapter::GetInstance()->GetDeviceList(); + std::vector deviceIds; + for (auto item : devices) { + deviceIds.push_back(item.deviceId); + } + SyncAllData(key, deviceIds, onComplete); + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::GetTable(const std::string &key, std::map &result) +{ + if (!isOpened_) { + LOG_ERROR("not opened %{public}s", key.c_str()); + return ERR_DB_NOT_INIT; + } + std::lock_guard lock(operationMutex_); + if (delegates_.count(key) == 0) { + LOG_INFO("FlatObjectStorageEngine::GetTable %{public}s not exist", key.c_str()); + return ERR_DB_NOT_EXIST; + } + result.clear(); + DistributedDB::KvStoreResultSet *resultSet = nullptr; + Key emptyKey; + LOG_INFO("start GetEntries"); + DistributedDB::DBStatus status = delegates_.at(key)->GetEntries(emptyKey, resultSet); + if (status != DistributedDB::DBStatus::OK) { + LOG_INFO("FlatObjectStorageEngine::GetTable %{public}s GetEntries fail", key.c_str()); + return ERR_DB_GET_FAIL; + } + LOG_INFO("end GetEntries"); + while (resultSet->IsAfterLast()) { + DistributedDB::Entry entry; + status = resultSet->GetEntry(entry); + if (status != DistributedDB::DBStatus::OK) { + LOG_INFO("FlatObjectStorageEngine::GetTable GetEntry fail"); + return ERR_DB_ENTRY_FAIL; + } + result.insert_or_assign(StringUtils::BytesToStr(entry.key), entry.value); + resultSet->MoveToNext(); + } + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::UpdateItem(const std::string &key, const std::string &itemKey, Value &value) +{ + if (!isOpened_) { + return ERR_DB_NOT_INIT; + } + std::lock_guard lock(operationMutex_); + if (delegates_.count(key) == 0) { + LOG_INFO("FlatObjectStorageEngine::GetTable %{public}s not exist", key.c_str()); + return ERR_DB_NOT_EXIST; + } + auto delegate = delegates_.at(key); + LOG_INFO("start Put"); + auto status = delegate->Put(StringUtils::StrToBytes(itemKey), value); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR("%{public}s Put fail[%{public}d]", key.c_str(), status); + return ERR_CLOSE_STORAGE; + } + LOG_INFO("put success"); + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::UpdateItems( + const std::string &key, const std::map> &data) +{ + if (!isOpened_ || data.size() == 0) { + return ERR_DB_NOT_INIT; + } + std::lock_guard lock(operationMutex_); + if (delegates_.count(key) == 0) { + LOG_INFO("FlatObjectStorageEngine::UpdateItems %{public}s not exist", key.c_str()); + return ERR_DB_NOT_EXIST; + } + + std::vector entries; + for (auto &item : data) { + DistributedDB::Entry entry = { .key = StringUtils::StrToBytes(item.first), .value = item.second }; + entries.emplace_back(entry); + } + auto delegate = delegates_.at(key); + LOG_INFO("start PutBatch"); + auto status = delegate->PutBatch(entries); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR("%{public}s PutBatch fail[%{public}d]", key.c_str(), status); + return ERR_CLOSE_STORAGE; + } + LOG_INFO("put success"); + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::DeleteTable(const std::string &key) +{ + if (!isOpened_) { + return ERR_DB_NOT_INIT; + } + std::lock_guard lock(operationMutex_); + if (delegates_.count(key) == 0) { + LOG_INFO("FlatObjectStorageEngine::GetTable %{public}s not exist", key.c_str()); + return ERR_DB_NOT_EXIST; + } + LOG_INFO("start DeleteTable %{public}s", key.c_str()); + auto status = storeManager_->CloseKvStore(delegates_.at(key)); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR( + "FlatObjectStorageEngine::CloseKvStore %{public}s CloseKvStore fail[%{public}d]", key.c_str(), status); + return ERR_CLOSE_STORAGE; + } + LOG_INFO("DeleteTable success"); + delegates_.erase(key); + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::GetItem(const std::string &key, const std::string &itemKey, Value &value) +{ + if (!isOpened_) { + return ERR_DB_NOT_INIT; + } + std::lock_guard lock(operationMutex_); + if (delegates_.count(key) == 0) { + LOG_ERROR("FlatObjectStorageEngine::GetItem %{public}s not exist", key.c_str()); + return ERR_DB_NOT_EXIST; + } + LOG_INFO("start Get %{public}s", key.c_str()); + DistributedDB::DBStatus status = delegates_.at(key)->Get(StringUtils::StrToBytes(itemKey), value); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR("FlatObjectStorageEngine::GetItem %{public}s item fail %{public}d", itemKey.c_str(), status); + return status; + } + LOG_INFO("end Get %{public}s", key.c_str()); + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::RegisterObserver(const std::string &key, std::shared_ptr watcher) +{ + if (!isOpened_) { + LOG_ERROR("FlatObjectStorageEngine::RegisterObserver kvStore has not init"); + return ERR_DB_NOT_INIT; + } + std::lock_guard lock(operationMutex_); + if (delegates_.count(key) == 0) { + LOG_INFO("FlatObjectStorageEngine::RegisterObserver %{public}s not exist", key.c_str()); + return ERR_DB_NOT_EXIST; + } + if (observerMap_.count(key) != 0) { + LOG_INFO("FlatObjectStorageEngine::RegisterObserver observer already exist."); + return SUCCESS; + } + auto delegate = delegates_.at(key); + std::vector tmpKey; + LOG_INFO("start RegisterObserver %{public}s", key.c_str()); + DistributedDB::DBStatus status = + delegate->RegisterObserver(tmpKey, DistributedDB::ObserverMode::OBSERVER_CHANGES_FOREIGN, watcher.get()); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR("FlatObjectStorageEngine::RegisterObserver watch err %{public}d", status); + return ERR_REGISTER; + } + LOG_INFO("end RegisterObserver %{public}s", key.c_str()); + observerMap_.insert_or_assign(key, watcher); + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::UnRegisterObserver(const std::string &key) +{ + if (!isOpened_) { + LOG_ERROR("FlatObjectStorageEngine::RegisterObserver kvStore has not init"); + return ERR_DB_NOT_INIT; + } + std::lock_guard lock(operationMutex_); + if (delegates_.count(key) == 0) { + LOG_INFO("FlatObjectStorageEngine::RegisterObserver %{public}s not exist", key.c_str()); + return ERR_DB_NOT_EXIST; + } + auto iter = observerMap_.find(key); + if (iter == observerMap_.end()) { + LOG_ERROR("FlatObjectStorageEngine::UnRegisterObserver observer not exist."); + return ERR_NO_OBSERVER; + } + auto delegate = delegates_.at(key); + std::shared_ptr watcher = iter->second; + LOG_INFO("start UnRegisterObserver %{public}s", key.c_str()); + DistributedDB::DBStatus status = delegate->UnRegisterObserver(watcher.get()); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR("FlatObjectStorageEngine::UnRegisterObserver unRegister err %{public}d", status); + return ERR_UNRIGSTER; + } + LOG_INFO("end UnRegisterObserver %{public}s", key.c_str()); + observerMap_.erase(key); + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::SetStatusNotifier(std::shared_ptr watcher) +{ + if (!isOpened_) { + LOG_ERROR("FlatObjectStorageEngine::SetStatusNotifier kvStore has not init"); + return ERR_DB_NOT_INIT; + } + auto databaseStatusNotifyCallback = [this](std::string userId, std::string appId, std::string storeId, + const std::string deviceId, bool onlineStatus) -> void { + LOG_INFO("complete"); + if (statusWatcher_ == nullptr) { + LOG_INFO("FlatObjectStorageEngine::statusWatcher_ null"); + return; + } + if (onlineStatus) { + auto onComplete = [this, storeId](const std::map &devices) { + for (auto item : devices) { + LOG_INFO("%{public}s pull data result %{public}d in device %{public}s", storeId.c_str(), + item.second, SoftBusAdapter::GetInstance()->ToNodeID(item.first).c_str()); + } + if (statusWatcher_ != nullptr) { + for (auto item : devices) { + statusWatcher_->OnChanged(storeId, SoftBusAdapter::GetInstance()->ToNodeID(item.first), + item.second == DistributedDB::OK ? "online" : "offline"); + } + } + }; + SyncAllData(storeId, std::vector({ deviceId }), onComplete); + } else { + statusWatcher_->OnChanged(storeId, SoftBusAdapter::GetInstance()->ToNodeID(deviceId), "offline"); + } + }; + storeManager_->SetStoreStatusNotifier(databaseStatusNotifyCallback); + LOG_INFO("FlatObjectStorageEngine::SetStatusNotifier success"); + statusWatcher_ = watcher; + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::SyncAllData(const std::string &sessionId, const std::vector &deviceIds, + const std::function &)> &onComplete) +{ + LOG_INFO("start"); + std::lock_guard lock(operationMutex_); + if (delegates_.count(sessionId) == 0) { + LOG_ERROR("FlatObjectStorageEngine::SyncAllData %{public}s already deleted", sessionId.c_str()); + return ERR_DB_NOT_EXIST; + } + DistributedDB::KvStoreNbDelegate *kvstore = delegates_.at(sessionId); + if (deviceIds.empty()) { + LOG_INFO("single device,no need sync"); + return ERR_SINGLE_DEVICE; + } + LOG_INFO("start sync %{public}s", sessionId.c_str()); + DistributedDB::DBStatus status = kvstore->Sync(deviceIds, DistributedDB::SyncMode::SYNC_MODE_PULL_ONLY, onComplete); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR("FlatObjectStorageEngine::UnRegisterObserver unRegister err %{public}d", status); + return ERR_UNRIGSTER; + } + LOG_INFO("end sync %{public}s", sessionId.c_str()); + return SUCCESS; +} + +uint32_t FlatObjectStorageEngine::GetItems(const std::string &key, std::map> &data) +{ + if (!isOpened_) { + LOG_ERROR("FlatObjectStorageEngine::GetItems %{public}s not init", key.c_str()); + return ERR_DB_NOT_INIT; + } + std::lock_guard lock(operationMutex_); + if (delegates_.count(key) == 0) { + LOG_ERROR("FlatObjectStorageEngine::GetItems %{public}s not exist", key.c_str()); + return ERR_DB_NOT_EXIST; + } + LOG_INFO("start Get %{public}s", key.c_str()); + std::vector entries; + DistributedDB::DBStatus status = delegates_.at(key)->GetEntries(StringUtils::StrToBytes(""), entries); + if (status != DistributedDB::DBStatus::OK) { + LOG_ERROR("FlatObjectStorageEngine::GetItems item fail status = %{public}d", status); + return status; + } + for (auto &item : entries) { + data[StringUtils::BytesToStr(item.key)] = item.value; + } + LOG_INFO("end Get %{public}s", key.c_str()); + return SUCCESS; +} + +void Watcher::OnChange(const DistributedDB::KvStoreChangedData &data) +{ + std::vector changedData; + std::string tmp; + for (DistributedDB::Entry item : data.GetEntriesInserted()) { + tmp = StringUtils::BytesToStr(item.key); + LOG_INFO("inserted %{public}s", tmp.c_str()); + // property key start with p_, 2 is p_ size + if (tmp.compare(0, FIELDS_PREFIX_LEN, FIELDS_PREFIX) == 0) { + changedData.push_back(tmp.substr(FIELDS_PREFIX_LEN)); + } + } + for (DistributedDB::Entry item : data.GetEntriesUpdated()) { + tmp = StringUtils::BytesToStr(item.key); + LOG_INFO("updated %{public}s", tmp.c_str()); + // property key start with p_, 2 is p_ size + if (tmp.compare(0, FIELDS_PREFIX_LEN, FIELDS_PREFIX) == 0) { + changedData.push_back(tmp.substr(FIELDS_PREFIX_LEN)); + } + } + this->OnChanged(sessionId_, changedData); +} + +Watcher::Watcher(const std::string &sessionId) : sessionId_(sessionId) +{ +} +} // namespace OHOS::ObjectStore diff --git a/data_object/frameworks/innerkitsimpl/src/adaptor/flat_object_store.cpp b/data_object/frameworks/innerkitsimpl/src/adaptor/flat_object_store.cpp new file mode 100644 index 0000000000000000000000000000000000000000..388f656882d0b0918a279aedfb3493c2e83d26e9 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/adaptor/flat_object_store.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "flat_object_store.h" + +#include "client_adaptor.h" +#include "distributed_objectstore_impl.h" +#include "logger.h" +#include "object_callback.h" +#include "object_service_proxy.h" +#include "objectstore_errors.h" +#include "softbus_adapter.h" + +namespace OHOS::ObjectStore { +FlatObjectStore::FlatObjectStore(const std::string &bundleName) +{ + bundleName_ = bundleName; + storageEngine_ = std::make_shared(); + uint32_t status = storageEngine_->Open(bundleName); + if (status != SUCCESS) { + LOG_ERROR("FlatObjectStore: Failed to open, error: open storage engine failure %{public}d", status); + } + cacheManager_ = new CacheManager(); +} + +FlatObjectStore::~FlatObjectStore() +{ + if (storageEngine_ != nullptr) { + storageEngine_->Close(); + storageEngine_ = nullptr; + } + delete cacheManager_; + cacheManager_ = nullptr; +} + +uint32_t FlatObjectStore::CreateObject(const std::string &sessionId) +{ + if (!storageEngine_->isOpened_) { + LOG_ERROR("FlatObjectStore::DB has not inited"); + return ERR_DB_NOT_INIT; + } + uint32_t status = storageEngine_->CreateTable(sessionId); + if (status != SUCCESS) { + LOG_ERROR("FlatObjectStore::CreateObject createTable err %{public}d", status); + return status; + } + std::function> &data)> callback = + [sessionId, this]( + const std::map> &data) { + if (data.size() > 0) { + LOG_INFO("objectstore, retrieve success"); + auto result = storageEngine_->UpdateItems(sessionId, data); + if (result != SUCCESS) { + LOG_ERROR("UpdateItems failed, status = %{public}d", result); + } + } else { + LOG_INFO("objectstore, retrieve empty"); + } + }; + cacheManager_->ResumeObject(bundleName_, sessionId, callback); + return SUCCESS; +} + +uint32_t FlatObjectStore::Delete(const std::string &sessionId) +{ + if (!storageEngine_->isOpened_) { + LOG_ERROR("FlatObjectStore::DB has not inited"); + return ERR_DB_NOT_INIT; + } + uint32_t status = storageEngine_->DeleteTable(sessionId); + if (status != SUCCESS) { + LOG_ERROR("FlatObjectStore: Failed to delete object %{public}d", status); + return status; + } + return SUCCESS; +} + +uint32_t FlatObjectStore::Watch(const std::string &sessionId, std::shared_ptr watcher) +{ + if (!storageEngine_->isOpened_) { + LOG_ERROR("FlatObjectStore::DB has not inited"); + return ERR_DB_NOT_INIT; + } + uint32_t status = storageEngine_->RegisterObserver(sessionId, watcher); + if (status != SUCCESS) { + LOG_ERROR("FlatObjectStore::Watch failed %{public}d", status); + } + return status; +} + +uint32_t FlatObjectStore::UnWatch(const std::string &sessionId) +{ + if (!storageEngine_->isOpened_) { + LOG_ERROR("FlatObjectStore::DB has not inited"); + return ERR_DB_NOT_INIT; + } + uint32_t status = storageEngine_->UnRegisterObserver(sessionId); + if (status != SUCCESS) { + LOG_ERROR("FlatObjectStore::Watch failed %{public}d", status); + } + return status; +} + +uint32_t FlatObjectStore::Put(const std::string &sessionId, const std::string &key, std::vector value) +{ + if (!storageEngine_->isOpened_) { + LOG_ERROR("FlatObjectStore::DB has not inited"); + return ERR_DB_NOT_INIT; + } + return storageEngine_->UpdateItem(sessionId, key, value); +} + +uint32_t FlatObjectStore::Get(std::string &sessionId, const std::string &key, Bytes &value) +{ + if (!storageEngine_->isOpened_) { + LOG_ERROR("FlatObjectStore::DB has not inited"); + return ERR_DB_NOT_INIT; + } + return storageEngine_->GetItem(sessionId, key, value); +} + +uint32_t FlatObjectStore::SetStatusNotifier(std::shared_ptr notifier) +{ + if (!storageEngine_->isOpened_) { + LOG_ERROR("FlatObjectStore::DB has not inited"); + return ERR_DB_NOT_INIT; + } + return storageEngine_->SetStatusNotifier(notifier); +} + +uint32_t FlatObjectStore::SyncAllData(const std::string &sessionId, + const std::function &)> &onComplete) +{ + if (!storageEngine_->isOpened_) { + LOG_ERROR("FlatObjectStore::DB has not inited"); + return ERR_DB_NOT_INIT; + } + std::vector devices = SoftBusAdapter::GetInstance()->GetDeviceList(); + std::vector deviceIds; + for (auto item : devices) { + deviceIds.push_back(item.deviceId); + } + return storageEngine_->SyncAllData(sessionId, deviceIds, onComplete); +} + +uint32_t FlatObjectStore::Save(const std::string &sessionId, const std::string &deviceId) +{ + if (cacheManager_ == nullptr) { + LOG_ERROR("FlatObjectStore::cacheManager_ is null"); + return ERR_NULL_PTR; + } + std::map> objectData; + uint32_t status = storageEngine_->GetItems(sessionId, objectData); + if (status != SUCCESS) { + LOG_ERROR("FlatObjectStore::GetItems fail"); + return status; + } + return cacheManager_->Save(bundleName_, sessionId, deviceId, objectData); +} + +uint32_t FlatObjectStore::RevokeSave(const std::string &sessionId) +{ + if (cacheManager_ == nullptr) { + LOG_ERROR("FlatObjectStore::cacheManager_ is null"); + return ERR_NULL_PTR; + } + return cacheManager_->RevokeSave(bundleName_, sessionId); +} + +CacheManager::CacheManager() +{ +} + +uint32_t CacheManager::Save(const std::string &bundleName, const std::string &sessionId, const std::string &deviceId, + const std::map> &objectData) +{ + std::unique_lock lck(mutex_); + ConditionLock conditionLock; + int32_t status = SaveObject(bundleName, sessionId, deviceId, objectData, + [this, &deviceId, &conditionLock](const std::map &results) { + LOG_INFO("CacheManager::task callback"); + if (results.count(deviceId) != 0) { + conditionLock.Notify(results.at(deviceId)); + } else { + conditionLock.Notify(ERR_DB_GET_FAIL); + } + }); + if (status != SUCCESS) { + LOG_ERROR("SaveObject failed"); + return status; + } + LOG_INFO("CacheManager::start wait"); + status = conditionLock.Wait(); + LOG_INFO("CacheManager::end wait, %{public}d", status); + return status == SUCCESS ? status : ERR_DB_GET_FAIL; +} + +uint32_t CacheManager::RevokeSave(const std::string &bundleName, const std::string &sessionId) +{ + std::unique_lock lck(mutex_); + ConditionLock conditionLock; + std::function callback = [this, &conditionLock](int32_t result) { + LOG_INFO("CacheManager::task callback"); + conditionLock.Notify(result); + }; + int32_t status = RevokeSaveObject(bundleName, sessionId, callback); + if (status != SUCCESS) { + LOG_ERROR("RevokeSaveObject failed"); + return status; + } + LOG_INFO("CacheManager::start wait"); + status = conditionLock.Wait(); + LOG_INFO("CacheManager::end wait, %{public}d", status); + return status == SUCCESS ? status : ERR_DB_GET_FAIL; +} + +int32_t CacheManager::SaveObject(const std::string &bundleName, const std::string &sessionId, + const std::string &deviceId, const std::map> &objectData, + const std::function &)> &callback) +{ + sptr proxy = ClientAdaptor::GetObjectService(); + if (proxy == nullptr) { + LOG_ERROR("proxy is nullptr."); + return ERR_NULL_PTR; + } + sptr objectSaveCallback = new ObjectSaveCallback(callback); + int32_t status = proxy->ObjectStoreSave(bundleName, sessionId, deviceId, objectData, objectSaveCallback); + if (status != SUCCESS) { + LOG_ERROR("object save failed code=%d.", static_cast(status)); + } + LOG_INFO("object save successful"); + return status; +} + +int32_t CacheManager::RevokeSaveObject( + const std::string &bundleName, const std::string &sessionId, std::function &callback) +{ + sptr proxy = ClientAdaptor::GetObjectService(); + if (proxy == nullptr) { + LOG_ERROR("proxy is nullptr."); + return ERR_NULL_PTR; + } + sptr objectRevokeSaveCallback = new ObjectRevokeSaveCallback(callback); + int32_t status = proxy->ObjectStoreRevokeSave(bundleName, sessionId, objectRevokeSaveCallback); + if (status != SUCCESS) { + LOG_ERROR("object revoke save failed code=%d.", static_cast(status)); + } + LOG_INFO("object revoke save successful"); + return status; +} + +int32_t CacheManager::ResumeObject(const std::string &bundleName, const std::string &sessionId, + std::function> &data)> &callback) +{ + sptr proxy = ClientAdaptor::GetObjectService(); + if (proxy == nullptr) { + LOG_ERROR("proxy is nullptr."); + return ERR_NULL_PTR; + } + sptr objectRevokeSaveCallback = new ObjectRetrieveCallback(callback); + int32_t status = proxy->ObjectStoreRetrieve(bundleName, sessionId, objectRevokeSaveCallback); + if (status != SUCCESS) { + LOG_ERROR("object resume failed code=%d.", static_cast(status)); + } + LOG_INFO("object resume successful"); + return status; +} +} // namespace OHOS::ObjectStore diff --git a/data_object/frameworks/innerkitsimpl/src/adaptor/object_callback.cpp b/data_object/frameworks/innerkitsimpl/src/adaptor/object_callback.cpp new file mode 100644 index 0000000000000000000000000000000000000000..408121f7aab03af0f0233d3eee7e1afd10516282 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/adaptor/object_callback.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "object_callback.h" + +namespace OHOS::ObjectStore { +void ObjectSaveCallback::Completed(const std::map &results) +{ + callback_(results); +} + +ObjectSaveCallback::ObjectSaveCallback(const std::function &)> &callback) + : callback_(callback) +{ +} + +void ObjectRevokeSaveCallback::Completed(int32_t status) +{ + callback_(status); +} + +ObjectRevokeSaveCallback::ObjectRevokeSaveCallback(const std::function &callback) : callback_(callback) +{ +} + +void ObjectRetrieveCallback::Completed(const std::map> &results) +{ + callback_(results); +} + +ObjectRetrieveCallback::ObjectRetrieveCallback( + const std::function> &)> &callback) + : callback_(callback) +{ +} +} // namespace OHOS::DistributedKv diff --git a/data_object/frameworks/innerkitsimpl/src/communicator/app_device_handler.cpp b/data_object/frameworks/innerkitsimpl/src/communicator/app_device_handler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2838d3e71d16af138720c543fa92c85ea6e1f101 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/communicator/app_device_handler.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "app_device_handler.h" + +#include + +namespace OHOS { +namespace ObjectStore { +AppDeviceHandler::AppDeviceHandler() +{ + softbusAdapter_ = SoftBusAdapter::GetInstance(); +} + +AppDeviceHandler::~AppDeviceHandler() +{ + LOG_INFO("destruct"); +} +void AppDeviceHandler::Init() +{ + softbusAdapter_->Init(); +} + +Status AppDeviceHandler::StartWatchDeviceChange( + const AppDeviceStatusChangeListener *observer, __attribute__((unused)) const PipeInfo &pipeInfo) +{ + return softbusAdapter_->StartWatchDeviceChange(observer, pipeInfo); +} + +Status AppDeviceHandler::StopWatchDeviceChange( + const AppDeviceStatusChangeListener *observer, __attribute__((unused)) const PipeInfo &pipeInfo) +{ + return softbusAdapter_->StopWatchDeviceChange(observer, pipeInfo); +} + +std::vector AppDeviceHandler::GetDeviceList() const +{ + return softbusAdapter_->GetDeviceList(); +} + +DeviceInfo AppDeviceHandler::GetLocalDevice() +{ + return softbusAdapter_->GetLocalDevice(); +} + +DeviceInfo AppDeviceHandler::GetLocalBasicInfo() const +{ + return softbusAdapter_->GetLocalBasicInfo(); +} + +std::vector AppDeviceHandler::GetRemoteNodesBasicInfo() const +{ + return softbusAdapter_->GetRemoteNodesBasicInfo(); +} + +std::string AppDeviceHandler::GetUdidByNodeId(const std::string &nodeId) const +{ + return softbusAdapter_->GetUdidByNodeId(nodeId); +} +} // namespace ObjectStore +} // namespace OHOS diff --git a/data_object/frameworks/innerkitsimpl/src/communicator/app_pipe_handler.cpp b/data_object/frameworks/innerkitsimpl/src/communicator/app_pipe_handler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d42b1d07895b7efe85aeffabd524f539d19609e2 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/communicator/app_pipe_handler.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "app_pipe_handler.h" + +#include + +#include "logger.h" + +namespace OHOS { +namespace ObjectStore { +using namespace std; + +AppPipeHandler::~AppPipeHandler() +{ + LOG_INFO("destructor pipeId: %{public}s", pipeInfo_.pipeId.c_str()); +} + +AppPipeHandler::AppPipeHandler(const PipeInfo &pipeInfo) : pipeInfo_(pipeInfo) +{ + LOG_INFO("constructor pipeId: %{public}s", pipeInfo_.pipeId.c_str()); + softbusAdapter_ = SoftBusAdapter::GetInstance(); +} + +Status AppPipeHandler::SendData( + const PipeInfo &pipeInfo, const DeviceId &deviceId, const uint8_t *ptr, int size, const MessageInfo &info) +{ + return softbusAdapter_->SendData(pipeInfo, deviceId, ptr, size, info); +} + +Status AppPipeHandler::StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + return softbusAdapter_->StartWatchDataChange(observer, pipeInfo); +} + +Status AppPipeHandler::StopWatchDataChange( + __attribute__((unused)) const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + return softbusAdapter_->StopWatchDataChange(observer, pipeInfo); +} + +bool AppPipeHandler::IsSameStartedOnPeer( + const struct PipeInfo &pipeInfo, __attribute__((unused)) const struct DeviceId &peer) +{ + return softbusAdapter_->IsSameStartedOnPeer(pipeInfo, peer); +} + +int AppPipeHandler::CreateSessionServer(const std::string &sessionName) const +{ + return softbusAdapter_->CreateSessionServerAdapter(sessionName); +} + +int AppPipeHandler::RemoveSessionServer(const std::string &sessionName) const +{ + return softbusAdapter_->RemoveSessionServerAdapter(sessionName); +} +} // namespace ObjectStore +} // namespace OHOS diff --git a/data_object/frameworks/innerkitsimpl/src/communicator/app_pipe_mgr.cpp b/data_object/frameworks/innerkitsimpl/src/communicator/app_pipe_mgr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..700d7ba2fb850d80b6cac21cfa80c7d68eb3b4fa --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/communicator/app_pipe_mgr.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "app_pipe_mgr.h" + +namespace OHOS { +namespace ObjectStore { +static const int MAX_TRANSFER_SIZE = 1024 * 1024 * 5; +Status AppPipeMgr::StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + LOG_INFO("begin"); + if (observer == nullptr || pipeInfo.pipeId.empty()) { + LOG_ERROR("argument invalid"); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it == dataBusMap_.end()) { + LOG_ERROR("pipeid not found"); + return Status::ERROR; + } + LOG_INFO("end"); + return it->second->StartWatchDataChange(observer, pipeInfo); +} + +// stop DataChangeListener to watch data change; +Status AppPipeMgr::StopWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + LOG_INFO("begin"); + if (observer == nullptr || pipeInfo.pipeId.empty()) { + LOG_ERROR("argument invalid"); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it == dataBusMap_.end()) { + LOG_ERROR("pipeid not found"); + return Status::ERROR; + } + LOG_INFO("end"); + return it->second->StopWatchDataChange(observer, pipeInfo); +} + +// Send data to other device, function will be called back after sent to notify send result. +Status AppPipeMgr::SendData( + const PipeInfo &pipeInfo, const DeviceId &deviceId, const uint8_t *ptr, int size, const MessageInfo &info) +{ + if (size > MAX_TRANSFER_SIZE || size <= 0 || ptr == nullptr || pipeInfo.pipeId.empty() + || deviceId.deviceId.empty()) { + LOG_WARN("Input is invalid, maxSize:%{public}d, current size:%{public}d", MAX_TRANSFER_SIZE, size); + return Status::ERROR; + } + LOG_DEBUG("pipeInfo:%{public}s ,size:%{public}d", pipeInfo.pipeId.c_str(), size); + std::shared_ptr appPipeHandler; + { + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it == dataBusMap_.end()) { + LOG_WARN("pipeInfo:%{public}s not found", pipeInfo.pipeId.c_str()); + return Status::KEY_NOT_FOUND; + } + appPipeHandler = it->second; + } + return appPipeHandler->SendData(pipeInfo, deviceId, ptr, size, info); +} + +// start server +Status AppPipeMgr::Start(const PipeInfo &pipeInfo) +{ + if (pipeInfo.pipeId.empty()) { + LOG_WARN("Start Failed, pipeInfo is empty."); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it != dataBusMap_.end()) { + LOG_WARN("repeated start, pipeInfo:%{public}s.", pipeInfo.pipeId.c_str()); + return Status::REPEATED_REGISTER; + } + LOG_DEBUG("Start pipeInfo:%{public}s ", pipeInfo.pipeId.c_str()); + auto handler = std::make_shared(pipeInfo); + if (handler == nullptr) { + LOG_WARN("pipeInfo:%{public}s. new failed", pipeInfo.pipeId.c_str()); + return Status::ILLEGAL_STATE; + } + int ret = handler->CreateSessionServer(pipeInfo.pipeId); + if (ret != 0) { + LOG_WARN("Start pipeInfo:%{public}s, failed ret:%{public}d.", pipeInfo.pipeId.c_str(), ret); + return Status::ILLEGAL_STATE; + } + + dataBusMap_.insert(std::pair>(pipeInfo.pipeId, handler)); + return Status::SUCCESS; +} + +// stop server +Status AppPipeMgr::Stop(const PipeInfo &pipeInfo) +{ + std::shared_ptr appPipeHandler; + { + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it == dataBusMap_.end()) { + LOG_WARN("pipeInfo:%{public}s not found", pipeInfo.pipeId.c_str()); + return Status::KEY_NOT_FOUND; + } + appPipeHandler = it->second; + int ret = appPipeHandler->RemoveSessionServer(pipeInfo.pipeId); + if (ret != 0) { + LOG_WARN("Stop pipeInfo:%{public}s ret:%{public}d.", pipeInfo.pipeId.c_str(), ret); + return Status::ERROR; + } + dataBusMap_.erase(pipeInfo.pipeId); + return Status::SUCCESS; + } + return Status::KEY_NOT_FOUND; +} + +bool AppPipeMgr::IsSameStartedOnPeer(const struct PipeInfo &pipeInfo, const struct DeviceId &peer) +{ + LOG_INFO("start"); + if (pipeInfo.pipeId.empty() || peer.deviceId.empty()) { + LOG_ERROR("pipeId or deviceId is empty. Return false."); + return false; + } + LOG_INFO("pipeInfo == [%{public}s]", pipeInfo.pipeId.c_str()); + std::shared_ptr appPipeHandler; + { + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it == dataBusMap_.end()) { + LOG_ERROR("pipeInfo:%{public}s not found. Return false.", pipeInfo.pipeId.c_str()); + return false; + } + appPipeHandler = it->second; + } + return appPipeHandler->IsSameStartedOnPeer(pipeInfo, peer); +} +} // namespace ObjectStore +} // namespace OHOS diff --git a/data_object/frameworks/innerkitsimpl/src/communicator/ark_communication_provider.cpp b/data_object/frameworks/innerkitsimpl/src/communicator/ark_communication_provider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cdb8dba61535540be0b9e6497dfb26b43314efaa --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/communicator/ark_communication_provider.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ark_communication_provider.h" + +#include + +namespace OHOS { +namespace ObjectStore { +CommunicationProvider &ArkCommunicationProvider::Init() +{ + static ArkCommunicationProvider instance; + if (instance.isInited) { + return instance; + } + LOG_INFO("begin"); + std::lock_guard lock(mutex_); + if (!instance.isInited) { + instance.Initialize(); + } + instance.isInited = true; + LOG_INFO("normal end"); + return instance; +} +ArkCommunicationProvider::ArkCommunicationProvider() + : CommunicationProviderImpl(appPipeMgrImpl_, appDeviceHandlerImpl_) +{ +} +} // namespace ObjectStore +} // namespace OHOS diff --git a/data_object/frameworks/innerkitsimpl/src/communicator/communication_provider.cpp b/data_object/frameworks/innerkitsimpl/src/communicator/communication_provider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ffa2a1c4a2c0b2e4aace51197e362b728aed35c9 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/communicator/communication_provider.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "communication_provider.h" + +#include "ark_communication_provider.h" + +namespace OHOS { +namespace ObjectStore { +CommunicationProvider &CommunicationProvider::GetInstance() +{ + return ArkCommunicationProvider::Init(); +} +} // namespace ObjectStore +} // namespace OHOS diff --git a/data_object/frameworks/innerkitsimpl/src/communicator/communication_provider_impl.cpp b/data_object/frameworks/innerkitsimpl/src/communicator/communication_provider_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..95f94f36e2d1bdfb0ea190d117a59b73083306c1 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/communicator/communication_provider_impl.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "communication_provider_impl.h" + +#include + +namespace OHOS { +namespace ObjectStore { +std::mutex CommunicationProviderImpl::mutex_; +CommunicationProviderImpl::CommunicationProviderImpl(AppPipeMgr &appPipeMgr, AppDeviceHandler &deviceHandler) + : appPipeMgr_(appPipeMgr), appDeviceHandler_(deviceHandler) +{ +} + +CommunicationProviderImpl::~CommunicationProviderImpl() +{ + LOG_DEBUG("destructor."); +} + +Status CommunicationProviderImpl::Initialize() +{ + appDeviceHandler_.Init(); + return Status::SUCCESS; +} + +Status CommunicationProviderImpl::StartWatchDeviceChange( + const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo) +{ + return appDeviceHandler_.StartWatchDeviceChange(observer, pipeInfo); +} + +Status CommunicationProviderImpl::StopWatchDeviceChange( + const AppDeviceStatusChangeListener *observer, const PipeInfo &pipeInfo) +{ + return appDeviceHandler_.StopWatchDeviceChange(observer, pipeInfo); +} + +DeviceInfo CommunicationProviderImpl::GetLocalDevice() const +{ + return appDeviceHandler_.GetLocalDevice(); +} + +std::vector CommunicationProviderImpl::GetDeviceList() const +{ + return appDeviceHandler_.GetDeviceList(); +} + +Status CommunicationProviderImpl::StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + return appPipeMgr_.StartWatchDataChange(observer, pipeInfo); +} + +Status CommunicationProviderImpl::StopWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + return appPipeMgr_.StopWatchDataChange(observer, pipeInfo); +} + +Status CommunicationProviderImpl::SendData( + const PipeInfo &pipeInfo, const DeviceId &deviceId, const uint8_t *ptr, int size, const MessageInfo &info) +{ + return appPipeMgr_.SendData(pipeInfo, deviceId, ptr, size, info); +} + +Status CommunicationProviderImpl::Start(const PipeInfo &pipeInfo) +{ + return appPipeMgr_.Start(pipeInfo); +} + +Status CommunicationProviderImpl::Stop(const PipeInfo &pipeInfo) +{ + return appPipeMgr_.Stop(pipeInfo); +} + +bool CommunicationProviderImpl::IsSameStartedOnPeer(const PipeInfo &pipeInfo, const DeviceId &peer) const +{ + return appPipeMgr_.IsSameStartedOnPeer(pipeInfo, peer); +} +} // namespace ObjectStore +} // namespace OHOS diff --git a/data_object/frameworks/innerkitsimpl/src/communicator/process_communicator_impl.cpp b/data_object/frameworks/innerkitsimpl/src/communicator/process_communicator_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..05265887cf1aa7a4a2208ef957b84469342922f3 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/communicator/process_communicator_impl.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "process_communicator_impl.h" + +#include + +namespace OHOS { +namespace ObjectStore { +using namespace DistributedDB; +ProcessCommunicatorImpl::ProcessCommunicatorImpl() +{ +} + +ProcessCommunicatorImpl::~ProcessCommunicatorImpl() +{ + LOG_ERROR("destructor."); +} + +DBStatus ProcessCommunicatorImpl::Start(const std::string &processLabel) +{ + LOG_INFO("init commProvider"); + thisProcessLabel_ = processLabel; + PipeInfo pi = { thisProcessLabel_ }; + Status errCode = CommunicationProvider::GetInstance().Start(pi); + if (errCode != Status::SUCCESS) { + LOG_ERROR("commProvider_ Start Fail."); + return DBStatus::DB_ERROR; + } + return DBStatus::OK; +} + +DBStatus ProcessCommunicatorImpl::Stop() +{ + PipeInfo pi = { thisProcessLabel_ }; + Status errCode = CommunicationProvider::GetInstance().Stop(pi); + if (errCode != Status::SUCCESS) { + LOG_ERROR("commProvider_ Stop Fail."); + return DBStatus::DB_ERROR; + } + return DBStatus::OK; +} + +DBStatus ProcessCommunicatorImpl::RegOnDeviceChange(const OnDeviceChange &callback) +{ + { + std::lock_guard onDeviceChangeLockGard(onDeviceChangeMutex_); + onDeviceChangeHandler_ = callback; + } + + PipeInfo pi = { thisProcessLabel_ }; + if (callback) { + Status errCode = CommunicationProvider::GetInstance().StartWatchDeviceChange(this, pi); + if (errCode != Status::SUCCESS) { + LOG_ERROR("commProvider_ StartWatchDeviceChange Fail."); + return DBStatus::DB_ERROR; + } + } else { + Status errCode = CommunicationProvider::GetInstance().StopWatchDeviceChange(this, pi); + if (errCode != Status::SUCCESS) { + LOG_ERROR("commProvider_ StopWatchDeviceChange Fail."); + return DBStatus::DB_ERROR; + } + } + + return DBStatus::OK; +} + +DBStatus ProcessCommunicatorImpl::RegOnDataReceive(const OnDataReceive &callback) +{ + { + std::lock_guard onDataReceiveLockGard(onDataReceiveMutex_); + onDataReceiveHandler_ = callback; + } + + PipeInfo pi = { thisProcessLabel_ }; + if (callback) { + Status errCode = CommunicationProvider::GetInstance().StartWatchDataChange(this, pi); + if (errCode != Status::SUCCESS) { + LOG_ERROR("commProvider_ StartWatchDataChange Fail."); + return DBStatus::DB_ERROR; + } + } else { + Status errCode = CommunicationProvider::GetInstance().StopWatchDataChange(this, pi); + if (errCode != Status::SUCCESS) { + LOG_ERROR("commProvider_ StopWatchDataChange Fail."); + return DBStatus::DB_ERROR; + } + } + return DBStatus::OK; +} + +DBStatus ProcessCommunicatorImpl::SendData(const DeviceInfos &dstDevInfo, const uint8_t *data, uint32_t length) +{ + PipeInfo pi = { thisProcessLabel_ }; + DeviceId destination; + destination.deviceId = dstDevInfo.identifier; + Status errCode = CommunicationProvider::GetInstance().SendData(pi, destination, data, static_cast(length)); + if (errCode != Status::SUCCESS) { + LOG_ERROR("commProvider_ SendData Fail."); + return DBStatus::DB_ERROR; + } + + return DBStatus::OK; +} + +uint32_t ProcessCommunicatorImpl::GetMtuSize() +{ + return MTU_SIZE; +} + +uint32_t ProcessCommunicatorImpl::GetMtuSize(const DeviceInfos &devInfo) +{ + LOG_INFO("GetMtuSize start"); + std::vector devInfos = CommunicationProvider::GetInstance().GetDeviceList(); + for (auto const &entry : devInfos) { + LOG_INFO("GetMtuSize deviceType: %{public}s", entry.deviceType.c_str()); + bool isWatch = (entry.deviceType == SMART_WATCH_TYPE || entry.deviceType == CHILDREN_WATCH_TYPE); + if (entry.deviceId == devInfo.identifier && isWatch) { + return MTU_SIZE_WATCH; + } + } + return MTU_SIZE; +} + +DeviceInfos ProcessCommunicatorImpl::GetLocalDeviceInfos() +{ + DeviceInfos localDevInfos; + DeviceInfo devInfo = CommunicationProvider::GetInstance().GetLocalDevice(); + localDevInfos.identifier = devInfo.deviceId; + return localDevInfos; +} + +std::vector ProcessCommunicatorImpl::GetRemoteOnlineDeviceInfosList() +{ + std::vector remoteDevInfos; + std::vector devInfoVec = CommunicationProvider::GetInstance().GetDeviceList(); + for (auto const &entry : devInfoVec) { + DeviceInfos remoteDev; + remoteDev.identifier = entry.deviceId; + remoteDevInfos.push_back(remoteDev); + } + return remoteDevInfos; +} + +bool ProcessCommunicatorImpl::IsSameProcessLabelStartedOnPeerDevice(const DeviceInfos &peerDevInfo) +{ + PipeInfo pi = { thisProcessLabel_ }; + DeviceId di = { peerDevInfo.identifier }; + return CommunicationProvider::GetInstance().IsSameStartedOnPeer(pi, di); +} + +void ProcessCommunicatorImpl::OnMessage( + const DeviceInfo &info, const uint8_t *ptr, const int size, __attribute__((unused)) const PipeInfo &pipeInfo) const +{ + std::lock_guard onDataReceiveLockGuard(onDataReceiveMutex_); + if (onDataReceiveHandler_ == nullptr) { + LOG_ERROR("onDataReceiveHandler_ invalid."); + return; + } + DeviceInfos devInfo; + devInfo.identifier = info.deviceId; + onDataReceiveHandler_(devInfo, ptr, static_cast(size)); +} + +void ProcessCommunicatorImpl::OnDeviceChanged(const DeviceInfo &info, const DeviceChangeType &type) const +{ + std::lock_guard onDeviceChangeLockGuard(onDeviceChangeMutex_); + if (onDeviceChangeHandler_ == nullptr) { + LOG_ERROR("onDeviceChangeHandler_ invalid."); + return; + } + DeviceInfos devInfo; + devInfo.identifier = info.deviceId; + onDeviceChangeHandler_(devInfo, (type == DeviceChangeType::DEVICE_ONLINE)); +} +} // namespace ObjectStore +} // namespace OHOS diff --git a/data_object/frameworks/innerkitsimpl/src/communicator/softbus_adapter_standard.cpp b/data_object/frameworks/innerkitsimpl/src/communicator/softbus_adapter_standard.cpp new file mode 100644 index 0000000000000000000000000000000000000000..00b05690b6e106950708e8ee53a90b73a7464d1f --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/src/communicator/softbus_adapter_standard.cpp @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include "kv_store_delegate_manager.h" +#include "process_communicator_impl.h" +#include "securec.h" +#include "session.h" +#include "softbus_adapter.h" +#include "softbus_bus_center.h" + +namespace OHOS { +namespace ObjectStore { +constexpr int32_t HEAD_SIZE = 3; +constexpr int32_t END_SIZE = 3; +constexpr int32_t MIN_SIZE = HEAD_SIZE + END_SIZE + 3; +constexpr const char *REPLACE_CHAIN = "***"; +constexpr const char *DEFAULT_ANONYMOUS = "******"; +constexpr int32_t SOFTBUS_OK = 0; +constexpr int32_t SOFTBUS_ERR = 1; +constexpr int32_t INVALID_SESSION_ID = -1; +constexpr int32_t SESSION_NAME_SIZE_MAX = 65; +constexpr int32_t DEVICE_ID_SIZE_MAX = 65; +constexpr int32_t ID_BUF_LEN = 65; +using namespace std; + +class AppDeviceListenerWrap { +public: + explicit AppDeviceListenerWrap() + { + } + ~AppDeviceListenerWrap() + { + } + static void SetDeviceHandler(SoftBusAdapter *handler); + static void OnDeviceOnline(NodeBasicInfo *info); + static void OnDeviceOffline(NodeBasicInfo *info); + static void OnDeviceInfoChanged(NodeBasicInfoType type, NodeBasicInfo *info); + +private: + static SoftBusAdapter *softBusAdapter_; + static void NotifyAll(NodeBasicInfo *info, DeviceChangeType type); +}; +SoftBusAdapter *AppDeviceListenerWrap::softBusAdapter_; + +class AppDataListenerWrap { +public: + static void SetDataHandler(SoftBusAdapter *handler); + static int OnSessionOpened(int sessionId, int result); + static void OnSessionClosed(int sessionId); + static void OnMessageReceived(int sessionId, const void *data, unsigned int dataLen); + static void OnBytesReceived(int sessionId, const void *data, unsigned int dataLen); + +public: + // notifiy all listeners when received message + static void NotifyDataListeners( + const uint8_t *ptr, const int size, const std::string &deviceId, const PipeInfo &pipeInfo); + static SoftBusAdapter *softBusAdapter_; +}; +SoftBusAdapter *AppDataListenerWrap::softBusAdapter_; +std::shared_ptr SoftBusAdapter::instance_; + +void AppDeviceListenerWrap::OnDeviceInfoChanged(NodeBasicInfoType type, NodeBasicInfo *info) +{ + std::string udid = softBusAdapter_->GetUdidByNodeId(std::string(info->networkId)); + LOG_INFO("[InfoChange] type:%{public}d, id:%{public}s, name:%{public}s", type, + SoftBusAdapter::ToBeAnonymous(udid).c_str(), info->deviceName); +} + +void AppDeviceListenerWrap::OnDeviceOffline(NodeBasicInfo *info) +{ + std::string udid = softBusAdapter_->GetUdidByNodeId(std::string(info->networkId)); + LOG_INFO("[Offline] id:%{public}s, name:%{public}s, typeId:%{public}d", + SoftBusAdapter::ToBeAnonymous(udid).c_str(), info->deviceName, info->deviceTypeId); + NotifyAll(info, DeviceChangeType::DEVICE_OFFLINE); +} + +void AppDeviceListenerWrap::OnDeviceOnline(NodeBasicInfo *info) +{ + std::string udid = softBusAdapter_->GetUdidByNodeId(std::string(info->networkId)); + LOG_INFO("[Online] id:%{public}s, name:%{public}s, typeId:%{public}d", SoftBusAdapter::ToBeAnonymous(udid).c_str(), + info->deviceName, info->deviceTypeId); + NotifyAll(info, DeviceChangeType::DEVICE_ONLINE); +} + +void AppDeviceListenerWrap::SetDeviceHandler(SoftBusAdapter *handler) +{ + LOG_INFO("SetDeviceHandler."); + softBusAdapter_ = handler; +} + +void AppDeviceListenerWrap::NotifyAll(NodeBasicInfo *info, DeviceChangeType type) +{ + DeviceInfo di = { std::string(info->networkId), std::string(info->deviceName), std::to_string(info->deviceTypeId) }; + softBusAdapter_->NotifyAll(di, type); +} + +SoftBusAdapter::SoftBusAdapter() +{ + LOG_INFO("begin"); + AppDeviceListenerWrap::SetDeviceHandler(this); + AppDataListenerWrap::SetDataHandler(this); + + nodeStateCb_.events = EVENT_NODE_STATE_MASK; + nodeStateCb_.onNodeOnline = AppDeviceListenerWrap::OnDeviceOnline; + nodeStateCb_.onNodeOffline = AppDeviceListenerWrap::OnDeviceOffline; + nodeStateCb_.onNodeBasicInfoChanged = AppDeviceListenerWrap::OnDeviceInfoChanged; + + sessionListener_.OnSessionOpened = AppDataListenerWrap::OnSessionOpened; + sessionListener_.OnSessionClosed = AppDataListenerWrap::OnSessionClosed; + sessionListener_.OnBytesReceived = AppDataListenerWrap::OnBytesReceived; + sessionListener_.OnMessageReceived = AppDataListenerWrap::OnMessageReceived; +} + +SoftBusAdapter::~SoftBusAdapter() +{ + LOG_INFO("begin"); + int32_t errNo = UnregNodeDeviceStateCb(&nodeStateCb_); + if (errNo != SOFTBUS_OK) { + LOG_ERROR("UnregNodeDeviceStateCb fail %{public}d", errNo); + } +} + +void SoftBusAdapter::Init() +{ + LOG_INFO("begin"); + std::thread th = std::thread([&]() { + int i = 0; + constexpr int RETRY_TIMES = 300; + while (i++ < RETRY_TIMES) { + int32_t errNo = RegNodeDeviceStateCb("ohos.objectstore", &nodeStateCb_); + if (errNo != SOFTBUS_OK) { + LOG_ERROR("RegNodeDeviceStateCb fail %{public}d, time:%{public}d", errNo, i); + std::this_thread::sleep_for(std::chrono::seconds(1)); + continue; + } + LOG_INFO("RegNodeDeviceStateCb success"); + return; + } + LOG_ERROR("Init failed %{public}d times and exit now.", RETRY_TIMES); + }); + th.detach(); +} + +Status SoftBusAdapter::StartWatchDeviceChange( + const AppDeviceStatusChangeListener *observer, __attribute__((unused)) const PipeInfo &pipeInfo) +{ + LOG_INFO("begin"); + if (observer == nullptr) { + LOG_WARN("observer is null."); + return Status::ERROR; + } + std::lock_guard lock(deviceChangeMutex_); + auto result = listeners_.insert(observer); + if (!result.second) { + LOG_WARN("Add listener error."); + return Status::ERROR; + } + LOG_INFO("end"); + return Status::SUCCESS; +} + +Status SoftBusAdapter::StopWatchDeviceChange( + const AppDeviceStatusChangeListener *observer, __attribute__((unused)) const PipeInfo &pipeInfo) +{ + LOG_INFO("begin"); + if (observer == nullptr) { + LOG_WARN("observer is null."); + return Status::ERROR; + } + std::lock_guard lock(deviceChangeMutex_); + auto result = listeners_.erase(observer); + if (result <= 0) { + return Status::ERROR; + } + LOG_INFO("end"); + return Status::SUCCESS; +} + +void SoftBusAdapter::NotifyAll(const DeviceInfo &deviceInfo, const DeviceChangeType &type) +{ + std::thread th = std::thread([this, deviceInfo, type]() { + std::vector listeners; + { + std::lock_guard lock(deviceChangeMutex_); + for (const auto &listener : listeners_) { + listeners.push_back(listener); + } + } + LOG_DEBUG("high"); + std::string udid = GetUdidByNodeId(deviceInfo.deviceId); + LOG_DEBUG("[Notify] to DB from: %{public}s, type:%{public}d", ToBeAnonymous(udid).c_str(), type); + UpdateRelationship(deviceInfo.deviceId, type); + for (const auto &device : listeners) { + if (device == nullptr) { + continue; + } + if (device->GetChangeLevelType() == ChangeLevelType::HIGH) { + DeviceInfo di = { udid, deviceInfo.deviceName, deviceInfo.deviceType }; + device->OnDeviceChanged(di, type); + break; + } + } + LOG_DEBUG("low"); + for (const auto &device : listeners) { + if (device == nullptr) { + continue; + } + if (device->GetChangeLevelType() == ChangeLevelType::LOW) { + DeviceInfo di = { udid, deviceInfo.deviceName, deviceInfo.deviceType }; + device->OnDeviceChanged(di, DeviceChangeType::DEVICE_OFFLINE); + device->OnDeviceChanged(di, type); + } + } + LOG_DEBUG("min"); + for (const auto &device : listeners) { + if (device == nullptr) { + continue; + } + if (device->GetChangeLevelType() == ChangeLevelType::MIN) { + DeviceInfo di = { udid, deviceInfo.deviceName, deviceInfo.deviceType }; + device->OnDeviceChanged(di, type); + } + } + }); + th.detach(); +} + +std::vector SoftBusAdapter::GetDeviceList() const +{ + std::vector dis; + NodeBasicInfo *info = nullptr; + int32_t infoNum = 0; + dis.clear(); + + int32_t ret = GetAllNodeDeviceInfo("ohos.objectstore", &info, &infoNum); + if (ret != SOFTBUS_OK) { + LOG_ERROR("GetAllNodeDeviceInfo error"); + return dis; + } + LOG_INFO("GetAllNodeDeviceInfo success infoNum=%{public}d", infoNum); + + for (int i = 0; i < infoNum; i++) { + std::string udid = GetUdidByNodeId(std::string(info[i].networkId)); + DeviceInfo deviceInfo = { udid, std::string(info[i].deviceName), std::to_string(info[i].deviceTypeId) }; + dis.push_back(deviceInfo); + } + if (info != nullptr) { + FreeNodeInfo(info); + } + return dis; +} + +DeviceInfo SoftBusAdapter::GetLocalDevice() +{ + if (!localInfo_.deviceId.empty()) { + return localInfo_; + } + + NodeBasicInfo info; + int32_t ret = GetLocalNodeDeviceInfo("ohos.objectstore", &info); + if (ret != SOFTBUS_OK) { + LOG_ERROR("GetLocalNodeDeviceInfo error"); + return DeviceInfo(); + } + std::string uuid = GetUdidByNodeId(std::string(info.networkId)); + LOG_DEBUG("[LocalDevice] id:%{private}s, name:%{private}s, type:%{private}d", ToBeAnonymous(uuid).c_str(), + info.deviceName, info.deviceTypeId); + localInfo_ = { uuid, std::string(info.deviceName), std::to_string(info.deviceTypeId) }; + return localInfo_; +} + +std::string SoftBusAdapter::GetUdidByNodeId(const std::string &nodeId) const +{ + char udid[ID_BUF_LEN] = { 0 }; + int32_t ret = GetNodeKeyInfo("ohos.objectstore", nodeId.c_str(), NodeDeviceInfoKey::NODE_KEY_UDID, + reinterpret_cast(udid), ID_BUF_LEN); + if (ret != SOFTBUS_OK) { + LOG_WARN("GetNodeKeyInfo error, nodeId:%{public}s", ToBeAnonymous(nodeId).c_str()); + return ""; + } + return std::string(udid); +} + +DeviceInfo SoftBusAdapter::GetLocalBasicInfo() const +{ + LOG_DEBUG("begin"); + NodeBasicInfo info; + int32_t ret = GetLocalNodeDeviceInfo("ohos.objectstore", &info); + if (ret != SOFTBUS_OK) { + LOG_ERROR("GetLocalNodeDeviceInfo error"); + return DeviceInfo(); + } + LOG_DEBUG("[LocalBasicInfo] networkId:%{private}s, name:%{private}s, " + "type:%{private}d", + ToBeAnonymous(std::string(info.networkId)).c_str(), info.deviceName, info.deviceTypeId); + DeviceInfo localInfo = { std::string(info.networkId), std::string(info.deviceName), + std::to_string(info.deviceTypeId) }; + return localInfo; +} + +std::vector SoftBusAdapter::GetRemoteNodesBasicInfo() const +{ + LOG_DEBUG("begin"); + std::vector dis; + NodeBasicInfo *info = nullptr; + int32_t infoNum = 0; + dis.clear(); + + int32_t ret = GetAllNodeDeviceInfo("ohos.objectstore", &info, &infoNum); + if (ret != SOFTBUS_OK) { + LOG_ERROR("GetAllNodeDeviceInfo error"); + return dis; + } + LOG_DEBUG("GetAllNodeDeviceInfo success infoNum=%{public}d", infoNum); + + for (int i = 0; i < infoNum; i++) { + dis.push_back( + { std::string(info[i].networkId), std::string(info[i].deviceName), std::to_string(info[i].deviceTypeId) }); + } + if (info != nullptr) { + FreeNodeInfo(info); + } + return dis; +} + +void SoftBusAdapter::UpdateRelationship(const std::string &networkid, const DeviceChangeType &type) +{ + auto udid = GetUdidByNodeId(networkid); + lock_guard lock(networkMutex_); + switch (type) { + case DeviceChangeType::DEVICE_OFFLINE: { + auto size = this->networkId2Udid_.erase(networkid); + if (size == 0) { + LOG_WARN("not found id:%{public}s.", networkid.c_str()); + } + break; + } + case DeviceChangeType::DEVICE_ONLINE: { + std::pair value = { networkid, udid }; + auto res = this->networkId2Udid_.insert(std::move(value)); + if (!res.second) { + LOG_WARN("insert failed."); + } + break; + } + default: { + LOG_WARN("unknown type."); + break; + } + } +} +std::string SoftBusAdapter::ToNodeID(const std::string &nodeId) const +{ + { + lock_guard lock(networkMutex_); + for (auto const &e : networkId2Udid_) { + if (nodeId == e.second) { // id is udid + return e.first; + } + } + } + + LOG_WARN("get the network id from devices."); + std::vector devices; + NodeBasicInfo *info = nullptr; + int32_t infoNum = 0; + std::string networkId; + int32_t ret = GetAllNodeDeviceInfo("ohos.objectstore", &info, &infoNum); + if (ret == SOFTBUS_OK) { + lock_guard lock(networkMutex_); + for (int i = 0; i < infoNum; i++) { + if (networkId2Udid_.find(info[i].networkId) != networkId2Udid_.end()) { + continue; + } + auto udid = GetUdidByNodeId(std::string(info[i].networkId)); + networkId2Udid_.insert({ info[i].networkId, udid }); + if (udid == nodeId) { + networkId = info[i].networkId; + } + } + } + if (info != nullptr) { + FreeNodeInfo(info); + } + return networkId; +} + +std::string SoftBusAdapter::ToBeAnonymous(const std::string &name) +{ + if (name.length() <= HEAD_SIZE) { + return DEFAULT_ANONYMOUS; + } + + if (name.length() < MIN_SIZE) { + return (name.substr(0, HEAD_SIZE) + REPLACE_CHAIN); + } + + return (name.substr(0, HEAD_SIZE) + REPLACE_CHAIN + name.substr(name.length() - END_SIZE, END_SIZE)); +} + +std::shared_ptr SoftBusAdapter::GetInstance() +{ + static std::once_flag onceFlag; + std::call_once(onceFlag, [&] { instance_ = std::make_shared(); }); + return instance_; +} + +Status SoftBusAdapter::StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + LOG_DEBUG("begin"); + if (observer == nullptr) { + return Status::INVALID_ARGUMENT; + } + lock_guard lock(dataChangeMutex_); + auto it = dataChangeListeners_.find(pipeInfo.pipeId); + if (it != dataChangeListeners_.end()) { + LOG_WARN("Add listener error or repeated adding."); + return Status::ERROR; + } + LOG_DEBUG("current appid %{public}s", pipeInfo.pipeId.c_str()); + dataChangeListeners_.insert({ pipeInfo.pipeId, observer }); + return Status::SUCCESS; +} + +Status SoftBusAdapter::StopWatchDataChange( + __attribute__((unused)) const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + LOG_DEBUG("begin"); + lock_guard lock(dataChangeMutex_); + if (dataChangeListeners_.erase(pipeInfo.pipeId)) { + return Status::SUCCESS; + } + LOG_WARN("stop data observer error, pipeInfo:%{public}s", pipeInfo.pipeId.c_str()); + return Status::ERROR; +} + +Status SoftBusAdapter::SendData( + const PipeInfo &pipeInfo, const DeviceId &deviceId, const uint8_t *ptr, int size, const MessageInfo &info) +{ + SessionAttribute attr; + attr.dataType = TYPE_BYTES; + LOG_INFO("[SendData] to %{public}s ,session:%{public}s, size:%{public}d", + ToBeAnonymous(deviceId.deviceId).c_str(), pipeInfo.pipeId.c_str(), size); + int sessionId = OpenSession( + pipeInfo.pipeId.c_str(), pipeInfo.pipeId.c_str(), ToNodeID(deviceId.deviceId).c_str(), "GROUP_ID", &attr); + if (sessionId < 0) { + LOG_WARN("OpenSession %{public}s, type:%{public}d failed, sessionId:%{public}d", pipeInfo.pipeId.c_str(), + info.msgType, sessionId); + return Status::CREATE_SESSION_ERROR; + } + int state = GetSessionStatus(sessionId); + LOG_DEBUG("Waited for notification, state:%{public}d", state); + if (state != SOFTBUS_OK) { + LOG_ERROR("OpenSession callback result error"); + return Status::CREATE_SESSION_ERROR; + } + LOG_DEBUG("[SendBytes] start,session id is %{public}d, size is %{public}d, " + "session type is %{public}d.", + sessionId, size, attr.dataType); + int32_t ret = SendBytes(sessionId, (void *)ptr, size); + if (ret != SOFTBUS_OK) { + LOG_ERROR("[SendBytes] to %{public}d failed, ret:%{public}d.", sessionId, ret); + return Status::ERROR; + } + return Status::SUCCESS; +} + +int32_t SoftBusAdapter::GetSessionStatus(int32_t sessionId) +{ + auto semaphore = GetSemaphore(sessionId); + return semaphore->Wait(); +} + +void SoftBusAdapter::OnSessionOpen(int32_t sessionId, int32_t status) +{ + auto semaphore = GetSemaphore(sessionId); + semaphore->Notify(status); +} + +void SoftBusAdapter::OnSessionClose(int32_t sessionId) +{ + lock_guard lock(statusMutex_); + auto it = sessionsStatus_.find(sessionId); + if (it != sessionsStatus_.end()) { + it->second->Clear(); + sessionsStatus_.erase(it); + } +} + +std::shared_ptr> SoftBusAdapter::GetSemaphore(int32_t sessionId) +{ + lock_guard lock(statusMutex_); + if (sessionsStatus_.find(sessionId) == sessionsStatus_.end()) { + sessionsStatus_.emplace(sessionId, std::make_shared>()); + } + return sessionsStatus_[sessionId]; +} + +bool SoftBusAdapter::IsSameStartedOnPeer( + const struct PipeInfo &pipeInfo, __attribute__((unused)) const struct DeviceId &peer) +{ + LOG_INFO( + "pipeInfo:%{public}s peer.deviceId:%{public}s", pipeInfo.pipeId.c_str(), ToBeAnonymous(peer.deviceId).c_str()); + { + lock_guard lock(busSessionMutex_); + if (busSessionMap_.find(pipeInfo.pipeId + peer.deviceId) != busSessionMap_.end()) { + LOG_INFO("Found session in map. Return true."); + return true; + } + } + SessionAttribute attr; + attr.dataType = TYPE_BYTES; + int sessionId = OpenSession( + pipeInfo.pipeId.c_str(), pipeInfo.pipeId.c_str(), ToNodeID(peer.deviceId).c_str(), "GROUP_ID", &attr); + LOG_INFO("[IsSameStartedOnPeer] sessionId=%{public}d", sessionId); + if (sessionId == INVALID_SESSION_ID) { + LOG_ERROR("OpenSession return null, pipeInfo:%{public}s. Return false.", pipeInfo.pipeId.c_str()); + return false; + } + LOG_INFO("session started, pipeInfo:%{public}s. sessionId:%{public}d Return " + "true. ", + pipeInfo.pipeId.c_str(), sessionId); + return true; +} + +void SoftBusAdapter::SetMessageTransFlag(const PipeInfo &pipeInfo, bool flag) +{ + LOG_INFO("pipeInfo: %{public}s flag: %{public}d", pipeInfo.pipeId.c_str(), static_cast(flag)); + flag_ = flag; +} + +int SoftBusAdapter::CreateSessionServerAdapter(const std::string &sessionName) +{ + LOG_DEBUG("begin"); + return CreateSessionServer("ohos.objectstore", sessionName.c_str(), &sessionListener_); +} + +int SoftBusAdapter::RemoveSessionServerAdapter(const std::string &sessionName) const +{ + LOG_DEBUG("begin"); + return RemoveSessionServer("ohos.objectstore", sessionName.c_str()); +} + +void SoftBusAdapter::InsertSession(const std::string &sessionName) +{ + lock_guard lock(busSessionMutex_); + busSessionMap_.insert({sessionName, true}); +} + +void SoftBusAdapter::DeleteSession(const std::string &sessionName) +{ + lock_guard lock(busSessionMutex_); + busSessionMap_.erase(sessionName); +} + +void SoftBusAdapter::NotifyDataListeners( + const uint8_t *ptr, const int size, const std::string &deviceId, const PipeInfo &pipeInfo) +{ + LOG_DEBUG("begin"); + lock_guard lock(dataChangeMutex_); + auto it = dataChangeListeners_.find(pipeInfo.pipeId); + if (it != dataChangeListeners_.end()) { + LOG_DEBUG("ready to notify, pipeName:%{public}s, deviceId:%{public}s.", pipeInfo.pipeId.c_str(), + ToBeAnonymous(deviceId).c_str()); + DeviceInfo deviceInfo = { deviceId, "", "" }; + it->second->OnMessage(deviceInfo, ptr, size, pipeInfo); + return; + } + LOG_WARN("no listener %{public}s.", pipeInfo.pipeId.c_str()); +} + +void AppDataListenerWrap::SetDataHandler(SoftBusAdapter *handler) +{ + LOG_INFO("begin"); + softBusAdapter_ = handler; +} + +int AppDataListenerWrap::OnSessionOpened(int sessionId, int result) +{ + LOG_INFO("[SessionOpen] sessionId:%{public}d, result:%{public}d", sessionId, result); + char mySessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerSessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerDevId[DEVICE_ID_SIZE_MAX] = ""; + softBusAdapter_->OnSessionOpen(sessionId, result); + if (result != SOFTBUS_OK) { + LOG_WARN("session %{public}d open failed, result:%{public}d.", sessionId, result); + return result; + } + int ret = GetMySessionName(sessionId, mySessionName, sizeof(mySessionName)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my session name failed, session id is %{public}d.", sessionId); + return SOFTBUS_ERR; + } + ret = GetPeerSessionName(sessionId, peerSessionName, sizeof(peerSessionName)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my peer session name failed, session id is %{public}d.", sessionId); + return SOFTBUS_ERR; + } + ret = GetPeerDeviceId(sessionId, peerDevId, sizeof(peerDevId)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my peer device id failed, session id is %{public}d.", sessionId); + return SOFTBUS_ERR; + } + std::string peerUdid = softBusAdapter_->GetUdidByNodeId(std::string(peerDevId)); + LOG_DEBUG("[SessionOpen] mySessionName:%{public}s, " + "peerSessionName:%{public}s, peerDevId:%{public}s", + mySessionName, peerSessionName, SoftBusAdapter::ToBeAnonymous(peerUdid).c_str()); + + if (strlen(peerSessionName) < 1) { + softBusAdapter_->InsertSession(std::string(mySessionName) + peerUdid); + } else { + softBusAdapter_->InsertSession(std::string(peerSessionName) + peerUdid); + } + return 0; +} + +void AppDataListenerWrap::OnSessionClosed(int sessionId) +{ + LOG_INFO("[SessionClosed] sessionId:%{public}d", sessionId); + char mySessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerSessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerDevId[DEVICE_ID_SIZE_MAX] = ""; + + softBusAdapter_->OnSessionClose(sessionId); + int ret = GetMySessionName(sessionId, mySessionName, sizeof(mySessionName)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my session name failed, session id is %{public}d.", sessionId); + return; + } + ret = GetPeerSessionName(sessionId, peerSessionName, sizeof(peerSessionName)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my peer session name failed, session id is %{public}d.", sessionId); + return; + } + ret = GetPeerDeviceId(sessionId, peerDevId, sizeof(peerDevId)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my peer device id failed, session id is %{public}d.", sessionId); + return; + } + std::string peerUdid = softBusAdapter_->GetUdidByNodeId(std::string(peerDevId)); + LOG_DEBUG("[SessionClosed] mySessionName:%{public}s, " + "peerSessionName:%{public}s, peerDevId:%{public}s", + mySessionName, peerSessionName, SoftBusAdapter::ToBeAnonymous(peerUdid).c_str()); + + if (strlen(peerSessionName) < 1) { + softBusAdapter_->DeleteSession(std::string(mySessionName) + peerUdid); + } else { + softBusAdapter_->DeleteSession(std::string(peerSessionName) + peerUdid); + } +} + +void AppDataListenerWrap::OnMessageReceived(int sessionId, const void *data, unsigned int dataLen) +{ + LOG_INFO("begin"); + if (sessionId == INVALID_SESSION_ID) { + return; + } + char peerSessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerDevId[DEVICE_ID_SIZE_MAX] = ""; + int ret = GetPeerSessionName(sessionId, peerSessionName, sizeof(peerSessionName)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my peer session name failed, session id is %{public}d.", sessionId); + return; + } + ret = GetPeerDeviceId(sessionId, peerDevId, sizeof(peerDevId)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my peer device id failed, session id is %{public}d.", sessionId); + return; + } + std::string peerUdid = softBusAdapter_->GetUdidByNodeId(std::string(peerDevId)); + LOG_DEBUG("[MessageReceived] session id:%{public}d, " + "peerSessionName:%{public}s, peerDevId:%{public}s", + sessionId, peerSessionName, SoftBusAdapter::ToBeAnonymous(peerUdid).c_str()); + NotifyDataListeners(reinterpret_cast(data), dataLen, peerUdid, { std::string(peerSessionName) }); +} + +void AppDataListenerWrap::OnBytesReceived(int sessionId, const void *data, unsigned int dataLen) +{ + LOG_INFO("begin"); + if (sessionId == INVALID_SESSION_ID) { + return; + } + char peerSessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerDevId[DEVICE_ID_SIZE_MAX] = ""; + int ret = GetPeerSessionName(sessionId, peerSessionName, sizeof(peerSessionName)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my peer session name failed, session id is %{public}d.", sessionId); + return; + } + ret = GetPeerDeviceId(sessionId, peerDevId, sizeof(peerDevId)); + if (ret != SOFTBUS_OK) { + LOG_WARN("get my peer device id failed, session id is %{public}d.", sessionId); + return; + } + std::string peerUdid = softBusAdapter_->GetUdidByNodeId(std::string(peerDevId)); + LOG_DEBUG("[BytesReceived] session id:%{public}d, peerSessionName:%{public}s, " + "peerDevId:%{public}s", + sessionId, peerSessionName, SoftBusAdapter::ToBeAnonymous(peerUdid).c_str()); + NotifyDataListeners(reinterpret_cast(data), dataLen, peerUdid, { std::string(peerSessionName) }); +} + +void AppDataListenerWrap::NotifyDataListeners( + const uint8_t *ptr, const int size, const std::string &deviceId, const PipeInfo &pipeInfo) +{ + return softBusAdapter_->NotifyDataListeners(ptr, size, deviceId, pipeInfo); +} +} // namespace ObjectStore +} // namespace OHOS diff --git a/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/BUILD.gn b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..69bcbecebf79042b81d62a81efd7aa1de1c7d162 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/BUILD.gn @@ -0,0 +1,48 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") +config("module_private_config") { + visibility = [ ":*" ] + + include_dirs = [] +} + +##############################fuzztest########################################## +ohos_fuzztest("ObjectStoreFuzzTest") { + module_out_path = "data_object/impl" + + fuzz_config_file = "//foundation/distributeddatamgr/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer" + + sources = [ "objectstore_fuzzer.cpp" ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "data_object:distributeddataobject_impl", + "hilog_native:libhilog", + ] +} + +############################################################################### +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":ObjectStoreFuzzTest", + ] +} +############################################################################### diff --git a/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/corpus/init b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..8eb5a7d6eb6b7d71f0c70c244e5768d62bee6ac5 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/corpus/init @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +FUZZ \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/objectstore_fuzzer.cpp b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/objectstore_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c41d7fe91a6b19e013930379fd37791231e1ff86 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/objectstore_fuzzer.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "distributed_object.h" +#include "distributed_objectstore.h" +#include "objectstore_errors.h" + +using namespace OHOS::ObjectStore; + +namespace OHOS { +bool PutDoubleFuzz(const uint8_t *data, size_t size) +{ + bool result = false; + std::string bundleName = "com.example.myapplication"; + std::string sessionId = "123456"; + double sval = static_cast(size); + std::string skey(data, data + size); + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + DistributedObject *object = objectStore->CreateObject(sessionId); + uint32_t ret = object->PutDouble(skey, sval); + if (!ret) { + result = true; + } + objectStore->DeleteObject(sessionId); + return result; +} + +bool PutBooleanFuzz(const uint8_t *data, size_t size) +{ + bool result = false; + std::string bundleName = "com.example.myapplication"; + std::string sessionId = "123456"; + std::string skey(data, data + size); + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + DistributedObject *object = objectStore->CreateObject(sessionId); + uint32_t ret = object->PutBoolean(skey, true); + if (!ret) { + result = true; + } + ret = object->PutBoolean(skey, false); + if (ret != SUCCESS) { + result = false; + } + objectStore->DeleteObject(sessionId); + return result; +} + +bool PutStringFuzz(const uint8_t *data, size_t size) +{ + bool result = false; + std::string bundleName = "com.example.myapplication"; + std::string sessionId = "123456"; + std::string skey(data, data + size); + std::string sval(data, data + size); + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + DistributedObject *object = objectStore->CreateObject(sessionId); + uint32_t ret = object->PutString(skey, sval); + if (!ret) { + result = true; + } + objectStore->DeleteObject(sessionId); + return result; +} + +bool PutComplexFuzz(const uint8_t *data, size_t size) +{ + bool result = false; + std::string bundleName = "com.example.myapplication"; + std::string sessionId = "123456"; + size_t sum = 10; + std::string skey(data, data + size); + std::vector value; + for (int i = 0; i < sum; i++) { + value.push_back(*data + i); + } + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + DistributedObject *object = objectStore->CreateObject(sessionId); + uint32_t ret = object->PutComplex(skey, value); + if (!ret) { + result = true; + } + objectStore->DeleteObject(sessionId); + return result; +} + +bool GetDoubleFuzz(const uint8_t *data, size_t size) +{ + bool result = false; + std::string bundleName = "com.example.myapplication"; + std::string sessionId = "123456"; + double sval = static_cast(size); + double val; + std::string skey(data, data + size); + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + DistributedObject *object = objectStore->CreateObject(sessionId); + if (SUCCESS == object->PutDouble(skey, sval)) { + uint32_t ret = object->GetDouble(skey, val); + if (!ret) { + result = true; + } + } + objectStore->DeleteObject(sessionId); + return result; +} + +bool GetBooleanFuzz(const uint8_t *data, size_t size) +{ + bool val, result = false; + std::string bundleName = "com.example.myapplication"; + std::string sessionId = "123456"; + std::string skey(data, data + size); + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + DistributedObject *object = objectStore->CreateObject(sessionId); + if (SUCCESS == object->PutBoolean(skey, true)) { + uint32_t ret = object->GetBoolean(skey, val); + if (!ret) { + result = true; + } + } + objectStore->DeleteObject(sessionId); + return result; +} + +bool GetStringFuzz(const uint8_t *data, size_t size) +{ + bool result = false; + std::string bundleName = "com.example.myapplication"; + std::string sessionId = "123456"; + std::string skey(data, data + size); + std::string sval(data, data + size); + std::string val; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + DistributedObject *object = objectStore->CreateObject(sessionId); + if (SUCCESS == object->PutString(skey, sval)) { + uint32_t ret = object->GetString(skey, val); + if (!ret) { + result = true; + } + } + objectStore->DeleteObject(sessionId); + return result; +} + +bool GetComplexFuzz(const uint8_t *data, size_t size) +{ + bool result = false; + std::string bundleName = "com.example.myapplication"; + std::string sessionId = "123456"; + size_t sum = 10; + std::string skey(data, data + size); + std::vector svalue; + std::vector val; + for (int i = 0; i < sum; i++) { + svalue.push_back(*data + i); + } + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + DistributedObject *object = objectStore->CreateObject(sessionId); + if (SUCCESS == object->PutComplex(skey, svalue)) { + uint32_t ret = object->GetComplex(skey, val); + if (!ret) { + result = true; + } + } + objectStore->DeleteObject(sessionId); + return result; +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + OHOS::PutDoubleFuzz(data, size); + OHOS::PutBooleanFuzz(data, size); + OHOS::PutStringFuzz(data, size); + OHOS::PutComplexFuzz(data, size); + OHOS::GetDoubleFuzz(data, size); + OHOS::GetBooleanFuzz(data, size); + OHOS::GetStringFuzz(data, size); + OHOS::GetComplexFuzz(data, size); + /* Run your code on data */ + return 0; +} \ No newline at end of file diff --git a/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/objectstore_fuzzer.h b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/objectstore_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..3431adfecd542b98f09dd654f2a145a0ca12b3d3 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/objectstore_fuzzer.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBJECTSTORE_FUZZER_H +#define OBJECTSTORE_FUZZER_H + +#define OBJECTSTORE_FUZZER_H "objectstore_fuzzer" + +#endif // OBJECTSTORE_FUZZER_H + diff --git a/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/project.xml b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..6e8ad2cfde8f8bda4beb6cabbe7efd8bc3c54eec --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/test/fuzztest/objectstore_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 300 + + 4096 + + diff --git a/data_object/frameworks/innerkitsimpl/test/unittest/BUILD.gn b/data_object/frameworks/innerkitsimpl/test/unittest/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..521846dc68e17198c900644d47eda07c319b246a --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/test/unittest/BUILD.gn @@ -0,0 +1,42 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/test.gni") + +module_output_path = "data_object/impl" + +config("module_private_config") { + visibility = [ ":*" ] + + include_dirs = [] +} + +ohos_unittest("NativeObjectStoreTest") { + module_out_path = module_output_path + + sources = [ "object_store_test.cpp" ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "data_object:distributeddataobject_impl", + "hilog_native:libhilog", + ] + + deps = [ "//third_party/googletest:gtest_main" ] +} + +group("unittest") { + testonly = true + deps = [ ":NativeObjectStoreTest" ] +} diff --git a/data_object/frameworks/innerkitsimpl/test/unittest/object_store_test.cpp b/data_object/frameworks/innerkitsimpl/test/unittest/object_store_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..39cbc362986b2bf53561d01a05e8e4111efaa7c1 --- /dev/null +++ b/data_object/frameworks/innerkitsimpl/test/unittest/object_store_test.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include "distributed_object.h" +#include "distributed_objectstore.h" +#include "objectstore_errors.h" + +using namespace testing::ext; +using namespace OHOS::ObjectStore; + +constexpr static double SALARY = 100.5; + +static void TestSetSessionId(std::string bundleName, std::string sessionId) +{ + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + uint32_t ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(SUCCESS, ret); +} + +static void TestSaveAndRevokeSave(std::string bundleName, std::string sessionId) +{ + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + uint32_t ret = object->PutString("name", "zhangsan"); + EXPECT_EQ(SUCCESS, ret); + ret = object->PutDouble("salary", SALARY); + EXPECT_EQ(SUCCESS, ret); + ret = object->PutBoolean("isTrue", true); + EXPECT_EQ(SUCCESS, ret); + + ret = object->Save("local"); + EXPECT_EQ(SUCCESS, ret); + ret = object->RevokeSave(); + EXPECT_EQ(SUCCESS, ret); + + ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(SUCCESS, ret); +} + +class NativeObjectStoreTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void NativeObjectStoreTest::SetUpTestCase(void) +{ + // input testsuit setup step,setup invoked before all testcases +} + +void NativeObjectStoreTest::TearDownTestCase(void) +{ + // input testsuit teardown step,teardown invoked after all testcases +} + +void NativeObjectStoreTest::SetUp(void) +{ + // input testcase setup step,setup invoked before each testcases +} + +void NativeObjectStoreTest::TearDown(void) +{ + // input testcase teardown step,teardown invoked after each testcases +} + +/** + * @tc.name: DistributedObjectStore_Create_Destroy_001 + * @tc.desc: test Create DistributedObject and Destroy DistrbutedObject + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObjectStore_Create_Destroy_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + uint32_t ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(SUCCESS, ret); +} + +/** + * @tc.name: DistributedObjectStore_Get_001 + * @tc.desc: test DistributedObjectStore Get. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObjectStore_Get_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + DistributedObject *object2 = nullptr; + uint32_t ret = objectStore->Get(sessionId, &object2); + EXPECT_EQ(SUCCESS, ret); + EXPECT_EQ(object, object2); + + ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(SUCCESS, ret); +} + +/** + * @tc.name: DistributedObjectStore_Watch_UnWatch_001 + * @tc.desc: test DistributedObjectStore Watch and UnWatch. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObjectStore_Watch_UnWatch_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + auto watcherPtr = std::shared_ptr(); + uint32_t ret = objectStore->Watch(object, watcherPtr); + EXPECT_EQ(SUCCESS, ret); + + ret = objectStore->UnWatch(object); + EXPECT_EQ(SUCCESS, ret); + + ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(SUCCESS, ret); +} + +/** + * @tc.name: DistributedObjectStore_SetStatusNotifier_001 + * @tc.desc: test DistributedObjectStore SetStatusNotifier. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObjectStore_SetStatusNotifier_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + auto notifierPtr = std::shared_ptr(); + uint32_t ret = objectStore->SetStatusNotifier(notifierPtr); + EXPECT_EQ(ret, 0); + + ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(ret, 0); +} + +/** + * @tc.name: DistributedObject_Double_001 + * @tc.desc: test DistributedObjectStore PutDouble. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObject_Double_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + uint32_t ret = object->PutDouble("salary", SALARY); + EXPECT_EQ(ret, 0); + + double value = 0.0; + object->GetDouble("salary", value); + EXPECT_EQ(ret, 0); + EXPECT_EQ(value, SALARY); + + ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(ret, 0); +} + +/** + * @tc.name: DistributedObject_Boolean_001 + * @tc.desc: test DistributedObjectStore PutBoolean. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObject_Boolean_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + uint32_t ret = object->PutBoolean("isTrue", true); + EXPECT_EQ(SUCCESS, ret); + + + bool value = false; + ret = object->GetBoolean("isTrue", value); + EXPECT_EQ(SUCCESS, ret); + EXPECT_EQ(true, value); + + ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(SUCCESS, ret); +} + +/** + * @tc.name: DistributedObject_String_001 + * @tc.desc: test DistributedObjectStore String. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObject_String_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + uint32_t ret = object->PutString("name", "zhangsan"); + EXPECT_EQ(SUCCESS, ret); + + std::string value = ""; + ret = object->GetString("name", value); + EXPECT_EQ(SUCCESS, ret); + EXPECT_EQ(value, "zhangsan"); + + ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(SUCCESS, ret); +} + +/** + * @tc.name: DistributedObject_GetSessionId_001 + * @tc.desc: test DistributedObjectStore GetSessionId. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObject_GetSessionId_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + std::string getSessionId = object->GetSessionId(); + EXPECT_EQ(sessionId, getSessionId); + uint32_t ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(SUCCESS, ret); +} + +/** + * @tc.name: DistributedObject_TestSetSessionId_001 + * @tc.desc: test DistributedObjectStore TestSetSessionId. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObject_TestSetSessionId_001, TestSize.Level1) +{ + std::thread t1(TestSetSessionId, "default1", "session1"); + std::thread t2(TestSetSessionId, "default2", "session2"); + std::thread t3(TestSetSessionId, "default3", "session3"); + t1.join(); + t2.join(); + t3.join(); +} + +/** + * @tc.name: DistributedObject_GetType_001 + * @tc.desc: test DistributedObject GetType. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObject_GetType_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + DistributedObjectStore *objectStore = DistributedObjectStore::GetInstance(bundleName); + EXPECT_NE(nullptr, objectStore); + DistributedObject *object = objectStore->CreateObject(sessionId); + EXPECT_NE(nullptr, object); + + uint32_t ret = object->PutString("name", "zhangsan"); + EXPECT_EQ(SUCCESS, ret); + Type type; + ret = object->GetType("name", type); + EXPECT_EQ(SUCCESS, ret); + EXPECT_EQ(TYPE_STRING, type); + + ret = object->PutDouble("salary", SALARY); + EXPECT_EQ(SUCCESS, ret); + ret = object->GetType("salary", type); + EXPECT_EQ(SUCCESS, ret); + EXPECT_EQ(TYPE_DOUBLE, type); + + ret = object->PutBoolean("isTrue", true); + EXPECT_EQ(SUCCESS, ret); + ret = object->GetType("isTrue", type); + EXPECT_EQ(SUCCESS, ret); + EXPECT_EQ(TYPE_BOOLEAN, type); + + ret = objectStore->DeleteObject(sessionId); + EXPECT_EQ(SUCCESS, ret); +} + +/** + * @tc.name: DistributedObject_Save_RevokeSave_001 + * @tc.desc: test DistributedObjectStore Save. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObject_Save_RevokeSave_001, TestSize.Level1) +{ + std::string bundleName = "default"; + std::string sessionId = "123456"; + TestSaveAndRevokeSave(bundleName, sessionId); +} + +/** + * @tc.name: DistributedObject_Save_RevokeSave_002 + * @tc.desc: test DistributedObjectStore Save. + * @tc.type: FUNC + */ +HWTEST_F(NativeObjectStoreTest, DistributedObject_Save_RevokeSave_002, TestSize.Level1) +{ + std::thread t1(TestSaveAndRevokeSave, "default1", "session1"); + std::thread t2(TestSaveAndRevokeSave, "default2", "session2"); + std::thread t3(TestSaveAndRevokeSave, "default3", "session3"); + t1.join(); + t2.join(); + t3.join(); +} \ No newline at end of file diff --git a/data_object/frameworks/jskitsimpl/include/adaptor/js_common.h b/data_object/frameworks/jskitsimpl/include/adaptor/js_common.h new file mode 100644 index 0000000000000000000000000000000000000000..65d9da5c0ba65bb1a6a507fbf22b6f2434af1ea1 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/include/adaptor/js_common.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JS_COMMON_H +#define JS_COMMON_H +#include "hilog/log.h" +namespace OHOS::ObjectStore { +#define CHECK_EQUAL_WITH_RETURN_NULL(status, value) \ + { \ + if (status != value) { \ + LOG_ERROR("error! %{public}d %{public}d", status, value); \ + return nullptr; \ + } \ + } +#define CHECK_EQUAL_WITH_RETURN_VOID(status, value) \ + { \ + if (status != value) { \ + LOG_ERROR("error! %{public}d %{public}d", status, value); \ + return; \ + } \ + } +#define ASSERT_MATCH_ELSE_RETURN_VOID(condition) \ + { \ + if (!(condition)) { \ + LOG_ERROR("error! %{public}s", #condition); \ + return; \ + } \ + } +#define ASSERT_MATCH_ELSE_RETURN_NULL(condition) \ + { \ + if (!(condition)) { \ + LOG_ERROR("error! %{public}s", #condition); \ + return nullptr; \ + } \ + } +#define ASSERT_MATCH_ELSE_GOTO_ERROR(condition) \ + { \ + if (!(condition)) { \ + LOG_ERROR("error! %{public}s", #condition); \ + goto ERROR; \ + } \ + } +} // namespace OHOS::ObjectStore +static const char *CHANGE = "change"; +static const char *STATUS = "status"; +#endif // JS_COMMON_H diff --git a/data_object/frameworks/jskitsimpl/include/adaptor/js_distributedobject.h b/data_object/frameworks/jskitsimpl/include/adaptor/js_distributedobject.h new file mode 100644 index 0000000000000000000000000000000000000000..9cca0f5476bc77e7517737ed6e626d2142f16d99 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/include/adaptor/js_distributedobject.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JS_DISTRIBUTEDOBJECT_H +#define JS_DISTRIBUTEDOBJECT_H + +#include + +#include "distributed_objectstore.h" +#include "js_object_wrapper.h" +namespace OHOS::ObjectStore { +struct ConstructContext { + DistributedObjectStore *objectStore; + DistributedObject *object; +}; + +class JSDistributedObject { +public: + static napi_value JSConstructor(napi_env env, napi_callback_info info); + static napi_value JSGet(napi_env env, napi_callback_info info); + static napi_value JSPut(napi_env env, napi_callback_info info); + static napi_value JSSave(napi_env env, napi_callback_info info); + static napi_value JSRevokeSave(napi_env env, napi_callback_info info); + static napi_value GetCons(napi_env env); + +private: + static void DoPut(napi_env env, JSObjectWrapper *wrapper, char *key, napi_valuetype type, napi_value value); + static void DoGet(napi_env env, JSObjectWrapper *wrapper, char *key, napi_value &value); + static napi_value GetSaveResultCons(napi_env env, std::string &sessionId, double version, std::string deviceId); + static napi_value GetRevokeSaveResultCons(napi_env env, std::string &sessionId); +}; +} // namespace OHOS::ObjectStore + +#endif diff --git a/data_object/frameworks/jskitsimpl/include/adaptor/js_distributedobjectstore.h b/data_object/frameworks/jskitsimpl/include/adaptor/js_distributedobjectstore.h new file mode 100644 index 0000000000000000000000000000000000000000..5ab653b7f9ced5e5a85fd6146f39b0e177b7a563 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/include/adaptor/js_distributedobjectstore.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JS_DISTRIBUTEDDATAOBJECTSTORE_H +#define JS_DISTRIBUTEDDATAOBJECTSTORE_H + +#include + +#include "distributed_objectstore.h" +#include "js_native_api.h" +#include "js_object_wrapper.h" +#include "node_api.h" +namespace OHOS::ObjectStore { +class JSDistributedObjectStore { +public: + static napi_value JSCreateObjectSync(napi_env env, napi_callback_info info); + static napi_value JSDestroyObjectSync(napi_env env, napi_callback_info info); + static napi_value JSOn(napi_env env, napi_callback_info info); + static napi_value JSOff(napi_env env, napi_callback_info info); + static napi_value JSRecordCallback(napi_env env, napi_callback_info info); + static napi_value JSDeleteCallback(napi_env env, napi_callback_info info); +private: + static napi_value NewDistributedObject( + napi_env env, DistributedObjectStore *objectStore, DistributedObject *object, const std::string &objectId); + static void AddCallback(napi_env env, std::map> &callbacks, + const std::string &objectId, napi_value callback); + static void DelCallback(napi_env env, std::map> &callbacks, + const std::string &sessionId, napi_value callback = nullptr); + static bool CheckSyncPermission(); + static void RestoreWatchers(napi_env env, JSObjectWrapper *wrapper, const std::string &objectId); + static std::string GetBundleName(napi_env env); + static bool IsSandBox(); +}; +} // namespace OHOS::ObjectStore +#endif // JS_DISTRIBUTEDDATAOBJECTSTORE_H diff --git a/data_object/frameworks/jskitsimpl/include/adaptor/js_object_wrapper.h b/data_object/frameworks/jskitsimpl/include/adaptor/js_object_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..626be1db3af60cf0e1f2f9f2e13753c9e4c821cf --- /dev/null +++ b/data_object/frameworks/jskitsimpl/include/adaptor/js_object_wrapper.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JS_OBJECT_WRAPPER_H +#define JS_OBJECT_WRAPPER_H + +#include + +#include "distributed_object.h" +#include "distributed_objectstore.h" +#include "js_watcher.h" + +namespace OHOS::ObjectStore { +class JSObjectWrapper { +public: + JSObjectWrapper(DistributedObjectStore *objectStore, DistributedObject *object); + virtual ~JSObjectWrapper(); + DistributedObject *GetObject(); + bool AddWatch(napi_env env, const char *type, napi_value handler); + void DeleteWatch(napi_env env, const char *type, napi_value handler = nullptr); + bool isUndefined(char *value); + void AddUndefined(char *value); + void DeleteUndefined(char *value); + void DestroyObject(); + +private: + DistributedObjectStore *objectStore_; + DistributedObject *object_; + std::unique_ptr watcher_ = nullptr; + std::shared_mutex watchMutex_{}; + std::vector undefinedProperties; +}; +} // namespace OHOS::ObjectStore + +#endif diff --git a/data_object/frameworks/jskitsimpl/include/adaptor/js_watcher.h b/data_object/frameworks/jskitsimpl/include/adaptor/js_watcher.h new file mode 100644 index 0000000000000000000000000000000000000000..877c3180a366efc157269ce0d0c3f00c7d481acf --- /dev/null +++ b/data_object/frameworks/jskitsimpl/include/adaptor/js_watcher.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JSWATCHER_H +#define JSWATCHER_H + +#include "distributed_objectstore.h" +#include "flat_object_store.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "uv_queue.h" + +namespace OHOS::ObjectStore { +class JSWatcher; +struct EventHandler { + napi_ref callbackRef = nullptr; + EventHandler *next = nullptr; +}; + +class EventListener { +public: + EventListener() : handlers_(nullptr) + { + } + + virtual ~EventListener() + { + } + + virtual bool Add(napi_env env, napi_value handler); + + virtual bool Del(napi_env env, napi_value handler); + + virtual void Clear(napi_env env); + + EventHandler *Find(napi_env env, napi_value handler); + EventHandler *handlers_; +}; + +class ChangeEventListener : public EventListener { +public: + ChangeEventListener(JSWatcher *watcher, DistributedObjectStore *objectStore, DistributedObject *object); + + bool Add(napi_env env, napi_value handler) override; + + bool Del(napi_env env, napi_value handler) override; + + void Clear(napi_env env) override; + +private: + bool isWatched_ = false; + DistributedObjectStore *objectStore_; + DistributedObject *object_; + JSWatcher *watcher_; +}; + +class StatusEventListener : public EventListener { +public: + StatusEventListener(JSWatcher *watcher, const std::string &sessionId); + bool Add(napi_env env, napi_value handler) override; + + bool Del(napi_env env, napi_value handler) override; + + void Clear(napi_env env) override; + +private: + JSWatcher *watcher_; + std::string sessionId_; +}; + +class JSWatcher : public UvQueue { +public: + JSWatcher(const napi_env env, DistributedObjectStore *objectStore, DistributedObject *object); + + ~JSWatcher(); + + bool On(const char *type, napi_value handler); + + void Off(const char *type, napi_value handler = nullptr); + + void Emit(const char *type, const std::string &sessionId, const std::vector &changeData); + + void Emit(const char *type, const std::string &sessionId, const std::string &networkId, const std::string &status); + +private: + struct ChangeArgs { + ChangeArgs(const napi_ref callback, const std::string &sessionId, const std::vector &changeData); + napi_ref callback_; + const std::string sessionId_; + const std::vector changeData_; + }; + struct StatusArgs { + StatusArgs(const napi_ref callback, const std::string &sessionId, const std::string &networkId, + const std::string &status); + napi_ref callback_; + const std::string sessionId_; + const std::string networkId_; + const std::string status_; + }; + EventListener *Find(const char *type); + static void ProcessChange(napi_env env, std::list &args); + static void ProcessStatus(napi_env env, std::list &args); + napi_env env_; + ChangeEventListener *changeEventListener_; + StatusEventListener *statusEventListener_; +}; + +class WatcherImpl : public ObjectWatcher { +public: + WatcherImpl(JSWatcher *watcher) : watcher_(watcher) + { + } + + virtual ~WatcherImpl(); + + void OnChanged(const std::string &sessionid, const std::vector &changedData) override; + +private: + JSWatcher *watcher_ = nullptr; +}; +} // namespace OHOS::ObjectStore + +#endif // JSWATCHER_H diff --git a/data_object/frameworks/jskitsimpl/include/adaptor/notifier_impl.h b/data_object/frameworks/jskitsimpl/include/adaptor/notifier_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..0f6d394f1b4c68ef2ad19919f1c0774df4bf134e --- /dev/null +++ b/data_object/frameworks/jskitsimpl/include/adaptor/notifier_impl.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JS_NOTIFIER_IMPL_H +#define JS_NOTIFIER_IMPL_H + +#include + +#include "distributed_objectstore.h" +#include "js_watcher.h" +namespace OHOS::ObjectStore { +class NotifierImpl : public StatusNotifier { +public: + static std::shared_ptr GetInstance(); + virtual ~NotifierImpl(); + void AddWatcher(std::string &sessionId, JSWatcher *watcher); + void DelWatcher(std::string &sessionId); + void OnChanged( + const std::string &sessionId, const std::string &networkId, const std::string &onlineStatus) override; + +private: + std::mutex mutex_; + std::map watchers_; +}; +} // namespace OHOS::ObjectStore +#endif // JS_NOTIFIER_IMPL_H diff --git a/data_object/frameworks/jskitsimpl/include/common/js_util.h b/data_object/frameworks/jskitsimpl/include/common/js_util.h new file mode 100644 index 0000000000000000000000000000000000000000..4e1c675c928caad012d6a4759126a01f1b89ed48 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/include/common/js_util.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OHOS_JS_UTIL_H +#define OHOS_JS_UTIL_H +#include +#include +#include + +#include "napi/native_api.h" +#include "napi/native_node_api.h" + +namespace OHOS::ObjectStore { +class JSUtil final { +public: + /* napi_value <-> bool */ + static napi_status GetValue(napi_env env, napi_value in, bool &out); + static napi_status SetValue(napi_env env, const bool &in, napi_value &out); + + /* napi_value <-> double */ + static napi_status GetValue(napi_env env, napi_value in, double &out); + static napi_status SetValue(napi_env env, const double &in, napi_value &out); + + /* napi_value <-> std::string */ + static napi_status GetValue(napi_env env, napi_value in, std::string &out); + static napi_status SetValue(napi_env env, const std::string &in, napi_value &out); + + /* napi_value <-> std::vector */ + static napi_status GetValue(napi_env env, napi_value in, std::vector &out); + static napi_status SetValue(napi_env env, const std::vector &in, napi_value &out); + + /* napi_value <-> std::vector */ + static napi_status GetValue(napi_env env, napi_value in, std::vector &out); + static napi_status SetValue(napi_env env, const std::vector &in, napi_value &out); +}; + +#define LOG_ERROR_RETURN(condition, message, retVal) \ + do { \ + if (!(condition)) { \ + LOG_ERROR("test (" #condition ") failed: " message); \ + return retVal; \ + } \ + } while (0) + +#define LOG_ERROR_RETURN_VOID(condition, message) \ + do { \ + if (!(condition)) { \ + LOG_ERROR("test (" #condition ") failed: " message); \ + return; \ + } \ + } while (0) +} // namespace OHOS::ObjectStore +#endif // OHOS_JS_UTIL_H diff --git a/data_object/frameworks/jskitsimpl/include/common/napi_queue.h b/data_object/frameworks/jskitsimpl/include/common/napi_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..58865d7fdd679709aa6507f64cc82266cd27e582 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/include/common/napi_queue.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef NAPI_QUEUE_H +#define NAPI_QUEUE_H +#include +#include +#include + +#include "napi/native_api.h" +#include "napi/native_common.h" +#include "napi/native_node_api.h" + +namespace OHOS::ObjectStore { +using NapiCbInfoParser = std::function; +using NapiAsyncExecute = std::function; +using NapiAsyncComplete = std::function; +static constexpr size_t ARGC_MAX = 6; +struct ContextBase { + virtual ~ContextBase(); + void GetCbInfo( + napi_env env, napi_callback_info info, NapiCbInfoParser parse = NapiCbInfoParser(), bool sync = false); + + inline void GetCbInfoSync(napi_env env, napi_callback_info info, NapiCbInfoParser parse = NapiCbInfoParser()) + { + /* sync = true, means no callback, not AsyncWork. */ + GetCbInfo(env, info, parse, true); + } + + napi_env env = nullptr; + napi_value output = nullptr; + napi_status status = napi_invalid_arg; + std::string error; + + napi_value self = nullptr; + void *native = nullptr; + +private: + napi_deferred deferred = nullptr; + napi_async_work work = nullptr; + napi_ref callbackRef = nullptr; + napi_ref selfRef = nullptr; + + NapiAsyncExecute execute = nullptr; + NapiAsyncComplete complete = nullptr; + std::shared_ptr hold; /* cross thread data */ + + friend class NapiQueue; +}; + +/* check condition related to argc/argv, return and logging. */ +#define CHECK_ARGS_RETURN_VOID(ctxt, condition, message) \ + do { \ + if (!(condition)) { \ + (ctxt)->status = napi_invalid_arg; \ + (ctxt)->error = std::string(message); \ + LOG_ERROR("test (" #condition ") failed: " message); \ + return; \ + } \ + } while (0) + +#define CHECK_STATUS_RETURN_VOID(ctxt, message) \ + do { \ + if ((ctxt)->status != napi_ok) { \ + (ctxt)->error = std::string(message); \ + LOG_ERROR("test (ctxt->status == napi_ok) failed: " message); \ + return; \ + } \ + } while (0) +class NapiQueue { +public: + static napi_value AsyncWork(napi_env env, std::shared_ptr ctxt, const std::string &name, + NapiAsyncExecute execute = NapiAsyncExecute(), NapiAsyncComplete complete = NapiAsyncComplete()); + +private: + enum { + /* AsyncCallback / Promise output result index */ + RESULT_ERROR = 0, + RESULT_DATA = 1, + RESULT_ALL = 2 + }; + static void GenerateOutput(ContextBase *ctxt); +}; +} // namespace OHOS::ObjectStore +#endif // OHOS_NAPI_QUEUE_H diff --git a/data_object/frameworks/jskitsimpl/include/common/uv_queue.h b/data_object/frameworks/jskitsimpl/include/common/uv_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..699186aafa40745ad39fb215345aa7886446286f --- /dev/null +++ b/data_object/frameworks/jskitsimpl/include/common/uv_queue.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef UV_QUEUE_H +#define UV_QUEUE_H +#include +#include +#include +#include + +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "uv.h" + +namespace OHOS::ObjectStore { +typedef void (*Process)(napi_env env, std::list &); +class UvQueue { +public: + UvQueue(napi_env env); + virtual ~UvQueue(); + + void CallFunction(Process process, void *argv); + +private: + napi_env env_; + std::shared_mutex mutex_{}; + // key is callback,value is list of args + std::map> args_; + uv_loop_s *loop_ = nullptr; +}; +} // namespace OHOS::ObjectStore +#endif diff --git a/data_object/frameworks/jskitsimpl/src/adaptor/js_distributedobject.cpp b/data_object/frameworks/jskitsimpl/src/adaptor/js_distributedobject.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a404ed105e07e7a55f5c65197925126ebc28f15 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/src/adaptor/js_distributedobject.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_distributedobject.h" + +#include + +#include "js_common.h" +#include "js_object_wrapper.h" +#include "js_util.h" +#include "logger.h" +#include "napi_queue.h" +#include "objectstore_errors.h" + +namespace OHOS::ObjectStore { +constexpr size_t KEY_SIZE = 64; +napi_value JSDistributedObject::JSConstructor(napi_env env, napi_callback_info info) +{ + LOG_INFO("start"); + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, nullptr, 0, &thisVar, &data); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + return thisVar; +} + +// get(key: string): ValueType; +napi_value JSDistributedObject::JSGet(napi_env env, napi_callback_info info) +{ + size_t requireArgc = 1; + size_t argc = 1; + napi_value argv[1] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + char key[KEY_SIZE] = { 0 }; + size_t keyLen = 0; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(argc >= requireArgc); + status = napi_get_value_string_utf8(env, argv[0], key, KEY_SIZE, &keyLen); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + JSObjectWrapper *wrapper = nullptr; + status = napi_unwrap(env, thisVar, (void **)&wrapper); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(wrapper != nullptr); + ASSERT_MATCH_ELSE_RETURN_NULL(wrapper->GetObject() != nullptr); + napi_value result = nullptr; + if (wrapper->isUndefined(key)) { + napi_get_undefined(env, &result); + return result; + } + DoGet(env, wrapper, key, result); + return result; +} + +// put(key: string, value: ValueType): void; +napi_value JSDistributedObject::JSPut(napi_env env, napi_callback_info info) +{ + size_t requireArgc = 2; + size_t argc = 2; + napi_value argv[2] = { 0 }; + napi_value thisVar = nullptr; + char key[KEY_SIZE] = { 0 }; + size_t keyLen = 0; + napi_valuetype valueType; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(argc >= requireArgc); + status = napi_typeof(env, argv[0], &valueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + CHECK_EQUAL_WITH_RETURN_NULL(valueType, napi_string); + status = napi_get_value_string_utf8(env, argv[0], key, KEY_SIZE, &keyLen); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + status = napi_typeof(env, argv[1], &valueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + JSObjectWrapper *wrapper = nullptr; + status = napi_unwrap(env, thisVar, (void **)&wrapper); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(wrapper != nullptr); + ASSERT_MATCH_ELSE_RETURN_NULL(wrapper->GetObject() != nullptr); + if (valueType == napi_undefined) { + wrapper->AddUndefined(key); + return nullptr; + } + wrapper->DeleteUndefined(key); + DoPut(env, wrapper, key, valueType, argv[1]); + LOG_INFO("put %{public}s success", key); + return nullptr; +} + +napi_value JSDistributedObject::GetCons(napi_env env) +{ + static thread_local napi_ref g_instance = nullptr; + napi_value distributedObjectClass = nullptr; + if (g_instance != nullptr) { + napi_status status = napi_get_reference_value(env, g_instance, &distributedObjectClass); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + return distributedObjectClass; + } + const char *distributedObjectName = "DistributedObject"; + napi_property_descriptor distributedObjectDesc[] = { + DECLARE_NAPI_FUNCTION("put", JSDistributedObject::JSPut), + DECLARE_NAPI_FUNCTION("get", JSDistributedObject::JSGet), + DECLARE_NAPI_FUNCTION("save", JSDistributedObject::JSSave), + DECLARE_NAPI_FUNCTION("revokeSave", JSDistributedObject::JSRevokeSave), + }; + + napi_status status = napi_define_class(env, distributedObjectName, strlen(distributedObjectName), + JSDistributedObject::JSConstructor, nullptr, sizeof(distributedObjectDesc) / sizeof(distributedObjectDesc[0]), + distributedObjectDesc, &distributedObjectClass); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + if (g_instance == nullptr) { + status = napi_create_reference(env, distributedObjectClass, 1, &g_instance); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + } + return distributedObjectClass; +} + +void JSDistributedObject::DoPut( + napi_env env, JSObjectWrapper *wrapper, char *key, napi_valuetype type, napi_value value) +{ + std::string keyString = key; + switch (type) { + case napi_boolean: { + bool putValue = false; + napi_status status = JSUtil::GetValue(env, value, putValue); + CHECK_EQUAL_WITH_RETURN_VOID(status, napi_ok); + wrapper->GetObject()->PutBoolean(keyString, putValue); + break; + } + case napi_number: { + double putValue = 0; + napi_status status = JSUtil::GetValue(env, value, putValue); + CHECK_EQUAL_WITH_RETURN_VOID(status, napi_ok); + wrapper->GetObject()->PutDouble(keyString, putValue); + break; + } + case napi_string: { + std::string putValue; + napi_status status = JSUtil::GetValue(env, value, putValue); + CHECK_EQUAL_WITH_RETURN_VOID(status, napi_ok); + wrapper->GetObject()->PutString(keyString, putValue); + break; + } + case napi_object: { + std::vector putValue; + napi_status status = JSUtil::GetValue(env, value, putValue); + CHECK_EQUAL_WITH_RETURN_VOID(status, napi_ok); + wrapper->GetObject()->PutComplex(keyString, putValue); + break; + } + default: { + LOG_ERROR("error type! %{public}d", type); + break; + } + } +} + +void JSDistributedObject::DoGet(napi_env env, JSObjectWrapper *wrapper, char *key, napi_value &value) +{ + std::string keyString = key; + Type type = TYPE_STRING; + wrapper->GetObject()->GetType(keyString, type); + LOG_DEBUG("get type %{public}s %{public}d", key, type); + switch (type) { + case TYPE_STRING: { + std::string result; + uint32_t ret = wrapper->GetObject()->GetString(keyString, result); + ASSERT_MATCH_ELSE_RETURN_VOID(ret == SUCCESS) + napi_status status = JSUtil::SetValue(env, result, value); + ASSERT_MATCH_ELSE_RETURN_VOID(status == napi_ok) + break; + } + case TYPE_DOUBLE: { + double result; + uint32_t ret = wrapper->GetObject()->GetDouble(keyString, result); + LOG_DEBUG("%{public}f", result); + ASSERT_MATCH_ELSE_RETURN_VOID(ret == SUCCESS) + napi_status status = JSUtil::SetValue(env, result, value); + ASSERT_MATCH_ELSE_RETURN_VOID(status == napi_ok) + break; + } + case TYPE_BOOLEAN: { + bool result; + uint32_t ret = wrapper->GetObject()->GetBoolean(keyString, result); + LOG_DEBUG("%{public}d", result); + ASSERT_MATCH_ELSE_RETURN_VOID(ret == SUCCESS) + napi_status status = JSUtil::SetValue(env, result, value); + ASSERT_MATCH_ELSE_RETURN_VOID(status == napi_ok) + break; + } + case TYPE_COMPLEX: { + std::vector result; + uint32_t ret = wrapper->GetObject()->GetComplex(keyString, result); + ASSERT_MATCH_ELSE_RETURN_VOID(ret == SUCCESS) + napi_status status = JSUtil::SetValue(env, result, value); + ASSERT_MATCH_ELSE_RETURN_VOID(status == napi_ok) + break; + } + default: { + LOG_ERROR("error type! %{public}d", type); + break; + } + } +} + +// save(deviceId: string, version: number, callback?:AsyncCallback): void; +// save(deviceId: string, version: number): Promise; +napi_value JSDistributedObject::JSSave(napi_env env, napi_callback_info info) +{ + LOG_DEBUG("JSSave()"); + struct SaveContext : public ContextBase { + double version; + std::string deviceId; + DistributedObject *object; + }; + auto ctxt = std::make_shared(); + std::function getCbOpe = [env, ctxt](size_t argc, napi_value *argv) { + // required 1 arguments :: + CHECK_ARGS_RETURN_VOID(ctxt, argc >= 2, "invalid arguments!"); + ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->deviceId); + CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid deviceId!"); + ctxt->status = JSUtil::GetValue(env, argv[1], ctxt->version); + CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[1], i.e. invalid version!"); + JSObjectWrapper *wrapper = nullptr; + napi_status status = napi_unwrap(env, ctxt->self, (void **)&wrapper); + CHECK_EQUAL_WITH_RETURN_VOID(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_VOID(wrapper != nullptr); + ASSERT_MATCH_ELSE_RETURN_VOID(wrapper->GetObject() != nullptr); + ctxt->object = wrapper->GetObject(); + }; + ctxt->GetCbInfo(env, info, getCbOpe); + auto output = [env, ctxt](napi_value &result) { + if (ctxt->status == napi_ok) { + ctxt->status = napi_new_instance(env, + JSDistributedObject::GetSaveResultCons(env, ctxt->object->GetSessionId(), + ctxt->version, ctxt->deviceId), 0, nullptr, &result); + CHECK_STATUS_RETURN_VOID(ctxt, "output failed!"); + } + }; + return NapiQueue::AsyncWork( + env, ctxt, std::string(__FUNCTION__), + [ctxt]() { + LOG_INFO("start"); + if (ctxt->object == nullptr) { + LOG_ERROR("object is null"); + ctxt->status = napi_invalid_arg; + ctxt->error = std::string("object is null"); + return; + } + uint32_t status = ctxt->object->Save(ctxt->deviceId); + if (status != SUCCESS) { + LOG_ERROR("Save failed, status = %{public}d", status); + ctxt->status = napi_invalid_arg; + ctxt->error = std::string("operation failed"); + return; + } + ctxt->status = napi_ok; + LOG_INFO("end"); + }, + output); +} + +// revokeSave(callback?:AsyncCallback): void; +// revokeSave(): Promise; +napi_value JSDistributedObject::JSRevokeSave(napi_env env, napi_callback_info info) +{ + LOG_DEBUG("JSRevokeSave()"); + struct RevokeSaveContext : public ContextBase { + DistributedObject *object; + }; + auto ctxt = std::make_shared(); + std::function getCbOpe = [env, ctxt](size_t argc, napi_value *argv) { + JSObjectWrapper *wrapper = nullptr; + napi_status status = napi_unwrap(env, ctxt->self, (void **)&wrapper); + CHECK_EQUAL_WITH_RETURN_VOID(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_VOID(wrapper != nullptr); + ASSERT_MATCH_ELSE_RETURN_VOID(wrapper->GetObject() != nullptr); + ctxt->object = wrapper->GetObject(); + }; + ctxt->GetCbInfo(env, info, getCbOpe); + auto output = [env, ctxt](napi_value &result) { + if (ctxt->status == napi_ok) { + ctxt->status = napi_new_instance(env, + JSDistributedObject::GetRevokeSaveResultCons(env, ctxt->object->GetSessionId()), 0, nullptr, &result); + CHECK_STATUS_RETURN_VOID(ctxt, "output failed!"); + } + }; + return NapiQueue::AsyncWork( + env, ctxt, std::string(__FUNCTION__), + [ctxt]() { + LOG_INFO("start"); + if (ctxt->object == nullptr) { + LOG_ERROR("object is null"); + ctxt->status = napi_invalid_arg; + ctxt->error = std::string("object is null"); + return; + } + uint32_t status = ctxt->object->RevokeSave(); + if (status != SUCCESS) { + LOG_ERROR("Save failed, status = %{public}d", status); + ctxt->status = napi_invalid_arg; + ctxt->error = std::string("operation failed"); + return; + } + ctxt->status = napi_ok; + LOG_INFO("end"); + }, + output); +} + +napi_value JSDistributedObject::GetSaveResultCons( + napi_env env, std::string &sessionId, double version, std::string deviceId) +{ + const char *objectName = "SaveResult"; + napi_value napiSessionId, napiVersion, napiDeviceId; + napi_value result; + + napi_status status = JSUtil::SetValue(env, sessionId, napiSessionId); + ASSERT_MATCH_ELSE_RETURN_NULL(status == napi_ok); + status = JSUtil::SetValue(env, version, napiVersion); + ASSERT_MATCH_ELSE_RETURN_NULL(status == napi_ok); + status = JSUtil::SetValue(env, deviceId, napiDeviceId); + ASSERT_MATCH_ELSE_RETURN_NULL(status == napi_ok); + napi_property_descriptor desc[] = { + DECLARE_NAPI_PROPERTY("sessionId", napiSessionId), + DECLARE_NAPI_PROPERTY("version", napiVersion), + DECLARE_NAPI_PROPERTY("deviceId", napiDeviceId) + }; + + status = napi_define_class(env, objectName, strlen(objectName), JSDistributedObject::JSConstructor, nullptr, + sizeof(desc) / sizeof(desc[0]), desc, &result); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + return result; +} + +napi_value JSDistributedObject::GetRevokeSaveResultCons(napi_env env, std::string &sessionId) +{ + const char *objectName = "RevokeSaveResult"; + napi_value napiSessionId; + napi_value result; + + napi_status status = JSUtil::SetValue(env, sessionId, napiSessionId); + ASSERT_MATCH_ELSE_RETURN_NULL(status == napi_ok); + napi_property_descriptor desc[] = { + DECLARE_NAPI_PROPERTY("sessionId", napiSessionId) + }; + + status = napi_define_class(env, objectName, strlen(objectName), JSDistributedObject::JSConstructor, nullptr, + sizeof(desc) / sizeof(desc[0]), desc, &result); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + return result; +} +} // namespace OHOS::ObjectStore \ No newline at end of file diff --git a/data_object/frameworks/jskitsimpl/src/adaptor/js_distributedobjectstore.cpp b/data_object/frameworks/jskitsimpl/src/adaptor/js_distributedobjectstore.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7b21bd39f50a6d4c45f50ac8306945a7db62246b --- /dev/null +++ b/data_object/frameworks/jskitsimpl/src/adaptor/js_distributedobjectstore.cpp @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_distributedobjectstore.h" + +#include + +#include "ability_context.h" +#include "accesstoken_kit.h" +#include "application_context.h" +#include "distributed_objectstore.h" +#include "js_common.h" +#include "js_distributedobject.h" +#include "js_object_wrapper.h" +#include "js_util.h" +#include "logger.h" +#include "objectstore_errors.h" + +namespace OHOS::ObjectStore { +constexpr size_t TYPE_SIZE = 10; +const std::string DISTRIBUTED_DATASYNC = "ohos.permission.DISTRIBUTED_DATASYNC"; +static std::map> g_statusCallBacks; +static std::map> g_changeCallBacks; + +void JSDistributedObjectStore::AddCallback(napi_env env, std::map> &callbacks, + const std::string &objectId, napi_value callback) +{ + LOG_INFO("add callback %{public}s", objectId.c_str()); + napi_ref ref = nullptr; + napi_status status = napi_create_reference(env, callback, 1, &ref); + CHECK_EQUAL_WITH_RETURN_VOID(status, napi_ok); + if (callbacks.count(objectId) != 0) { + auto lists = callbacks.at(objectId); + lists.push_back(ref); + callbacks.insert_or_assign(objectId, lists); + } else { + std::list lists = { ref }; + callbacks.insert_or_assign(objectId, lists); + } +} +void JSDistributedObjectStore::DelCallback(napi_env env, std::map> &callbacks, + const std::string &sessionId, napi_value callback) +{ + LOG_INFO("del callback %{public}s", sessionId.c_str()); + napi_status status; + if (callback == nullptr) { + if (callbacks.count(sessionId) != 0) { + for (auto ref : callbacks.at(sessionId)) { + status = napi_delete_reference(env, ref); + CHECK_EQUAL_WITH_RETURN_VOID(status, napi_ok); + } + callbacks.erase(sessionId); + } + return; + } + napi_value callbackTmp; + if (callbacks.count(sessionId) != 0) { + auto lists = callbacks.at(sessionId); + for (auto iter = lists.begin(); iter != lists.end();) { + status = napi_get_reference_value(env, *iter, &callbackTmp); + CHECK_EQUAL_WITH_RETURN_VOID(status, napi_ok); + bool isEquals = false; + napi_strict_equals(env, callbackTmp, callback, &isEquals); + if (isEquals) { + napi_delete_reference(env, *iter); + iter = lists.erase(iter); + } else { + iter++; + } + } + if (lists.empty()) { + callbacks.erase(sessionId); + } else { + callbacks.insert_or_assign(sessionId, lists); + } + } +} +napi_value JSDistributedObjectStore::NewDistributedObject( + napi_env env, DistributedObjectStore *objectStore, DistributedObject *object, const std::string &objectId) +{ + napi_value result; + napi_status status = napi_new_instance(env, JSDistributedObject::GetCons(env), 0, nullptr, &result); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + JSObjectWrapper *objectWrapper = new JSObjectWrapper(objectStore, object); + status = napi_wrap( + env, result, objectWrapper, + [](napi_env env, void *data, void *hint) { + if (data == nullptr) { + LOG_WARN("objectWrapper is nullptr."); + return; + } + auto objectWrapper = (JSObjectWrapper *)data; + if (objectWrapper->GetObject() == nullptr) { + delete objectWrapper; + return; + } + LOG_INFO("start delete object"); + DistributedObjectStore::GetInstance(JSDistributedObjectStore::GetBundleName(env)) + ->DeleteObject(objectWrapper->GetObject()->GetSessionId()); + delete objectWrapper; + }, + nullptr, nullptr); + RestoreWatchers(env, objectWrapper, objectId); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + return result; +} + +// function createObjectSync(sessionId: string, objectId:string): DistributedObject; +napi_value JSDistributedObjectStore::JSCreateObjectSync(napi_env env, napi_callback_info info) +{ + if (IsSandBox()) { + LOG_WARN("entering sandbox app."); + return nullptr; + } + LOG_INFO("start JSCreateObjectSync"); + if (!JSDistributedObjectStore::CheckSyncPermission()) { + LOG_INFO("no permission ohos.permission.DISTRIBUTED_DATASYNC"); + return nullptr; + } + size_t requireArgc = 2; + size_t argc = 2; + napi_value argv[2] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + std::string sessionId; + std::string objectId; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(argc >= requireArgc); + napi_valuetype valueType = napi_undefined; + status = napi_typeof(env, argv[0], &valueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + CHECK_EQUAL_WITH_RETURN_NULL(valueType, napi_string) + status = JSUtil::GetValue(env, argv[0], sessionId); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + status = napi_typeof(env, argv[1], &valueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + CHECK_EQUAL_WITH_RETURN_NULL(valueType, napi_string) + status = JSUtil::GetValue(env, argv[1], objectId); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + DistributedObjectStore *objectInfo = + DistributedObjectStore::GetInstance(JSDistributedObjectStore::GetBundleName(env)); + ASSERT_MATCH_ELSE_RETURN_NULL(objectInfo != nullptr); + DistributedObject *object = objectInfo->CreateObject(sessionId); + ASSERT_MATCH_ELSE_RETURN_NULL(object != nullptr); + return NewDistributedObject(env, objectInfo, object, objectId); +} + +// function destroyObjectSync(object: DistributedObject): number; +napi_value JSDistributedObjectStore::JSDestroyObjectSync(napi_env env, napi_callback_info info) +{ + LOG_INFO("start"); + size_t requireArgc = 1; + size_t argc = 1; + napi_value argv[1] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + std::string sessionId; + std::string bundleName; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(argc >= requireArgc); + + JSObjectWrapper *objectWrapper = nullptr; + status = napi_unwrap(env, argv[0], (void **)&objectWrapper); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(objectWrapper != nullptr); + DistributedObjectStore *objectInfo = + DistributedObjectStore::GetInstance(JSDistributedObjectStore::GetBundleName(env)); + ASSERT_MATCH_ELSE_RETURN_NULL(objectInfo != nullptr && objectWrapper->GetObject() != nullptr); + objectWrapper->DeleteWatch(env, CHANGE); + objectWrapper->DeleteWatch(env, STATUS); + objectInfo->DeleteObject(objectWrapper->GetObject()->GetSessionId()); + objectWrapper->DestroyObject(); + return nullptr; +} + +// function on(type: 'change', object: DistributedObject, callback: Callback): void; +// function on(type: 'status', object: DistributedObject, callback: Callback): void; +napi_value JSDistributedObjectStore::JSOn(napi_env env, napi_callback_info info) +{ + LOG_INFO("start"); + size_t requireArgc = 3; + size_t argc = 3; + napi_value argv[3] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(argc >= requireArgc); + + char type[TYPE_SIZE] = { 0 }; + size_t eventTypeLen = 0; + napi_valuetype eventValueType = napi_undefined; + status = napi_typeof(env, argv[0], &eventValueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(eventValueType == napi_string); + status = napi_get_value_string_utf8(env, argv[0], type, TYPE_SIZE, &eventTypeLen); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + + napi_valuetype objectType = napi_undefined; + status = napi_typeof(env, argv[1], &objectType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(objectType == napi_object); + + napi_valuetype callbackType = napi_undefined; + status = napi_typeof(env, argv[2], &callbackType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(callbackType == napi_function); + + JSObjectWrapper *wrapper = nullptr; + status = napi_unwrap(env, argv[1], (void **)&wrapper); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(wrapper != nullptr); + wrapper->AddWatch(env, type, argv[2]); + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; +} + +// function off(type: 'change', object: DistributedObject, callback?: Callback): void; +// function off(type: 'status', object: DistributedObject, callback?: Callback): void; +napi_value JSDistributedObjectStore::JSOff(napi_env env, napi_callback_info info) +{ + LOG_INFO("start"); + size_t requireArgc = 2; + size_t argc = 3; + napi_value argv[3] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + char type[TYPE_SIZE] = { 0 }; + size_t typeLen = 0; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(argc >= requireArgc); + for (size_t i = 0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + status = napi_typeof(env, argv[i], &valueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + + if (i == 0 && valueType == napi_string) { + status = napi_get_value_string_utf8(env, argv[i], type, TYPE_SIZE, &typeLen); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + } else if (i == 1 && valueType == napi_object) { + continue; + } else if (i == 2 && (valueType == napi_function || valueType == napi_undefined)) { + continue; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + JSObjectWrapper *wrapper = nullptr; + status = napi_unwrap(env, argv[1], (void **)&wrapper); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(wrapper != nullptr); + if (argc == requireArgc) { + LOG_INFO("delete all"); + wrapper->DeleteWatch(env, type); + } else { + LOG_INFO("delete"); + wrapper->DeleteWatch(env, type, argv[2]); + } + + napi_value result = nullptr; + status = napi_get_undefined(env, &result); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + return result; +} + +std::string JSDistributedObjectStore::GetBundleName(napi_env env) +{ + static std::string bundleName; + if (bundleName.empty()) { + bundleName = AbilityRuntime::Context::GetApplicationContext()->GetBundleName(); + } + return bundleName; +} + +void JSDistributedObjectStore::RestoreWatchers(napi_env env, JSObjectWrapper *wrapper, const std::string &objectId) +{ + napi_status status; + napi_value callbackValue; + LOG_DEBUG("start restore %{public}s", objectId.c_str()); + if (g_changeCallBacks.count(objectId) != 0) { + LOG_INFO("restore change on %{public}s", objectId.c_str()); + for (auto callback : g_changeCallBacks.at(objectId)) { + status = napi_get_reference_value(env, callback, &callbackValue); + if (status != napi_ok) { + LOG_ERROR("error! %{public}d", status); + continue; + } + wrapper->AddWatch(env, CHANGE, callbackValue); + } + } else { + LOG_INFO("no callback %{public}s", objectId.c_str()); + } + if (g_statusCallBacks.count(objectId) != 0) { + LOG_INFO("restore status on %{public}s", objectId.c_str()); + for (auto callback : g_statusCallBacks.at(objectId)) { + status = napi_get_reference_value(env, callback, &callbackValue); + if (status != napi_ok) { + LOG_ERROR("error! %{public}d", status); + continue; + } + wrapper->AddWatch(env, STATUS, callbackValue); + } + } else { + LOG_INFO("no status callback %{public}s", objectId.c_str()); + } +} + +// function recordCallback(type: 'change', objectId: string, callback: Callback): void; +// function recordCallback(type: 'status', objectId: string, callback: Callback): void; +napi_value JSDistributedObjectStore::JSRecordCallback(napi_env env, napi_callback_info info) +{ + LOG_INFO("start"); + size_t requireArgc = 3; + size_t argc = 3; + napi_value argv[3] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(argc >= requireArgc); + + char type[TYPE_SIZE] = { 0 }; + size_t eventTypeLen = 0; + napi_valuetype valueType = napi_undefined; + status = napi_typeof(env, argv[0], &valueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(valueType == napi_string); + status = napi_get_value_string_utf8(env, argv[0], type, TYPE_SIZE, &eventTypeLen); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + + std::string objectId; + status = napi_typeof(env, argv[1], &valueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + CHECK_EQUAL_WITH_RETURN_NULL(valueType, napi_string) + status = JSUtil::GetValue(env, argv[1], objectId); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + + napi_valuetype callbackType = napi_undefined; + status = napi_typeof(env, argv[2], &callbackType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(callbackType == napi_function); + + if (!strcmp(CHANGE, type)) { + AddCallback(env, g_changeCallBacks, objectId, argv[2]); + } else if (!strcmp(STATUS, type)) { + AddCallback(env, g_statusCallBacks, objectId, argv[2]); + } + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; +} + +// function deleteCallback(type: 'change', objectId: string, callback?: Callback): void; +// function deleteCallback(type: 'status', objectId: string, callback?: Callback): void; +napi_value JSDistributedObjectStore::JSDeleteCallback(napi_env env, napi_callback_info info) +{ + LOG_INFO("start"); + size_t requireArgc = 3; + size_t argc = 3; + napi_value argv[3] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(argc >= requireArgc); + + char type[TYPE_SIZE] = { 0 }; + size_t eventTypeLen = 0; + napi_valuetype valueType = napi_undefined; + status = napi_typeof(env, argv[0], &valueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(valueType == napi_string); + status = napi_get_value_string_utf8(env, argv[0], type, TYPE_SIZE, &eventTypeLen); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + + std::string objectId; + status = napi_typeof(env, argv[1], &valueType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + CHECK_EQUAL_WITH_RETURN_NULL(valueType, napi_string) + status = JSUtil::GetValue(env, argv[1], objectId); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + + if (argc == 2) { + if (!strcmp(CHANGE, type)) { + DelCallback(env, g_changeCallBacks, objectId); + } else if (!strcmp(STATUS, type)) { + DelCallback(env, g_statusCallBacks, objectId); + } + } else { + napi_valuetype callbackType = napi_undefined; + status = napi_typeof(env, argv[2], &callbackType); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + ASSERT_MATCH_ELSE_RETURN_NULL(callbackType == napi_function); + if (!strcmp(CHANGE, type)) { + DelCallback(env, g_changeCallBacks, objectId, argv[2]); + } else if (!strcmp(STATUS, type)) { + DelCallback(env, g_statusCallBacks, objectId, argv[2]); + } + } + + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; +} + +bool JSDistributedObjectStore::CheckSyncPermission() +{ + int32_t ret = Security::AccessToken::AccessTokenKit::VerifyAccessToken( + AbilityRuntime::Context::GetApplicationContext()->GetApplicationInfo()->accessTokenId, DISTRIBUTED_DATASYNC); + if (ret == Security::AccessToken::PermissionState::PERMISSION_DENIED) { + LOG_ERROR("VerifyPermission %{public}d: PERMISSION_DENIED", + AbilityRuntime::Context::GetApplicationContext()->GetApplicationInfo()->accessTokenId); + return false; + } + return true; +} + +// don't create distributed data object while this application is sandbox +bool JSDistributedObjectStore::IsSandBox() +{ + int32_t dlpFlag = Security::AccessToken::AccessTokenKit::GetHapDlpFlag( + AbilityRuntime::Context::GetApplicationContext()->GetApplicationInfo()->accessTokenId); + if (dlpFlag != 0) { + return true; + } + return false; +} +} // namespace OHOS::ObjectStore diff --git a/data_object/frameworks/jskitsimpl/src/adaptor/js_module_init.cpp b/data_object/frameworks/jskitsimpl/src/adaptor/js_module_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..832177db8f5b6e1afaf16315e5b7a7386127f2f7 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/src/adaptor/js_module_init.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_common.h" +#include "js_distributedobjectstore.h" +#include "logger.h" +#include "notifier_impl.h" + +using namespace OHOS::ObjectStore; + +extern const char _binary_distributed_data_object_js_start[]; +extern const char _binary_distributed_data_object_js_end[]; + +extern const char _binary_distributed_data_object_abc_start[]; +extern const char _binary_distributed_data_object_abc_end[]; + +static napi_value DistributedDataObjectExport(napi_env env, napi_value exports) +{ + napi_status status; + static napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("createObjectSync", JSDistributedObjectStore::JSCreateObjectSync), + DECLARE_NAPI_FUNCTION("destroyObjectSync", JSDistributedObjectStore::JSDestroyObjectSync), + DECLARE_NAPI_FUNCTION("on", JSDistributedObjectStore::JSOn), + DECLARE_NAPI_FUNCTION("off", JSDistributedObjectStore::JSOff), + DECLARE_NAPI_FUNCTION("recordCallback", JSDistributedObjectStore::JSRecordCallback), + DECLARE_NAPI_FUNCTION("deleteCallback", JSDistributedObjectStore::JSDeleteCallback), + }; + + status = napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + CHECK_EQUAL_WITH_RETURN_NULL(status, napi_ok); + return exports; +} + +// storage module define +static napi_module storageModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = DistributedDataObjectExport, + .nm_modname = "data.distributedDataObject", + .nm_priv = ((void *)0), + .reserved = { 0 }, +}; + +// distributed_data_object.js +extern "C" __attribute__((visibility("default"))) void NAPI_data_distributedDataObject_GetJSCode( + const char **buf, int *bufLen) +{ + if (buf != nullptr) { + *buf = _binary_distributed_data_object_js_start; + } + + if (bufLen != nullptr) { + *bufLen = _binary_distributed_data_object_js_end - _binary_distributed_data_object_js_start; + } +} + +extern "C" __attribute__((visibility("default"))) void NAPI_data_distributedDataObject_GetABCCode( + const char **buf, int *bufLen) +{ + if (buf != nullptr) { + *buf = _binary_distributed_data_object_abc_start; + } + + if (bufLen != nullptr) { + *bufLen = _binary_distributed_data_object_abc_end - _binary_distributed_data_object_abc_start; + } +} + +// distributeddataobject module register +static __attribute__((constructor)) void RegisterModule() +{ + napi_module_register(&storageModule); +} \ No newline at end of file diff --git a/data_object/frameworks/jskitsimpl/src/adaptor/js_object_wrapper.cpp b/data_object/frameworks/jskitsimpl/src/adaptor/js_object_wrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef950ea13178be64a06e911d661229d5be19dc68 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/src/adaptor/js_object_wrapper.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_object_wrapper.h" + +#include "logger.h" +namespace OHOS::ObjectStore { +JSObjectWrapper::JSObjectWrapper(DistributedObjectStore *objectStore, DistributedObject *object) + : objectStore_(objectStore), object_(object) +{ +} + +JSObjectWrapper::~JSObjectWrapper() +{ + LOG_INFO("JSObjectWrapper::~JSObjectWrapper"); + std::unique_lock cacheLock(watchMutex_); + if (watcher_ != nullptr) { + watcher_ = nullptr; + } + LOG_INFO("JSObjectWrapper::~JSObjectWrapper end"); +} + +DistributedObject *JSObjectWrapper::GetObject() +{ + return object_; +} + +bool JSObjectWrapper::AddWatch(napi_env env, const char *type, napi_value handler) +{ + std::unique_lock cacheLock(watchMutex_); + if (watcher_ == nullptr) { + watcher_ = std::make_unique(env, objectStore_, object_); + if (watcher_ == nullptr) { + LOG_ERROR("JSObjectWrapper::new JSWatcher fail"); + return false; + } + } + return watcher_->On(type, handler); +} + +void JSObjectWrapper::DeleteWatch(napi_env env, const char *type, napi_value handler) +{ + std::unique_lock cacheLock(watchMutex_); + if (watcher_ != nullptr) { + watcher_->Off(type, handler); + LOG_INFO("JSObjectWrapper::DeleteWatch %{public}s", type); + } else { + LOG_ERROR("JSObjectWrapper::DeleteWatch watcher_ is null"); + } +} + +bool JSObjectWrapper::isUndefined(char *value) +{ + std::string tmpStr = value; + auto it = std::find(undefinedProperties.begin(), undefinedProperties.end(), tmpStr); + if (it == undefinedProperties.end()) { + return false; + } + return true; +} + +void JSObjectWrapper::AddUndefined(char *value) +{ + std::string tmpStr = value; + if (std::find(undefinedProperties.begin(), undefinedProperties.end(), tmpStr) == undefinedProperties.end()) { + undefinedProperties.push_back(tmpStr); + } +} + +void JSObjectWrapper::DeleteUndefined(char *value) +{ + std::string tmpStr = value; + auto it = std::find(undefinedProperties.begin(), undefinedProperties.end(), tmpStr); + if (it != undefinedProperties.end()) { + undefinedProperties.erase(it); + } +} + +void JSObjectWrapper::DestroyObject() +{ + object_ = nullptr; +} +} // namespace OHOS::ObjectStore \ No newline at end of file diff --git a/data_object/frameworks/jskitsimpl/src/adaptor/js_watcher.cpp b/data_object/frameworks/jskitsimpl/src/adaptor/js_watcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c1d4fec46174175dea17b1002245edbfd367a4c --- /dev/null +++ b/data_object/frameworks/jskitsimpl/src/adaptor/js_watcher.cpp @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_watcher.h" + +#include + +#include "js_common.h" +#include "js_util.h" +#include "logger.h" +#include "notifier_impl.h" +#include "objectstore_errors.h" + +namespace OHOS::ObjectStore { +JSWatcher::JSWatcher(const napi_env env, DistributedObjectStore *objectStore, DistributedObject *object) + : UvQueue(env), env_(env) +{ + changeEventListener_ = new ChangeEventListener(this, objectStore, object); + statusEventListener_ = new StatusEventListener(this, object->GetSessionId()); +} + +JSWatcher::~JSWatcher() +{ + delete changeEventListener_; + delete statusEventListener_; + changeEventListener_ = nullptr; + statusEventListener_ = nullptr; +} + +bool JSWatcher::On(const char *type, napi_value handler) +{ + EventListener *listener = Find(type); + if (listener == nullptr) { + LOG_ERROR("error type %{public}s", type); + return false; + } + return listener->Add(env_, handler); +} + +void JSWatcher::Off(const char *type, napi_value handler) +{ + EventListener *listener = Find(type); + if (listener == nullptr) { + LOG_ERROR("error type %{public}s", type); + return; + } + if (handler == nullptr) { + listener->Clear(env_); + } else { + listener->Del(env_, handler); + } +} +void JSWatcher::ProcessChange(napi_env env, std::list &args) +{ + constexpr static int8_t ARGV_SIZE = 2; + napi_value callback = nullptr; + napi_value global = nullptr; + napi_value param[ARGV_SIZE]; + napi_value result; + napi_status status = napi_get_global(env, &global); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + for (auto item : args) { + ChangeArgs *changeArgs = static_cast(item); + status = napi_get_reference_value(env, changeArgs->callback_, &callback); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + status = JSUtil::SetValue(env, changeArgs->sessionId_, param[0]); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + JSUtil::SetValue(env, changeArgs->changeData_, param[1]); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + LOG_INFO("start %{public}s, %{public}zu", changeArgs->sessionId_.c_str(), changeArgs->changeData_.size()); + status = napi_call_function(env, global, callback, ARGV_SIZE, param, &result); + LOG_INFO("end %{public}s, %{public}zu", changeArgs->sessionId_.c_str(), changeArgs->changeData_.size()); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + } +ERROR: + for (auto item : args) { + ChangeArgs *changeArgs = static_cast(item); + delete changeArgs; + } + args.clear(); +} +void JSWatcher::Emit(const char *type, const std::string &sessionId, const std::vector &changeData) +{ + if (changeData.empty()) { + LOG_ERROR("empty change"); + return; + } + LOG_INFO("start %{public}s, %{public}s", sessionId.c_str(), changeData.at(0).c_str()); + EventListener *listener = Find(type); + if (listener == nullptr) { + LOG_ERROR("error type %{public}s", type); + return; + } + + for (EventHandler *handler = listener->handlers_; handler != nullptr; handler = handler->next) { + ChangeArgs *changeArgs = new ChangeArgs(handler->callbackRef, sessionId, changeData); + CallFunction(ProcessChange, changeArgs); + } +} + +EventListener *JSWatcher::Find(const char *type) +{ + if (!strcmp(CHANGE, type)) { + return changeEventListener_; + } + if (!strcmp(STATUS, type)) { + return statusEventListener_; + } + return nullptr; +} + +void JSWatcher::ProcessStatus(napi_env env, std::list &args) +{ + constexpr static int8_t ARGV_SIZE = 3; + napi_value callback = nullptr; + napi_value global = nullptr; + napi_value param[ARGV_SIZE]; + napi_value result; + napi_status status = napi_get_global(env, &global); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + for (auto item : args) { + StatusArgs *statusArgs = static_cast(item); + status = napi_get_reference_value(env, statusArgs->callback_, &callback); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + status = JSUtil::SetValue(env, statusArgs->sessionId_, param[0]); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + JSUtil::SetValue(env, statusArgs->networkId_, param[1]); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + JSUtil::SetValue(env, statusArgs->status_, param[2]); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + LOG_INFO("start %{public}s, %{public}s, %{public}s", statusArgs->sessionId_.c_str(), + statusArgs->networkId_.c_str(), statusArgs->status_.c_str()); + status = napi_call_function(env, global, callback, ARGV_SIZE, param, &result); + LOG_INFO("end %{public}s, %{public}s, %{public}s", statusArgs->sessionId_.c_str(), + statusArgs->networkId_.c_str(), statusArgs->status_.c_str()); + ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok); + } +ERROR: + LOG_DEBUG("do clear"); + for (auto item : args) { + StatusArgs *statusArgs = static_cast(item); + delete statusArgs; + } + args.clear(); +} + +void JSWatcher::Emit( + const char *type, const std::string &sessionId, const std::string &networkId, const std::string &status) +{ + if (sessionId.empty() || networkId.empty()) { + LOG_ERROR("empty %{public}s %{public}s", sessionId.c_str(), networkId.c_str()); + return; + } + LOG_ERROR("status change %{public}s %{public}s", sessionId.c_str(), networkId.c_str()); + EventListener *listener = Find(type); + if (listener == nullptr) { + LOG_ERROR("error type %{public}s", type); + return; + } + + for (EventHandler *handler = listener->handlers_; handler != nullptr; handler = handler->next) { + StatusArgs *changeArgs = new StatusArgs(handler->callbackRef, sessionId, networkId, status); + CallFunction(ProcessStatus, changeArgs); + } + return; +} + +EventHandler *EventListener::Find(napi_env env, napi_value handler) +{ + EventHandler *result = nullptr; + for (EventHandler *i = handlers_; i != nullptr; i = i->next) { + napi_value callback = nullptr; + napi_get_reference_value(env, i->callbackRef, &callback); + bool isEquals = false; + napi_strict_equals(env, handler, callback, &isEquals); + if (isEquals) { + result = i; + } + } + return result; +} + +void EventListener::Clear(napi_env env) +{ + for (EventHandler *i = handlers_; i != nullptr; i = handlers_) { + handlers_ = i->next; + napi_delete_reference(env, i->callbackRef); + delete i; + } +} + +bool EventListener::Del(napi_env env, napi_value handler) +{ + EventHandler *temp = nullptr; + napi_status status; + for (EventHandler *i = handlers_; i != nullptr;) { + napi_value callback = nullptr; + status = napi_get_reference_value(env, i->callbackRef, &callback); + if (status != napi_ok) { + LOG_ERROR("error! %{public}d", status); + return false; + } + bool isEquals = false; + napi_strict_equals(env, handler, callback, &isEquals); + if (isEquals) { + EventHandler *delData = i; + i = i->next; + if (temp == nullptr) { + handlers_ = delData->next; + } else { + temp->next = delData->next; + } + napi_delete_reference(env, delData->callbackRef); + delete delData; + } else { + temp = i; + i = i->next; + } + } + return handlers_ == nullptr; +} + +bool EventListener::Add(napi_env env, napi_value handler) +{ + if (Find(env, handler) != nullptr) { + LOG_ERROR("has added,return"); + return false; + } + + if (handlers_ == nullptr) { + handlers_ = new EventHandler(); + handlers_->next = nullptr; + } else { + auto temp = new EventHandler(); + temp->next = handlers_; + handlers_ = temp; + } + napi_create_reference(env, handler, 1, &handlers_->callbackRef); + return true; +} + +void WatcherImpl::OnChanged(const std::string &sessionid, const std::vector &changedData) +{ + if (watcher_ == nullptr) { + LOG_ERROR("watcher_ is null"); + return; + } + watcher_->Emit(CHANGE, sessionid, changedData); +} + +WatcherImpl::~WatcherImpl() +{ + LOG_ERROR("destroy"); + watcher_ = nullptr; +} + +bool ChangeEventListener::Add(napi_env env, napi_value handler) +{ + if (!isWatched_ && object_ != nullptr) { + std::shared_ptr watcher = std::make_shared(watcher_); + if (watcher == nullptr) { + LOG_ERROR("new %{public}s error", object_->GetSessionId().c_str()); + return false; + } + uint32_t ret = objectStore_->Watch(object_, watcher); + if (ret != SUCCESS) { + LOG_ERROR("Watch %{public}s error", object_->GetSessionId().c_str()); + } else { + LOG_INFO("Watch %{public}s success", object_->GetSessionId().c_str()); + isWatched_ = true; + } + } + return EventListener::Add(env, handler); +} + +bool ChangeEventListener::Del(napi_env env, napi_value handler) +{ + bool isEmpty = EventListener::Del(env, handler); + if (isEmpty && isWatched_ && object_ != nullptr) { + uint32_t ret = objectStore_->UnWatch(object_); + if (ret != SUCCESS) { + LOG_ERROR("UnWatch %{public}s error", object_->GetSessionId().c_str()); + } else { + LOG_INFO("UnWatch %{public}s success", object_->GetSessionId().c_str()); + isWatched_ = false; + } + } + return isEmpty; +} + +void ChangeEventListener::Clear(napi_env env) +{ + EventListener::Clear(env); + if (isWatched_ && object_ != nullptr) { + uint32_t ret = objectStore_->UnWatch(object_); + if (ret != SUCCESS) { + LOG_ERROR("UnWatch %{public}s error", object_->GetSessionId().c_str()); + } else { + LOG_INFO("UnWatch %{public}s success", object_->GetSessionId().c_str()); + isWatched_ = false; + } + } +} + +ChangeEventListener::ChangeEventListener( + JSWatcher *watcher, DistributedObjectStore *objectStore, DistributedObject *object) + : objectStore_(objectStore), object_(object), watcher_(watcher) +{ +} + +bool StatusEventListener::Add(napi_env env, napi_value handler) +{ + LOG_INFO("Add status watch %{public}s", sessionId_.c_str()); + NotifierImpl::GetInstance()->AddWatcher(sessionId_, watcher_); + return EventListener::Add(env, handler); +} + +bool StatusEventListener::Del(napi_env env, napi_value handler) +{ + if (EventListener::Del(env, handler)) { + LOG_INFO("Del status watch %{public}s", sessionId_.c_str()); + NotifierImpl::GetInstance()->DelWatcher(sessionId_); + return true; + } + return false; +} + +void StatusEventListener::Clear(napi_env env) +{ + NotifierImpl::GetInstance()->DelWatcher(sessionId_); + EventListener::Clear(env); +} + +StatusEventListener::StatusEventListener(JSWatcher *watcher, const std::string &sessionId) + : watcher_(watcher), sessionId_(sessionId) +{ +} + +JSWatcher::ChangeArgs::ChangeArgs( + const napi_ref callback, const std::string &sessionId, const std::vector &changeData) + : callback_(callback), sessionId_(sessionId), changeData_(changeData) +{ +} + +JSWatcher::StatusArgs::StatusArgs( + const napi_ref callback, const std::string &sessionId, const std::string &networkId, const std::string &status) + : callback_(callback), sessionId_(sessionId), networkId_(networkId), status_(status) +{ +} +} // namespace OHOS::ObjectStore diff --git a/data_object/frameworks/jskitsimpl/src/adaptor/notifier_impl.cpp b/data_object/frameworks/jskitsimpl/src/adaptor/notifier_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..394a45532e9ae59b55dba04e8cd04b1dcb21a50b --- /dev/null +++ b/data_object/frameworks/jskitsimpl/src/adaptor/notifier_impl.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "notifier_impl.h" + +#include "logger.h" +#include "objectstore_errors.h" + +namespace OHOS::ObjectStore { +std::shared_ptr NotifierImpl::GetInstance() +{ + static std::shared_ptr instance; + static std::mutex instanceLock; + if (instance == nullptr) { + std::lock_guard lockGuard(instanceLock); + if (instance == nullptr) { + instance = std::make_shared(); + if (instance == nullptr) { + LOG_ERROR("Failed to alloc NotifierImpl!"); + return nullptr; + } + uint32_t ret = DistributedObjectStore::GetInstance()->SetStatusNotifier(instance); + if (ret != SUCCESS) { + LOG_ERROR("SetStatusNotifier %{public}d error", ret); + } else { + LOG_INFO("SetStatusNotifier success"); + } + } + } + return instance; +} + +void NotifierImpl::AddWatcher(std::string &sessionId, JSWatcher *watcher) +{ + std::lock_guard lock(mutex_); + watchers_.insert_or_assign(sessionId, watcher); +} + +void NotifierImpl::DelWatcher(std::string &sessionId) +{ + std::lock_guard lock(mutex_); + watchers_.erase(sessionId); +} + +void NotifierImpl::OnChanged( + const std::string &sessionId, const std::string &networkId, const std::string &onlineStatus) +{ + LOG_INFO( + "status changed %{public}s %{public}s %{public}s", sessionId.c_str(), networkId.c_str(), onlineStatus.c_str()); + std::lock_guard lock(mutex_); + if (watchers_.count(sessionId) != 0) { + LOG_INFO( + "start emit %{public}s %{public}s %{public}s", sessionId.c_str(), networkId.c_str(), onlineStatus.c_str()); + watchers_.at(sessionId)->Emit("status", sessionId, networkId, onlineStatus); + LOG_INFO( + "end emit %{public}s %{public}s %{public}s", sessionId.c_str(), networkId.c_str(), onlineStatus.c_str()); + } +} +NotifierImpl::~NotifierImpl() +{ +} +} // namespace OHOS::ObjectStore diff --git a/data_object/frameworks/jskitsimpl/src/common/js_util.cpp b/data_object/frameworks/jskitsimpl/src/common/js_util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e43d3708e3d4564282bf9cedb5ab4121a31d9504 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/src/common/js_util.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "js_util.h" + +#include +#include + +#include "logger.h" + +namespace OHOS::ObjectStore { +constexpr int32_t STR_MAX_LENGTH = 4096; +constexpr size_t STR_TAIL_LENGTH = 1; + +/* napi_value <-> bool */ +napi_status JSUtil::GetValue(napi_env env, napi_value in, bool &out) +{ + LOG_DEBUG("napi_value <- bool"); + return napi_get_value_bool(env, in, &out); +} + +napi_status JSUtil::SetValue(napi_env env, const bool &in, napi_value &out) +{ + LOG_DEBUG("napi_value -> bool"); + return napi_get_boolean(env, in, &out); +} + +/* napi_value <-> double */ +napi_status JSUtil::GetValue(napi_env env, napi_value in, double &out) +{ + LOG_DEBUG("napi_value -> double"); + return napi_get_value_double(env, in, &out); +} + +napi_status JSUtil::SetValue(napi_env env, const double &in, napi_value &out) +{ + LOG_DEBUG("napi_value <- double"); + return napi_create_double(env, in, &out); +} + +/* napi_value <-> std::string */ +napi_status JSUtil::GetValue(napi_env env, napi_value in, std::string &out) +{ + size_t maxLen = STR_MAX_LENGTH; + napi_status status = napi_get_value_string_utf8(env, in, NULL, 0, &maxLen); + if (maxLen <= 0) { + GET_AND_THROW_LAST_ERROR(env); + return status; + } + char *buf = new (std::nothrow) char[maxLen + STR_TAIL_LENGTH]; + if (buf != nullptr) { + size_t len = 0; + status = napi_get_value_string_utf8(env, in, buf, maxLen + STR_TAIL_LENGTH, &len); + if (status != napi_ok) { + GET_AND_THROW_LAST_ERROR(env); + } + buf[len] = 0; + out = std::string(buf); + delete[] buf; + } else { + status = napi_generic_failure; + } + return status; +} + +napi_status JSUtil::SetValue(napi_env env, const std::string &in, napi_value &out) +{ + LOG_DEBUG("napi_value <- std::string %{public}d", (int)in.length()); + return napi_create_string_utf8(env, in.c_str(), in.size(), &out); +} + +/* napi_value <-> std::vector */ +napi_status JSUtil::GetValue(napi_env env, napi_value in, std::vector &out) +{ + LOG_DEBUG("napi_value -> std::vector"); + bool isArray = false; + napi_is_array(env, in, &isArray); + LOG_ERROR_RETURN(isArray, "not an array", napi_invalid_arg); + + uint32_t length = 0; + napi_status status = napi_get_array_length(env, in, &length); + LOG_ERROR_RETURN((status == napi_ok) && (length > 0), "get_array failed!", napi_invalid_arg); + for (uint32_t i = 0; i < length; ++i) { + napi_value item = nullptr; + status = napi_get_element(env, in, i, &item); + LOG_ERROR_RETURN((item != nullptr) && (status == napi_ok), "no element", napi_invalid_arg); + std::string value; + status = GetValue(env, item, value); + LOG_ERROR_RETURN(status == napi_ok, "not a string", napi_invalid_arg); + out.push_back(value); + } + return status; +} + +napi_status JSUtil::SetValue(napi_env env, const std::vector &in, napi_value &out) +{ + LOG_DEBUG("napi_value <- std::vector"); + napi_status status = napi_create_array_with_length(env, in.size(), &out); + LOG_ERROR_RETURN(status == napi_ok, "create array failed!", status); + int index = 0; + for (auto &item : in) { + napi_value element = nullptr; + SetValue(env, item, element); + status = napi_set_element(env, out, index++, element); + LOG_ERROR_RETURN((status == napi_ok), "napi_set_element failed!", status); + } + return status; +} + +/* napi_value <-> std::vector */ +napi_status JSUtil::GetValue(napi_env env, napi_value in, std::vector &out) +{ + out.clear(); + LOG_DEBUG("napi_value -> std::vector "); + napi_typedarray_type type = napi_biguint64_array; + size_t length = 0; + napi_value buffer = nullptr; + size_t offset = 0; + void *data = nullptr; + napi_status status = napi_get_typedarray_info(env, in, &type, &length, &data, &buffer, &offset); + LOG_DEBUG("array type=%{public}d length=%{public}d offset=%{public}d status=%{public}d", (int)type, (int)length, + (int)offset, status); + LOG_ERROR_RETURN(status == napi_ok, "napi_get_typedarray_info failed!", napi_invalid_arg); + LOG_ERROR_RETURN(type == napi_uint8_array, "is not Uint8Array!", napi_invalid_arg); + LOG_ERROR_RETURN((length > 0) && (data != nullptr), "invalid data!", napi_invalid_arg); + out.assign((uint8_t *)data, ((uint8_t *)data) + length); + return status; +} + +napi_status JSUtil::SetValue(napi_env env, const std::vector &in, napi_value &out) +{ + LOG_DEBUG("napi_value <- std::vector "); + LOG_ERROR_RETURN(in.size() > 0, "invalid std::vector", napi_invalid_arg); + void *data = nullptr; + napi_value buffer = nullptr; + napi_status status = napi_create_arraybuffer(env, in.size(), &data, &buffer); + LOG_ERROR_RETURN((status == napi_ok), "create array buffer failed!", status); + + if (memcpy_s(data, in.size(), in.data(), in.size()) != EOK) { + LOG_ERROR("memcpy_s not EOK"); + return napi_invalid_arg; + } + status = napi_create_typedarray(env, napi_uint8_array, in.size(), buffer, 0, &out); + LOG_ERROR_RETURN((status == napi_ok), "napi_value <- std::vector invalid value", status); + return status; +} +} // namespace OHOS::ObjectStore diff --git a/data_object/frameworks/jskitsimpl/src/common/napi_queue.cpp b/data_object/frameworks/jskitsimpl/src/common/napi_queue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a20b8ee299f35efaad6a64cb72fdb68844c41637 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/src/common/napi_queue.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "napi_queue.h" +#include "logger.h" +#include "js_common.h" + +namespace OHOS::ObjectStore { +ContextBase::~ContextBase() +{ + LOG_DEBUG("no memory leak after callback or promise[resolved/rejected]"); + if (env != nullptr) { + if (work != nullptr) { + napi_delete_async_work(env, work); + } + if (callbackRef != nullptr) { + napi_delete_reference(env, callbackRef); + } + napi_delete_reference(env, selfRef); + env = nullptr; + } +} + +void ContextBase::GetCbInfo(napi_env envi, napi_callback_info info, NapiCbInfoParser parse, bool sync) +{ + env = envi; + size_t argc = ARGC_MAX; + napi_value argv[ARGC_MAX] = { nullptr }; + status = napi_get_cb_info(env, info, &argc, argv, &self, nullptr); + CHECK_STATUS_RETURN_VOID(this, "napi_get_cb_info failed!"); + CHECK_ARGS_RETURN_VOID(this, argc <= ARGC_MAX, "too many arguments!"); + CHECK_ARGS_RETURN_VOID(this, self != nullptr, "no JavaScript this argument!"); + napi_create_reference(env, self, 1, &selfRef); + status = napi_unwrap(env, self, &native); + CHECK_STATUS_RETURN_VOID(this, "self unwrap failed!"); + + if (!sync && (argc > 0)) { + // get the last arguments :: + size_t index = argc - 1; + napi_valuetype type = napi_undefined; + napi_status tyst = napi_typeof(env, argv[index], &type); + if ((tyst == napi_ok) && (type == napi_function)) { + status = napi_create_reference(env, argv[index], 1, &callbackRef); + CHECK_STATUS_RETURN_VOID(this, "ref callback failed!"); + argc = index; + LOG_DEBUG("async callback, no promise"); + } else { + LOG_DEBUG("no callback, async pormose"); + } + } + + if (parse) { + parse(argc, argv); + } else { + CHECK_ARGS_RETURN_VOID(this, argc == 0, "required no arguments!"); + } +} + +napi_value NapiQueue::AsyncWork(napi_env env, std::shared_ptr ctxt, const std::string& name, + NapiAsyncExecute execute, NapiAsyncComplete complete) +{ + LOG_DEBUG("name=%{public}s", name.c_str()); + ctxt->execute = std::move(execute); + ctxt->complete = std::move(complete); + + napi_value promise = nullptr; + if (ctxt->callbackRef == nullptr) { + napi_create_promise(ctxt->env, &ctxt->deferred, &promise); + LOG_DEBUG("create deferred promise"); + } else { + napi_get_undefined(ctxt->env, &promise); + } + + napi_value resource = nullptr; + napi_create_string_utf8(ctxt->env, name.c_str(), NAPI_AUTO_LENGTH, &resource); + napi_create_async_work( + ctxt->env, nullptr, resource, + [](napi_env env, void* data) { + ASSERT_MATCH_ELSE_RETURN_VOID(data != nullptr); + auto ctxt = reinterpret_cast(data); + LOG_DEBUG("napi_async_execute_callback ctxt->status=%{public}d", ctxt->status); + if (ctxt->execute && ctxt->status == napi_ok) { + ctxt->execute(); + } + }, + [](napi_env env, napi_status status, void* data) { + ASSERT_MATCH_ELSE_RETURN_VOID(data != nullptr); + auto ctxt = reinterpret_cast(data); + LOG_DEBUG("napi_async_complete_callback status=%{public}d, ctxt->status=%{public}d", status, ctxt->status); + if ((status != napi_ok) && (ctxt->status == napi_ok)) { + ctxt->status = status; + } + if ((ctxt->complete) && (status == napi_ok) && (ctxt->status == napi_ok)) { + ctxt->complete(ctxt->output); + } + GenerateOutput(ctxt); + }, + reinterpret_cast(ctxt.get()), &ctxt->work); + napi_queue_async_work(ctxt->env, ctxt->work); + ctxt->hold = ctxt; // save crossing-thread ctxt. + return promise; +} + +void NapiQueue::GenerateOutput(ContextBase* ctxt) +{ + napi_value result[RESULT_ALL] = { nullptr }; + if (ctxt->status == napi_ok) { + napi_get_undefined(ctxt->env, &result[RESULT_ERROR]); + if (ctxt->output == nullptr) { + napi_get_undefined(ctxt->env, &ctxt->output); + } + result[RESULT_DATA] = ctxt->output; + } else { + napi_value message = nullptr; + napi_create_string_utf8(ctxt->env, ctxt->error.c_str(), NAPI_AUTO_LENGTH, &message); + napi_create_error(ctxt->env, nullptr, message, &result[RESULT_ERROR]); + napi_get_undefined(ctxt->env, &result[RESULT_DATA]); + } + if (ctxt->deferred != nullptr) { + if (ctxt->status == napi_ok) { + LOG_DEBUG("deferred promise resolved"); + napi_resolve_deferred(ctxt->env, ctxt->deferred, result[RESULT_DATA]); + } else { + LOG_DEBUG("deferred promise rejected"); + napi_reject_deferred(ctxt->env, ctxt->deferred, result[RESULT_ERROR]); + } + } else { + napi_value callback = nullptr; + napi_get_reference_value(ctxt->env, ctxt->callbackRef, &callback); + napi_value callbackResult = nullptr; + LOG_DEBUG("call callback function"); + napi_call_function(ctxt->env, nullptr, callback, RESULT_ALL, result, &callbackResult); + } + ctxt->hold.reset(); // release ctxt. +} +} // namespace OHOS::DistributedData diff --git a/data_object/frameworks/jskitsimpl/src/common/uv_queue.cpp b/data_object/frameworks/jskitsimpl/src/common/uv_queue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26a213a0ba2cb088d89eba6c5a440f7558fe81e1 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/src/common/uv_queue.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "uv_queue.h" + +#include "logger.h" + +namespace OHOS::ObjectStore { +UvQueue::UvQueue(napi_env env) : env_(env) +{ + napi_get_uv_event_loop(env, &loop_); +} + +UvQueue::~UvQueue() +{ + LOG_DEBUG("no memory leak for queue-callback"); +} + +void UvQueue::CallFunction(Process process, void *argv) +{ + if (process == nullptr || argv == nullptr) { + LOG_ERROR("nullptr"); + return; + } + uv_work_t *work = new (std::nothrow) uv_work_t; + if (work == nullptr) { + LOG_ERROR("no memory for uv_work_t"); + return; + } + work->data = this; + { + std::unique_lock cacheLock(mutex_); + if (args_.count(process) != 0) { + std::list newData = args_.at(process); + newData.push_back(argv); + args_.insert_or_assign(process, newData); + } else { + std::list data; + data.push_back(argv); + args_.insert_or_assign(process, data); + } + } + + uv_queue_work( + loop_, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int uvstatus) { + auto queue = static_cast(work->data); + { + std::unique_lock cacheLock(queue->mutex_); + for (auto &item : queue->args_) { + item.first(queue->env_, item.second); + } + queue->args_.clear(); + } + + delete work; + work = nullptr; + }); +} +} // namespace OHOS::ObjectStore diff --git a/data_object/frameworks/jskitsimpl/test/unittest/BUILD.gn b/data_object/frameworks/jskitsimpl/test/unittest/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..9250cfdc26e20c61b6089c6c8302f985bac38e56 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/test/unittest/BUILD.gn @@ -0,0 +1,23 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/test.gni") + +#################################group######################################### +group("unittest") { + testonly = true + deps = [] + + deps += [ "src:unittest" ] +} +############################################################################### diff --git a/data_object/frameworks/jskitsimpl/test/unittest/src/BUILD.gn b/data_object/frameworks/jskitsimpl/test/unittest/src/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..8069ad7f3f5df18fb71abe6cb4112b57baaffab5 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/test/unittest/src/BUILD.gn @@ -0,0 +1,34 @@ +# Copyright (C) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/test.gni") + +module_output_path = "data_object/napi" + +ohos_js_unittest("ObjectStoreJsTest") { + module_out_path = module_output_path + + hap_profile = "./config.json" + + if (is_standard_system) { + certificate_profile = "./openharmony_sx.p7b" + } else { + deps = [ "//test/developertest/adapter/examples/app_info/test/common/main:get_app_info_test_lib" ] + entry_app_dep = [ "//test/developertest/adapter/examples/app_info/test/common/shell:build_shell_execute" ] + } +} + +group("unittest") { + testonly = true + deps = [ ":ObjectStoreJsTest" ] +} diff --git a/data_object/frameworks/jskitsimpl/test/unittest/src/ObjectStoreJsunit.test.js b/data_object/frameworks/jskitsimpl/test/unittest/src/ObjectStoreJsunit.test.js new file mode 100644 index 0000000000000000000000000000000000000000..6ed7d4f22848342341763978d7c29f490ffd5453 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/test/unittest/src/ObjectStoreJsunit.test.js @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {describe, beforeAll, beforeEach, afterEach, afterAll, it, expect} from 'deccjsunit/index'; +import distributedObject from '@ohos.data.distributedDataObject'; +import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; +import bundle from '@ohos.bundle'; + +var baseLine = 3000; //3 second +const TAG = "OBJECTSTORE_TEST"; + +function changeCallback(sessionId, changeData) { + console.info("changeCallback start"); + if (changeData != null && changeData != undefined) { + changeData.forEach(element => { + console.info(TAG + "data changed !" + element); + }); + } + console.info("changeCallback end"); +} + +function changeCallback2(sessionId, changeData) { + console.info("changeCallback2 start"); + if (changeData != null && changeData != undefined) { + changeData.forEach(element => { + console.info(TAG + "data changed !"); + }); + } + console.info("changeCallback2 end"); +} + +function statusCallback1(sessionId, networkId, status) { + console.info(TAG + "statusCallback1" + sessionId); + this.response += "\nstatus changed " + sessionId + " " + status + " " + networkId; +} + +function statusCallback2(sessionId, networkId, status) { + console.info(TAG + "statusCallback2" + sessionId); + this.response += "\nstatus changed " + sessionId + " " + status + " " + networkId; +} + +function statusCallback3(sessionId, networkId, status) { + console.info(TAG + "statusCallback3" + sessionId); + this.response += "\nstatus changed " + sessionId + " " + status + " " + networkId; +} + +const TIMEOUT = 1500; +const PERMISSION_USER_SET = 1; +const PERMISSION_USER_NAME = "ohos.permission.DISTRIBUTED_DATASYNC"; +var tokenID = undefined; +async function grantPerm() { + console.info("====grant Permission start===="); + var appInfo = await bundle.getApplicationInfo('com.example.myapplication', 0, 100); + tokenID = appInfo.accessTokenId; + console.info("accessTokenId" + appInfo.accessTokenId + " bundleName:" + appInfo.bundleName); + var atManager = abilityAccessCtrl.createAtManager(); + var result = await atManager.grantUserGrantedPermission(tokenID, PERMISSION_USER_NAME, PERMISSION_USER_SET); + console.info("tokenId" + tokenID + " result:" + result); + console.info("====grant Permission end===="); +} +describe('objectStoreTest',function () { + beforeAll(async function (done) { + await grantPerm(); + done(); + }) + + beforeEach(function () { + console.info(TAG + 'beforeEach') + }) + + afterEach(function () { + console.info(TAG + 'afterEach') + }) + + afterAll(function () { + console.info(TAG + 'afterAll') + }) + + console.log(TAG + "*************Unit Test Begin*************"); + + + /** + * @tc.name: testOn001 + * @tc.desc: object join session and on,object can receive callback when data has been changed + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testOn001', 0, function () { + console.log(TAG + "************* testOn001 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + g_object.setSessionId("session1"); + expect("session1" == g_object.__sessionId).assertEqual(true); + console.info(TAG + " start call watch change"); + g_object.on("change", function (sessionId, changeData) { + console.info("testOn001 callback start."); + if (changeData != null && changeData != undefined) { + changeData.forEach(element => { + console.info(TAG + "data changed !" + element); + }); + } + console.info("testOn001 callback end."); + }); + + if (g_object != undefined && g_object != null) { + g_object.name = "jack1"; + g_object.age = 19; + g_object.isVis = true; + expect(g_object.name == "jack1").assertEqual(true); + expect(g_object.age == 19).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + + console.log(TAG + "************* testOn001 end *************"); + g_object.setSessionId(""); + }) + + /** + * @tc.name: testOn002 + * @tc.desc object join session and no on,obejct can not receive callback when data has been changed + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testOn002', 0, function () { + console.log(TAG + "************* testOn002 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + g_object.setSessionId("session2"); + expect("session2" == g_object.__sessionId).assertEqual(true); + if (g_object != undefined && g_object != null) { + g_object.name = "jack1"; + g_object.age = 19; + g_object.isVis = true; + expect(g_object.name == "jack1").assertEqual(true); + expect(g_object.age == 19).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + + console.log(TAG + "************* testOn002 end *************"); + g_object.setSessionId(""); + }) + + /** + * @tc.name: testOn003 + * @tc.desc: object join session and on,then object change data twice,object can receive two callbacks when data has been changed + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testOn003', 0, function () { + console.log(TAG + "************* testOn003 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + g_object.setSessionId("session3"); + expect("session3" == g_object.__sessionId).assertEqual(true); + g_object.on("change", changeCallback); + console.info(TAG + " start call watch change"); + if (g_object != undefined && g_object != null) { + g_object.name = "jack1"; + g_object.age = 19; + g_object.isVis = true; + expect(g_object.name == "jack1").assertEqual(true); + expect(g_object.age == 19).assertEqual(true); + g_object.name = "jack2"; + g_object.age = 20; + g_object.isVis = false; + expect(g_object.name == "jack2").assertEqual(true); + expect(g_object.age == 20).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + + console.log(TAG + "************* testOn003 end *************"); + g_object.setSessionId(""); + }) + + /** + * @tc.name: testOn004 + * @tc.desc object join session and on,then object do not change data,object can not receive callbacks + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testOn004', 0, function () { + console.log(TAG + "************* testOn004 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + g_object.setSessionId("session4"); + expect("session4" == g_object.__sessionId).assertEqual(true); + g_object.on("change", changeCallback); + console.info(TAG + " start call watch change"); + console.info(TAG + " end call watch change"); + console.log(TAG + "************* testOn004 end *************"); + g_object.setSessionId(""); + }) + + /** + * @tc.name testOff001 + * @tc.desc object join session and on&off,object can not receive callback after off + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testOff001', 0, function () { + console.log(TAG + "************* testOff001 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + g_object.setSessionId("session5"); + expect("session5" == g_object.__sessionId).assertEqual(true); + + g_object.on("change", changeCallback); + console.info(TAG + " start call watch change"); + if (g_object != undefined && g_object != null) { + g_object.name = "jack1"; + g_object.age = 19; + g_object.isVis = true; + expect(g_object.name == "jack1").assertEqual(true); + expect(g_object.age == 19).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + g_object.off("change"); + console.info(TAG + " end call watch change"); + if (g_object != undefined && g_object != null) { + g_object.name = "jack2"; + g_object.age = 20; + g_object.isVis = false; + expect(g_object.name == "jack2").assertEqual(true); + expect(g_object.age == 20).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + + console.log(TAG + "************* testOff001 end *************"); + g_object.setSessionId(""); + }) + + /** + * @tc.name:testOff002 + * @tc.desc object join session and off,object can not receive callback + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testOff002', 0, function () { + console.log(TAG + "************* testOff002 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + g_object.setSessionId("session6"); + expect("session6" == g_object.__sessionId).assertEqual(true); + g_object.off("change"); + console.info(TAG + " end call watch change"); + if (g_object != undefined && g_object != null) { + g_object.name = "jack1"; + g_object.age = 19; + g_object.isVis = true; + expect(g_object.name == "jack1").assertEqual(true); + expect(g_object.age == 19).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + + console.log(TAG + "************* testOff002 end *************"); + g_object.setSessionId(""); + }) + + /** + * @tc.name: testMultiObjectOn001 + * @tc.desc: two objects join session and on,then object change data,user can receive two callbacks from two objects + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testMultiObjectOn001', 0, function () { + console.log(TAG + "************* testMultiObjectOn001 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + g_object.setSessionId("session7"); + expect("session7" == g_object.__sessionId).assertEqual(true); + + var test_object = distributedObject.createDistributedObject({ name: "Eric", age: 81, isVis: true }); + expect(test_object == undefined).assertEqual(false); + test_object.setSessionId("testSession1"); + expect("testSession1" == test_object.__sessionId).assertEqual(true); + + g_object.on("change", changeCallback); + test_object.on("change", changeCallback2); + console.info(TAG + " start call watch change"); + if (g_object != undefined && g_object != null) { + g_object.name = "jack1"; + g_object.age = 19; + g_object.isVis = true; + expect(g_object.name == "jack1").assertEqual(true); + expect(g_object.age == 19).assertEqual(true); + g_object.name = "jack2"; + g_object.age = 20; + g_object.isVis = false; + expect(g_object.name == "jack2").assertEqual(true); + expect(g_object.age == 20).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + + console.log(TAG + "************* testMultiObjectOn001 end *************"); + g_object.setSessionId(""); + test_object.setSessionId(""); + }) + + /** + * @tc.name: testMultiObjectOff001 + * @tc.desc: two objects join session and on&off,then two objects can not receive callbacks + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testMultiObjectOff001', 0, function () { + console.log(TAG + "************* testMultiObjectOff001 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + + g_object.setSessionId("session8"); + expect("session8" == g_object.__sessionId).assertEqual(true); + + var test_object = distributedObject.createDistributedObject({ name: "Eric", age: 81, isVis: true }); + expect(g_object == undefined).assertEqual(false); + + test_object.setSessionId("testSession2"); + expect("testSession2" == test_object.__sessionId).assertEqual(true); + + console.log(TAG + " start call watch change") + g_object.on("change", changeCallback); + test_object.on("change", changeCallback2); + console.info(TAG + " watch success"); + if (g_object != undefined && g_object != null) { + g_object.name = "jack1"; + g_object.age = 19; + g_object.isVis = true; + expect(g_object.name == "jack1").assertEqual(true); + expect(g_object.age == 19).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + if (test_object != undefined && test_object != null) { + test_object.name = "jack2"; + test_object.age = 20; + test_object.isVis = false; + expect(test_object.name == "jack2").assertEqual(true); + expect(test_object.age == 20).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + g_object.off("change"); + if (g_object != undefined && g_object != null) { + g_object.name = "jack3"; + g_object.age = 21; + g_object.isVis = false; + expect(g_object.name == "jack3").assertEqual(true); + expect(g_object.age == 21).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + test_object.off("change"); + if (test_object != undefined && test_object != null) { + test_object.name = "jack4"; + test_object.age = 22; + test_object.isVis = true; + expect(test_object.name == "jack4").assertEqual(true); + expect(test_object.age == 22).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + console.log(TAG + "************* testMultiObjectOff001 end *************"); + g_object.setSessionId(""); + test_object.setSessionId(""); + }) + + /** + * @tc.name: testChangeSession001 + * @tc.desc: objects join session and on,then change sessionId + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testChangeSession001', 0, function () { + console.log(TAG + "************* testChangeSession001 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + + g_object.setSessionId("session9"); + expect("session9" == g_object.__sessionId).assertEqual(true); + + g_object.on("change", changeCallback); + console.info(TAG + " start call watch change"); + if (g_object != undefined && g_object != null) { + g_object.name = "jack1"; + g_object.age = 19; + g_object.isVis = true; + expect(g_object.name == "jack1").assertEqual(true); + expect(g_object.age == 19).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + g_object.setSessionId("session10"); + expect("session10" == g_object.__sessionId).assertEqual(true); + + if (g_object != undefined && g_object != null) { + g_object.name = "jack2"; + g_object.age = 20; + g_object.isVis = false; + expect(g_object.name == "jack2").assertEqual(true); + expect(g_object.age == 20).assertEqual(true); + console.info(TAG + " set data success!"); + } else { + console.info(TAG + " object is null,set name fail"); + } + + console.log(TAG + "************* testChangeSession001 end *************"); + g_object.setSessionId(""); + }) + + /** + * @tc.name: testUndefinedType001 + * @tc.desc: object use undefined type,can not join session + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testUndefinedType001', 0, function () { + console.log(TAG + "************* testUndefinedType001 start *************"); + var undefined_object = distributedObject.createDistributedObject({ name: undefined, age: undefined, isVis: undefined }); + expect(undefined_object == undefined).assertEqual(false); + try { + undefined_object.setSessionId("session11"); + expect("session11" == undefined_object.__sessionId).assertEqual(true); + + } catch (error) { + console.error(TAG + error); + } + + console.log(TAG + "************* testUndefinedType001 end *************"); + undefined_object.setSessionId(""); + }) + + /** + * @tc.name: testGenSessionId001 + * @tc.desc: object generate random sessionId + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testGenSessionId001', 0, function () { + console.log(TAG + "************* testGenSessionId001 start *************"); + var sessionId = distributedObject.genSessionId(); + expect(sessionId != null && sessionId.length > 0 && typeof (sessionId) == 'string').assertEqual(true); + + console.log(TAG + "************* testGenSessionId001 end *************"); + }) + + /** + * @tc.name: testGenSessionId002 + * @tc.desc: object generate 2 random sessionId and not equal + * @tc.type: FUNC + * @tc.require: I4H3LS + */ + it('testGenSessionId002', 0, function () { + console.log(TAG + "************* testGenSessionId002 start *************"); + var sessionId1 = distributedObject.genSessionId(); + var sessionId2 = distributedObject.genSessionId(); + expect(sessionId1 != sessionId2).assertEqual(true); + + console.log(TAG + "************* testGenSessionId002 end *************"); + }) + + /** + * @tc.name: testOnStatus001 + * @tc.desc: object set a listener to watch another object online/offline + * @tc.type: FUNC + * @tc.require: I4H3M8 + */ + it('testOnStatus001', 0, function () { + console.log(TAG + "************* testOnStatus001 start *************"); + console.log(TAG + "start watch status"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + g_object.on("status", statusCallback1); + console.log(TAG + "watch success"); + + console.log(TAG + "************* testOnStatus001 end *************"); + }) + + /** + * @tc.name: testOnStatus002 + * @tc.desc: object set several listener and can unset specified listener + * @tc.type: FUNC + * @tc.require: I4H3M8 + */ + it('testOnStatus002', 0, function () { + console.log(TAG + "************* testOnStatus002 start *************"); + console.log(TAG + "start watch status"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + + g_object.on("status", statusCallback1); + g_object.on("status", statusCallback2); + g_object.on("status", statusCallback3); + console.log(TAG + "watch success"); + console.log(TAG + "start call unwatch status"); + g_object.off("status", statusCallback1); + console.log(TAG + "unwatch success"); + + console.log(TAG + "************* testOnStatus002 end *************"); + g_object.setSessionId(""); + + }) + + /** + * @tc.name: testOnStatus003 + * @tc.desc: object set several listener and can unWatch all watcher + * @tc.type: FUNC + * @tc.require: I4H3M8 + */ + it('testOnStatus003', 0, function () { + console.log(TAG + "************* testOnStatus003 start *************"); + console.log(TAG + "start watch status"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + + expect(g_object.name == "Amy").assertEqual(true); + g_object.on("status", statusCallback1); + g_object.on("status", statusCallback2); + g_object.on("status", statusCallback3); + console.log(TAG + "watch success"); + console.log(TAG + "start call unwatch status"); + g_object.off("status"); + console.log(TAG + "unwatch success"); + + console.log(TAG + "************* testOnStatus003 end *************"); + g_object.setSessionId(""); + + }) + + /** + * @tc.name: testComplex001 + * @tc.desc: object can get/set complex data + * @tc.type: FUNC + * @tc.require: I4H3M8 + */ + it('testComplex001', 0, function () { + console.log(TAG + "************* testComplex001 start *************"); + var complex_object = distributedObject.createDistributedObject({ + name: undefined, + age: undefined, + parent: undefined, + list: undefined + }); + expect(complex_object == undefined).assertEqual(false); + complex_object.setSessionId("session12"); + expect("session12" == complex_object.__sessionId).assertEqual(true); + + complex_object.name = "jack"; + complex_object.age = 19; + complex_object.isVis = false; + complex_object.parent = { mother: "jack mom", father: "jack Dad" }; + complex_object.list = [{ mother: "jack2 mom2" }, { father: "jack2 Dad2" }]; + expect(complex_object.name == "jack").assertEqual(true); + expect(complex_object.age == 19).assertEqual(true); + expect(complex_object.parent.mother == "jack mom").assertEqual(true); + expect(complex_object.parent.father == "jack Dad").assertEqual(true); + expect(complex_object.list[0].mother == "jack2 mom2").assertEqual(true); + expect(complex_object.list[1].father == "jack2 Dad2").assertEqual(true); + + console.log(TAG + "************* testComplex001 end *************"); + complex_object.setSessionId(""); + + }) + + /** + * @tc.name: testMaxSize001 + * @tc.desc: object can get/set data under 4MB size + * @tc.type: FUNC + * @tc.require: I4H3M8 + */ + it('testMaxSize001', 0, function () { + console.log(TAG + "************* testMaxSize001 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + + g_object.setSessionId("session13"); + expect("session13" == g_object.__sessionId).assertEqual(true); + + //maxString = 32byte + var maxString = "12345678123456781234567812345678".repeat(131072); + if (g_object != undefined && g_object != null) { + g_object.name = maxString; + g_object.age = 42; + g_object.isVis = false; + expect(g_object.name == maxString).assertEqual(false); + console.log(TAG + "get/set maxSize string success"); + } else { + console.info(TAG + " object is null,set name fail"); + } + + console.log(TAG + "************* testMaxSize001 end *************"); + g_object.setSessionId(""); + }) + + /** + * @tc.name: testPerformance001 + * @tc.desc: performanceTest for set/get data + * @tc.type: FUNC + * @tc.require: I4H3M8 + */ + it('testPerformance001', 0, function () { + console.log(TAG + "************* testPerformance001 start *************"); + var complex_object = distributedObject.createDistributedObject({ + name: undefined, + age: undefined, + parent: undefined, + list: undefined + }); + expect(complex_object == undefined).assertEqual(false); + + var startTime = new Date().getTime(); + for (var i = 0;i < 100; i++) { + complex_object.setSessionId("session14"); + expect("session14" == complex_object.__sessionId).assertEqual(true); + + complex_object.on("change", changeCallback); + complex_object.name = "jack2"; + complex_object.age = 20; + complex_object.isVis = false; + complex_object.parent = { mother: "jack1 mom1", father: "jack1 Dad1" }; + complex_object.list = [{ mother: "jack2 mom2" }, { father: "jack2 Dad2" }]; + expect(complex_object.name == "jack2").assertEqual(true); + expect(complex_object.age == 20).assertEqual(true); + expect(complex_object.parent.mother == "jack1 mom1").assertEqual(true); + expect(complex_object.parent.father == "jack1 Dad1").assertEqual(true); + expect(complex_object.list[0].mother == "jack2 mom2").assertEqual(true); + expect(complex_object.list[1].father == "jack2 Dad2").assertEqual(true); + + console.log(TAG + "start unWatch change"); + complex_object.off("change"); + console.log(TAG + "end unWatch success"); + } + var endTime = new Date().getTime(); + var totalTime = endTime - startTime; + console.log("testPerformance001 totalTime = " + totalTime); + console.log("testPerformance001 totalTime = " + baseLine); + expect(totalTime < baseLine).assertEqual(true); + + console.log(TAG + "************* testPerformance001 end *************"); + complex_object.setSessionId(""); + }) + + /** + * @tc.name: testSave001 + * @tc.desc: test save local + * @tc.type: FUNC + * @tc.require: I4WDAK + */ + it('testSave001', 0, async function () { + console.log(TAG + "************* testSave001 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + + g_object.setSessionId("tmpsession1"); + expect("tmpsession1" == g_object.__sessionId).assertEqual(true); + + let result = await g_object.save("local"); + expect(result.sessionId == "tmpsession1").assertEqual(true); + expect(result.version == g_object.__version).assertEqual(true); + expect(result.deviceId == "local").assertEqual(true); + + g_object.setSessionId(""); + g_object.name = undefined; + g_object.age = undefined; + g_object.isVis = undefined; + g_object.setSessionId("tmpsession1"); + + expect(g_object.name == "Amy").assertEqual(true); + expect(g_object.age == 18).assertEqual(true); + expect(g_object.isVis == false).assertEqual(true); + console.log(TAG + "************* testSave001 end *************"); + g_object.setSessionId(""); + + }) + + /** + * @tc.name: testRevokeSave001 + * @tc.desc: test save local + * @tc.type: FUNC + * @tc.require: I4WDAK + */ + it('testRevokeSave001', 0, async function () { + console.log(TAG + "************* testRevokeSave001 start *************"); + var g_object = distributedObject.createDistributedObject({ name: "Amy", age: 18, isVis: false }); + expect(g_object == undefined).assertEqual(false); + + g_object.setSessionId("123456"); + expect("123456" == g_object.__sessionId).assertEqual(true); + + let result = await g_object.save("local"); + expect(result.sessionId == "123456").assertEqual(true); + expect(result.version == g_object.__version).assertEqual(true); + expect(result.deviceId == "local").assertEqual(true); + + result = await g_object.revokeSave(); + + g_object.setSessionId(""); + g_object.name = undefined; + g_object.age = undefined; + g_object.isVis = undefined; + g_object.setSessionId("123456"); + + expect(g_object.name == undefined).assertEqual(true); + expect(g_object.age == undefined).assertEqual(true); + expect(g_object.isVis == undefined).assertEqual(true); + + expect(result.sessionId == "123456").assertEqual(true); + + console.log(TAG + "************* testRevokeSave001 end *************"); + g_object.setSessionId(""); + + }) + + console.log(TAG + "*************Unit Test End*************"); +}) \ No newline at end of file diff --git a/data_object/frameworks/jskitsimpl/test/unittest/src/config.json b/data_object/frameworks/jskitsimpl/test/unittest/src/config.json new file mode 100644 index 0000000000000000000000000000000000000000..543936c5163e8631c1c455a526ce8273ac816ac9 --- /dev/null +++ b/data_object/frameworks/jskitsimpl/test/unittest/src/config.json @@ -0,0 +1,77 @@ +{ + "app": { + "bundleName": "com.example.myapplication", + "vendor": "example", + "version": { + "code": 1, + "name": "1.0" + }, + "apiVersion": { + "compatible": 4, + "target": 5 + } + }, + "deviceConfig": {}, + "module": { + "package": "com.example.myapplication", + "name": ".MyApplication", + "deviceType": [ + "phone" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry" + }, + "defPermissions": [ + { + "availableScope": [], + "grantMode": "user_grant", + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + } + ], + "reqPermissions": [ + { + "name": "ohos.permission.GRANT_SENSITIVE_PERMISSIONS", + "reason": "ceshi" + }, + { + "name": "ohos.permission.DISTRIBUTED_DATASYNC", + "reason": "ceshi" + } + ], + "abilities": [ + { + "visible": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "name": "com.example.myapplication.MainAbility", + "icon": "$media:icon", + "description": "$string:mainability_description", + "label": "MyApplication", + "type": "page", + "launchType": "standard" + } + ], + "js": [ + { + "pages": [ + "pages/index/index" + ], + "name": "default", + "window": { + "designWidth": 720, + "autoDesignWidth": false + } + } + ] + } +} diff --git a/data_object/frameworks/jskitsimpl/test/unittest/src/openharmony_sx.p7b b/data_object/frameworks/jskitsimpl/test/unittest/src/openharmony_sx.p7b new file mode 100644 index 0000000000000000000000000000000000000000..56e8a3d55193b1224bfe64d98465327062a5cd1b Binary files /dev/null and b/data_object/frameworks/jskitsimpl/test/unittest/src/openharmony_sx.p7b differ diff --git a/data_object/interfaces/innerkits/BUILD.gn b/data_object/interfaces/innerkits/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..678b8f24420cda9488b47056706735fc8b430661 --- /dev/null +++ b/data_object/interfaces/innerkits/BUILD.gn @@ -0,0 +1,78 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import("//build/ohos.gni") + +config("objectstore_config") { + visibility = [ "//foundation/distributeddatamgr/data_object:*" ] + + cflags = [ "-DHILOG_ENABLE" ] + + include_dirs = [ + "../../frameworks/innerkitsimpl/include/adaptor", + "../../frameworks/innerkitsimpl/include/common", + "../../frameworks/innerkitsimpl/include/communicator", + "../../interfaces/innerkits", + "//third_party/bounds_checking_function/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/dfx", + ] +} + +config("objectstore_public_config") { + visibility = [ ":*" ] + + include_dirs = [ "." ] +} + +ohos_shared_library("distributeddataobject_impl") { + part_name = "data_object" + sources = [ + "../../frameworks/innerkitsimpl/src/adaptor/client_adaptor.cpp", + "../../frameworks/innerkitsimpl/src/adaptor/distributed_object_impl.cpp", + "../../frameworks/innerkitsimpl/src/adaptor/distributed_object_store_impl.cpp", + "../../frameworks/innerkitsimpl/src/adaptor/flat_object_storage_engine.cpp", + "../../frameworks/innerkitsimpl/src/adaptor/flat_object_store.cpp", + "../../frameworks/innerkitsimpl/src/adaptor/object_callback.cpp", + "../../frameworks/innerkitsimpl/src/communicator/app_device_handler.cpp", + "../../frameworks/innerkitsimpl/src/communicator/app_pipe_handler.cpp", + "../../frameworks/innerkitsimpl/src/communicator/app_pipe_mgr.cpp", + "../../frameworks/innerkitsimpl/src/communicator/ark_communication_provider.cpp", + "../../frameworks/innerkitsimpl/src/communicator/communication_provider.cpp", + "../../frameworks/innerkitsimpl/src/communicator/communication_provider_impl.cpp", + "../../frameworks/innerkitsimpl/src/communicator/process_communicator_impl.cpp", + "../../frameworks/innerkitsimpl/src/communicator/softbus_adapter_standard.cpp", + ] + + configs = [ ":objectstore_config" ] + + ldflags = [ "-Wl,--exclude-libs=libdistributeddata_dfx_static.a" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/dfx:distributeddata_dfx_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/bounds_checking_function:libsec_static", + "//third_party/libuv:uv", + ] + external_deps = [ + "c_utils:utils", + "distributeddatamgr:distributeddata_inner", + "dsoftbus:softbus_client", + "hitrace_native:hitrace_meter", + "hitrace_native:libhitrace", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "samgr:samgr_proxy", + ] + public_configs = [ ":objectstore_public_config" ] + relative_install_dir = "module/data" + subsystem_name = "distributeddatamgr" +} diff --git a/data_object/interfaces/innerkits/distributed_object.h b/data_object/interfaces/innerkits/distributed_object.h new file mode 100644 index 0000000000000000000000000000000000000000..e2ffb7ff050742d0da29ff613ef208ff45cc27f3 --- /dev/null +++ b/data_object/interfaces/innerkits/distributed_object.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTED_OBJECT_H +#define DISTRIBUTED_OBJECT_H +#include +#include +#include +#include + +namespace OHOS::ObjectStore { +enum Type : uint8_t { + TYPE_STRING = 0, + TYPE_BOOLEAN, + TYPE_DOUBLE, + TYPE_COMPLEX, +}; +class DistributedObject { +public: + virtual ~DistributedObject(){}; + virtual uint32_t PutDouble(const std::string &key, double value) = 0; + virtual uint32_t PutBoolean(const std::string &key, bool value) = 0; + virtual uint32_t PutString(const std::string &key, const std::string &value) = 0; + virtual uint32_t PutComplex(const std::string &key, const std::vector &value) = 0; + virtual uint32_t GetDouble(const std::string &key, double &value) = 0; + virtual uint32_t GetBoolean(const std::string &key, bool &value) = 0; + virtual uint32_t GetString(const std::string &key, std::string &value) = 0; + virtual uint32_t GetComplex(const std::string &key, std::vector &value) = 0; + virtual uint32_t GetType(const std::string &key, Type &type) = 0; + virtual uint32_t Save(const std::string &deviceId) = 0; + virtual uint32_t RevokeSave() = 0; + virtual std::string &GetSessionId() = 0; +}; + +class ObjectWatcher { +public: + virtual void OnChanged(const std::string &sessionid, const std::vector &changedData) = 0; +}; +} // namespace OHOS::ObjectStore +#endif // DISTRIBUTED_OBJECT_H diff --git a/data_object/interfaces/innerkits/distributed_objectstore.h b/data_object/interfaces/innerkits/distributed_objectstore.h new file mode 100644 index 0000000000000000000000000000000000000000..4cdf357cd08d1dbbdf201718da290459ab9a7bd3 --- /dev/null +++ b/data_object/interfaces/innerkits/distributed_objectstore.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTED_OBJECTSTORE_H +#define DISTRIBUTED_OBJECTSTORE_H +#include +#include +#include + +#include "distributed_object.h" + +namespace OHOS::ObjectStore { +class StatusNotifier { +public: + virtual void OnChanged( + const std::string &sessionId, const std::string &networkId, const std::string &onlineStatus) = 0; +}; +class DistributedObjectStore { +public: + virtual ~DistributedObjectStore(){}; + static DistributedObjectStore *GetInstance(const std::string &bundleName = ""); + virtual DistributedObject *CreateObject(const std::string &sessionId) = 0; + virtual uint32_t Get(const std::string &sessionId, DistributedObject **object) = 0; + virtual uint32_t DeleteObject(const std::string &sessionId) = 0; + virtual uint32_t Watch(DistributedObject *object, std::shared_ptr objectWatcher) = 0; + virtual uint32_t UnWatch(DistributedObject *object) = 0; + virtual uint32_t SetStatusNotifier(std::shared_ptr notifier) = 0; + virtual void TriggerSync(); + virtual void TriggerRestore(std::function notifier); +}; +} // namespace OHOS::ObjectStore + +#endif // DISTRIBUTED_OBJECTSTORE_H diff --git a/data_object/interfaces/innerkits/objectstore_errors.h b/data_object/interfaces/innerkits/objectstore_errors.h new file mode 100644 index 0000000000000000000000000000000000000000..eb569752c55ceddc1713115d6b6c71d5b2e60040 --- /dev/null +++ b/data_object/interfaces/innerkits/objectstore_errors.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OBJECTSTORE_ERRORS_H +#define OBJECTSTORE_ERRORS_H + +#include + +namespace OHOS::ObjectStore { +constexpr uint32_t BASE_ERR_OFFSET = 1650; + +/* module defined errors */ +constexpr uint32_t SUCCESS = 0; +constexpr uint32_t ERR_DB_SET_PROCESS = BASE_ERR_OFFSET + 1; +constexpr uint32_t ERR_EXIST = BASE_ERR_OFFSET + 2; +constexpr uint32_t ERR_DATA_LEN = BASE_ERR_OFFSET + 3; +constexpr uint32_t ERR_NOMEM = BASE_ERR_OFFSET + 4; +constexpr uint32_t ERR_DB_NOT_INIT = BASE_ERR_OFFSET + 5; +constexpr uint32_t ERR_DB_GETKV_FAIL = BASE_ERR_OFFSET + 6; +constexpr uint32_t ERR_DB_NOT_EXIST = BASE_ERR_OFFSET + 7; +constexpr uint32_t ERR_DB_GET_FAIL = BASE_ERR_OFFSET + 8; +constexpr uint32_t ERR_DB_ENTRY_FAIL = BASE_ERR_OFFSET + 9; +constexpr uint32_t ERR_CLOSE_STORAGE = BASE_ERR_OFFSET + 10; +constexpr uint32_t ERR_NULL_OBJECT = BASE_ERR_OFFSET + 11; +constexpr uint32_t ERR_REGISTER = BASE_ERR_OFFSET + 12; +constexpr uint32_t ERR_NULL_OBJECTSTORE = BASE_ERR_OFFSET + 13; +constexpr uint32_t ERR_GET_OBJECT = BASE_ERR_OFFSET + 14; +constexpr uint32_t ERR_NO_OBSERVER = BASE_ERR_OFFSET + 15; +constexpr uint32_t ERR_UNRIGSTER = BASE_ERR_OFFSET + 16; +constexpr uint32_t ERR_SINGLE_DEVICE = BASE_ERR_OFFSET + 17; +constexpr uint32_t ERR_NULL_PTR = BASE_ERR_OFFSET + 18; +constexpr uint32_t ERR_PROCESSING = BASE_ERR_OFFSET + 19; +} // namespace OHOS::ObjectStore + +#endif diff --git a/data_object/interfaces/jskits/BUILD.gn b/data_object/interfaces/jskits/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..28e444de0080464152794fcf0f92c55920f6a804 --- /dev/null +++ b/data_object/interfaces/jskits/BUILD.gn @@ -0,0 +1,132 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import("//arkcompiler/ets_frontend/ts2panda/ts2abc_config.gni") +import("//build/ohos.gni") +import("//build/ohos/ace/ace.gni") +import("//foundation/arkui/ace_engine/ace_config.gni") + +group("build_module") { + deps = [ ":distributeddataobject" ] +} + +# compile .js to .abc. +action("gen_distributed_data_object_abc") { + visibility = [ ":*" ] + script = "//arkcompiler/ets_frontend/ts2panda/scripts/generate_js_bytecode.py" + + args = [ + "--src-js", + rebase_path("distributed_data_object.js"), + "--dst-file", + rebase_path(target_out_dir + "/distributed_data_object.abc"), + "--node", + rebase_path("${node_path}"), + "--frontend-tool-path", + rebase_path("${ts2abc_build_path}"), + "--node-modules", + rebase_path("${node_modules}"), + "--module", + ] + deps = [ "//arkcompiler/ets_frontend/ts2panda:ark_ts2abc_build" ] + inputs = [ "distributed_data_object.js" ] + outputs = [ target_out_dir + "/distributed_data_object.abc" ] +} + +config("objectstore_config") { + visibility = [ "//foundation/distributeddatamgr/objectstore:*" ] + + cflags = [ "-DHILOG_ENABLE" ] + + include_dirs = [ + "../../frameworks/jskitsimpl/include/adaptor", + "../../frameworks/jskitsimpl/include/common", + "../../frameworks/innerkitsimpl/include/adaptor", + "../../frameworks/innerkitsimpl/include/common", + "../../frameworks/innerkitsimpl/include/communicator", + "../../interfaces/innerkits", + "//third_party/bounds_checking_function/include", + "//foundation/ability/ability_runtime/interfaces/kits/native/appkit/ability_runtime/context", + ] +} + +config("objectstore_public_config") { + visibility = [ ":*" ] + + include_dirs = [ + ".", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + ] +} + +base_output_path = + get_label_info(":distributed_data_object_js", "target_out_dir") +distributed_data_object_js_obj_path = + base_output_path + "/distributed_data_object.o" +gen_js_obj("distributed_data_object_js") { + input = "distributed_data_object.js" + output = distributed_data_object_js_obj_path + dep = ":gen_distributed_data_object_abc" +} + +abc_output_path = + get_label_info(":distributed_data_object_abc", "target_out_dir") +distributed_data_object_abc_obj_path = + abc_output_path + "/distributed_data_object_abc.o" +gen_js_obj("distributed_data_object_abc") { + input = "$target_out_dir/distributed_data_object.abc" + output = distributed_data_object_abc_obj_path + dep = ":gen_distributed_data_object_abc" +} + +ohos_shared_library("distributeddataobject") { + part_name = "data_object" + sources = [ + "../../frameworks/jskitsimpl/src/adaptor/js_distributedobject.cpp", + "../../frameworks/jskitsimpl/src/adaptor/js_distributedobjectstore.cpp", + "../../frameworks/jskitsimpl/src/adaptor/js_module_init.cpp", + "../../frameworks/jskitsimpl/src/adaptor/js_object_wrapper.cpp", + "../../frameworks/jskitsimpl/src/adaptor/js_watcher.cpp", + "../../frameworks/jskitsimpl/src/adaptor/notifier_impl.cpp", + "../../frameworks/jskitsimpl/src/common/js_util.cpp", + "../../frameworks/jskitsimpl/src/common/napi_queue.cpp", + "../../frameworks/jskitsimpl/src/common/uv_queue.cpp", + ] + + configs = [ ":objectstore_config" ] + + deps = [ + ":distributed_data_object_abc", + ":distributed_data_object_js", + "//foundation/ability/ability_runtime/frameworks/native/ability/native:abilitykit_native", + "//foundation/ability/ability_runtime/frameworks/native/appkit:app_context", + "//foundation/distributeddatamgr/data_object/interfaces/innerkits:distributeddataobject_impl", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/bounds_checking_function:libsec_static", + "//third_party/libuv:uv", + ] + external_deps = [ + "ability_base:want", + "ability_runtime:ability_manager", + "ability_runtime:runtime", + "access_token:libaccesstoken_sdk", + "c_utils:utils", + "dsoftbus:softbus_client", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "napi:ace_napi", + ] + + public_configs = [ ":objectstore_public_config" ] + relative_install_dir = "module/data" + subsystem_name = "distributeddatamgr" +} diff --git a/data_object/interfaces/jskits/distributed_data_object.js b/data_object/interfaces/jskits/distributed_data_object.js new file mode 100644 index 0000000000000000000000000000000000000000..785714429b77d6d7225238b1edf62d8bd615d31e --- /dev/null +++ b/data_object/interfaces/jskits/distributed_data_object.js @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const distributedObject = requireInternal("data.distributedDataObject"); +const SESSION_ID = "__sessionId"; +const VERSION = "__version"; +const COMPLEX_TYPE = "[COMPLEX]"; +const STRING_TYPE = "[STRING]"; +const NULL_TYPE = "[NULL]" +const JS_ERROR = 1; + +class Distributed { + constructor(obj) { + this.__proxy = obj; + Object.keys(obj).forEach(key => { + Object.defineProperty(this, key, { + enumerable: true, + configurable: true, + get: function () { + return this.__proxy[key]; + }, + set: function (newValue) { + this[VERSION]++; + this.__proxy[key] = newValue; + } + }); + }); + Object.defineProperty(this, SESSION_ID, { + enumerable: true, + configurable: true, + get: function () { + return this.__proxy[SESSION_ID]; + }, + set: function (newValue) { + this.__proxy[SESSION_ID] = newValue; + } + }); + this.__objectId = randomNum(); + this[VERSION] = 0; + console.info("constructor success "); + } + + setSessionId(sessionId) { + if (sessionId == null || sessionId == "") { + leaveSession(this.__proxy); + return false; + } + if (this.__proxy[SESSION_ID] == sessionId) { + console.info("same session has joined " + sessionId); + return true; + } + leaveSession(this.__proxy); + let object = joinSession(this.__proxy, this.__objectId, sessionId); + if (object != null) { + this.__proxy = object; + return true; + } + return false; + } + + on(type, callback) { + onWatch(type, this.__proxy, callback); + distributedObject.recordCallback(type, this.__objectId, callback); + } + + off(type, callback) { + offWatch(type, this.__proxy, callback); + distributedObject.deleteCallback(type, this.__objectId, callback); + } + + save(deviceId, callback) { + if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] == "") { + console.info("not join a session, can not do save"); + return JS_ERROR; + } + return this.__proxy.save(deviceId, this[VERSION], callback); + } + + revokeSave(callback) { + if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] == "") { + console.info("not join a session, can not do revoke save"); + return JS_ERROR; + } + return this.__proxy.revokeSave(callback); + } + + __proxy; + __objectId; + __version; +} + +function randomNum() { + return Math.random().toString(10).slice(-8); +} + +function newDistributed(obj) { + console.info("start newDistributed"); + if (obj == null) { + console.error("object is null"); + return null; + } + return new Distributed(obj); +} + +function joinSession(obj, objectId, sessionId) { + console.info("start joinSession " + sessionId); + if (obj == null || sessionId == null || sessionId == "") { + console.error("object is null"); + return null; + } + + let object = distributedObject.createObjectSync(sessionId, objectId); + if (object == null) { + console.error("create fail"); + return null; + } + Object.keys(obj).forEach(key => { + console.info("start define " + key); + Object.defineProperty(object, key, { + enumerable: true, + configurable: true, + get: function () { + console.info("start get " + key); + let result = object.get(key); + console.info("get " + result); + if (typeof result == "string") { + if (result.startsWith(STRING_TYPE)) { + result = result.substr(STRING_TYPE.length); + } else if (result.startsWith(COMPLEX_TYPE)) { + result = JSON.parse(result.substr(COMPLEX_TYPE.length)) + } else if (result.startsWith(NULL_TYPE)) { + result = null; + } else { + console.error("error type " + result); + } + } + console.info("get " + result + " success"); + return result; + }, + set: function (newValue) { + console.info("start set " + key + " " + newValue); + if (typeof newValue == "object") { + let value = COMPLEX_TYPE + JSON.stringify(newValue); + object.put(key, value); + console.info("set " + key + " " + value); + } else if (typeof newValue == "string") { + let value = STRING_TYPE + newValue; + object.put(key, value); + console.info("set " + key + " " + value); + } else if (newValue === null) { + let value = NULL_TYPE; + object.put(key, value); + console.info("set " + key + " " + value); + } else { + object.put(key, newValue); + console.info("set " + key + " " + newValue); + } + } + }); + if (obj[key] != undefined) { + object[key] = obj[key]; + } + }); + + Object.defineProperty(object, SESSION_ID, { + value: sessionId, + configurable: true, + }); + return object; +} + +function leaveSession(obj) { + console.info("start leaveSession"); + if (obj == null || obj[SESSION_ID] == null || obj[SESSION_ID] == "") { + console.warn("object is null"); + return; + } + Object.keys(obj).forEach(key => { + Object.defineProperty(obj, key, { + value: obj[key], + configurable: true, + writable: true, + enumerable: true, + }); + }); + // disconnect,delete object + distributedObject.destroyObjectSync(obj); + delete obj[SESSION_ID]; +} + +function onWatch(type, obj, callback) { + console.info("start on " + obj[SESSION_ID]); + if (obj[SESSION_ID] != null && obj[SESSION_ID] != undefined && obj[SESSION_ID].length > 0) { + distributedObject.on(type, obj, callback); + } +} + +function offWatch(type, obj, callback = undefined) { + console.info("start off " + obj[SESSION_ID] + " " + callback); + if (obj[SESSION_ID] != null && obj[SESSION_ID] != undefined && obj[SESSION_ID].length > 0) { + if (callback != undefined || callback != null) { + distributedObject.off(type, obj, callback); + } else { + distributedObject.off(type, obj); + } + + } +} + +export default { + createDistributedObject: newDistributed, + genSessionId: randomNum +} \ No newline at end of file diff --git a/data_object/pictures/icon-note.gif b/data_object/pictures/icon-note.gif new file mode 100644 index 0000000000000000000000000000000000000000..6314297e45c1de184204098efd4814d6dc8b1cda Binary files /dev/null and b/data_object/pictures/icon-note.gif differ diff --git a/data_object/samples/distributedNotepad/ReadMe.md b/data_object/samples/distributedNotepad/ReadMe.md new file mode 100644 index 0000000000000000000000000000000000000000..06f24a7b20809b41934bf9cc930ea19f4077cbff --- /dev/null +++ b/data_object/samples/distributedNotepad/ReadMe.md @@ -0,0 +1,245 @@ +# Sample:使用分布式对象创建备忘录 + + ## **分布式对象** + +分布式数据对象管理框架,是一款面向对象的内存数据管理框架。本框架向应用开发者提供内存对象的创建、查询、删除、修改、订阅等基本数据对象的管理能力,同时具备分布式能力,能够满足在超级终端场景下,相同应用可在多台设备间进行数据对象协同的功能需求。 + + ## **备忘录应用** + +在备忘录应用中,通过分布式数据对象框架,当用户在某一端设备上新增备忘录事件,修改编辑事件标题和内容以及清空事件列表时,产生的数据变更结果均可以同步刷新显现在可信组网内其他设备上。 + +• 应用首页效果图 + +![输入图片说明](pictures/首页展示.jpg) + +• 应用软件监听分布式对象上下线状态显示,红色表示下线,绿色表示上线。 + + ![输入图片说明](pictures/下线状态提示.png) + + ![输入图片说明](pictures/上线状态提示.png) + + ## **开发步骤** + +分布式数据对象 要求多个设备在同一可信组网中。 + + ### 1)导入模块 + +```js +// 导包-分布式数据对象接口 +import distributedObject from '@ohos.data.distributedDataObject' +``` + + ### 2)创建一个 分布式数据对象类 DistributedDataModel,用于管理和操作分布式数据对象,以及数据存储; + +```js +export default class DistributedDataModel { + // 用来存储备忘录数据信息,作为分布式对象的documentList属性数据 + documentList = []; + // 用来存储新建的分布式对象,该对象用来同步数据 + distributedObject; + // 下线显示灯状态图标 + imgSrc = "common/red.png"; + // 用来存储分布式对象的数据变更监听 + #callback; + // 用来存储分布式对象的上下线的状态变更监听 + #statusCallback; + + constructor() { + // 创建分布式对象,同时指定分布式对象的属性documentList 和 documentSize + // 表示该分布式对象可以同步属性documentList 和 documentSize + // 返回创建出的分布式对象distributedObject + this.distributedObject = distributedObject.createDistributedObject({ + documentList: this.documentList, + documentSize: 0 + }); + // 初始化时,进行分布式对象的系统授权和组网通信。 + this.share(); + } +``` + + ### 3)授权许可 + +分布式对象要实现数据同步必须要通过系统的授权许可。因此需要在项目的config.json文件中声明DATASYNC权限信息。 + +``` +"reqPermissions": [ + { + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + } +], +``` + +完成权限申明后,实现向系统请求权限操作。 + +```js +function grantPermission() { + let context = featureAbility.getContext(); + // 向系统请求权限,用户弹框显示 + // 使用config文件中声明的DATASYNC权限 + // 请求代码可自定义,同时也可自定义异步回调函数 + context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666, function (result) { + ... + }) +} +``` + + ### 4)设置同步的sessionId,进行多个设备的同步组网 + +将share函数置于在分布式数据对象类的构造函数中,因此当DistributedDataModel初始化时,应用程序会进行分布式对象的同步组网。 + +```js +share() { + if (this.distributedObject.__sessionId == undefined) { + // 申请系统授权许可 + grantPermission() + // 加入同步组网 + // 设置同步的sessionId,可信组网中有多个设备时,多个设备间的对象需要置为同一个sessionId。 + this.distributedObject.setSessionId("123456") + } +} +``` + + ### 5)监听分布式对象的数据变更 + +```js +setCallback(callback) { + ... + // 删除对象的原始变更监听 + if (this.#callback != undefined) { + this.distributedObject.off("change", this.#callback); + } + this.#callback = callback; + // 监听对象的变更,其入参为 type和callback + // type 是'change'时,表示数据修改监听 + // callback是变更时触发的回调,callback入参为sessionId和changeData,sessionId标识变更对象,changeData为对象变更的属性 + this.distributedObject.on("change", this.#callback); +} +``` + + ### 6)监听分布式对象的上下线的状态变更 + +```js +setStatusCallback(callback) { + if (this.#statusCallback == callback) { + return; + } + if (this.#statusCallback != undefined) { + // 删除对象的原始上下线监听 + this.distributedObject.off("status", this.#statusCallback); + } + this.#statusCallback = callback; + // 监听对象状态的变更,其入参为 type和callback + // type 是'status'时,表示上下先状态更改监听 + // callback是变更时触发的回调,其回调参数sessionId标识变更对象的sessionId,networkId标识对象设备的networkId,status标识对象为'online'(上线)或'offline'(下线)的状态 + this.distributedObject.on("status", this.#statusCallback); +} +``` + + ## **应用示例** + +• 在应用首页,详情页面 和新增页面对应的js文件中导入创建的分布式对象类。 + +```js +import * as distr from '../../../model/DistributedDataModel.js' +``` + +• 对端修改数据时,changeCallback回调触发,依据变更数据changeData和设备组网同步的SessionId ,刷新界面。 + +```js +changeCallback(sessionId, changeData) { + changeData.forEach(element => { + if (element == "documentList") { + // 刷新界面上的备忘录数据列表 + this.dataModel.documentList = distr.g_dataModel.distributedObject.documentList; + } + else if (element == "documentSize") { + let size = distr.g_dataModel.distributedObject.documentSize; + // 刷新界面上列表总数 + this.dataModel.distributedObject.documentSize = size; + } + }); + } +``` + +- 在页面初始化函数中设定监听分布式对象上下线状态回调,进行设备组网检测,并亮灯提示。 +- 在页面初始化函数中设定监听分布式对象的数据变更回调,监听对端设备的数据变更。 + +```js +onInit() { + // 监听对端设备的数据变更 + // 发起方要在changeCallback里刷新界面,则需要将正确的this绑定给changeCallback + distr.g_dataModel.setCallback(this.changeCallback.bind(this)); + // 监听分布式对象的上下线状态 + distr.g_dataModel.setStatusCallback((sessionId, networkId, status) => { + // 刷新红绿灯界面 + if (status == "online") { + this.dataModel.imgSrc = "common/green.png"; + } + else { + this.dataModel.imgSrc = "common/red.png"; + } + }) + } +``` + + ## **应用场景** + +- 当用户进行“添加”数据操作时,获取页面的新添加的数据后,将其转存储于分布式对象的documentList属性中,并依据新的数据容量重新赋值documentSize属性。 + +```js +doAdd: function () { + distr.g_dataModel.add(this.title, this.content); + ... +} +``` + +```js +add(title, content) { + this.documentList = this.distributedObject.documentList; + this.documentList[this.distributedObject.documentSize] = { + index: this.distributedObject.documentSize, title: title, content: content + }; + // 分布式对象的数据变更会自动同步到组网内的可信设备 + this.distributedObject.documentList = this.documentList; + this.distributedObject.documentSize ++; + } +``` +- 当用户查看事件详情,并完成数据修改操作时,应用程序会将用户编辑的该条数据结果更新至分布式对象的documentList属性中。 + +```js +save: function () { + // 使用页面数据更新分布式对象属性数据 + distr.g_dataModel.update(this.editIndex, this.title, this.content); + ... +} +``` + +```js +update(index, title, content) { + this.documentList[index] = { + index: index, title: title, content: content + }; + // 分布式对象的数据变更会自动同步到组网内的可信设备 + this.distributedObject.documentList = this.documentList; +} +``` + +- 当用户在应用首页执行“清空”数据操作时,应用程序则会对分布式对象的documentList和documentSize属性进行重新初始化处理。实现对分布式对象的属性数据更改。 + +```js +clear: function () { + // 触发界面刷新 + this.dataModel.documentList = []; + distr.g_dataModel.clear(); + this.dataModel.distributedObject.documentSize = 0; + }, +``` + +```js +clear() { + this.documentList = []; + // 分布式对象的数据变更会自动同步到组网内的可信设备 + this.distributedObject.documentList = this.documentList; + this.distributedObject.documentSize = 0; +} +``` \ No newline at end of file diff --git a/data_object/samples/distributedNotepad/build.gradle b/data_object/samples/distributedNotepad/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..7a297a28053a479e17696179f1fd579a356b1069 --- /dev/null +++ b/data_object/samples/distributedNotepad/build.gradle @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +apply plugin: 'com.huawei.ohos.app' + +// For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510 +ohos { + compileSdkVersion 8 + supportSystem "standard" +} + +buildscript { + repositories { + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + } + dependencies { + classpath 'com.huawei.ohos:hap:3.0.5.2' + classpath 'com.huawei.ohos:decctest:1.2.7.2' + } +} + +allprojects { + repositories { + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + } +} diff --git a/data_object/samples/distributedNotepad/entry/build.gradle b/data_object/samples/distributedNotepad/entry/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..ba57263b838ef2669ed864c0bb68dcc054098c8f --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/build.gradle @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +apply plugin: 'com.huawei.ohos.hap' + +ohos { + compileSdkVersion 8 + defaultConfig { + compatibleSdkVersion 7 + } + buildTypes { + release { + proguardOpt { + proguardEnabled false + rulesFiles 'proguard-rules.pro' + } + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.har']) + testImplementation 'junit:junit:4.13.1' +} diff --git a/data_object/samples/distributedNotepad/entry/src/main/config.json b/data_object/samples/distributedNotepad/entry/src/main/config.json new file mode 100644 index 0000000000000000000000000000000000000000..0d57e04e0d0eb16c41834bbac8e9f1719ebcae54 --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/config.json @@ -0,0 +1,70 @@ +{ + "app": { + "bundleName": "com.example.distributedNotepad.hmservice", + "vendor": "example", + "version": { + "code": 1000000, + "name": "1.0.0" + } + }, + "deviceConfig": {}, + "module": { + "package": "com.example.distributedNotepad", + "name": ".MyApplication", + "mainAbility": ".MainAbility", + "srcPath": "", + "deviceType": [ + "phone" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry", + "installationFree": true + }, + "abilities": [ + { + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "orientation": "unspecified", + "visible": true, + "srcPath": "MainAbility", + "name": ".MainAbility", + "srcLanguage": "js", + "icon": "$media:icon", + "description": "$string:description_mainability", + "formsEnabled": false, + "label": "$string:entry_MainAbility", + "type": "page", + "launchType": "standard" + } + ], + "reqPermissions": [ + { + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + } + ], + "js": [ + { + "pages": [ + "pages/index/index", + "pages/add/add", + "pages/detail/detail" + ], + "name": ".MainAbility", + "window": { + "designWidth": 720, + "autoDesignWidth": false + } + } + ] + } +} \ No newline at end of file diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/app.js b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/app.js new file mode 100644 index 0000000000000000000000000000000000000000..e3a0891c0350ac6dec03b34963b6e570653355f2 --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/app.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + onCreate() { + console.info("Application onCreate"); + }, + onDestroy() { + console.info("Application onDestroy"); + } +}; diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/common/green.png b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/common/green.png new file mode 100644 index 0000000000000000000000000000000000000000..62c8974e7960ec04a24e46d8dfeedfeb56a21f9c Binary files /dev/null and b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/common/green.png differ diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/common/red.png b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/common/red.png new file mode 100644 index 0000000000000000000000000000000000000000..939c4abf1c58c3b9e9adca93c4ce1e53e11f84fa Binary files /dev/null and b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/common/red.png differ diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/i18n/en-US.json b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/i18n/en-US.json new file mode 100644 index 0000000000000000000000000000000000000000..f7e008ec1f3bf1364acdf5656213afa9fe9c3772 --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/i18n/en-US.json @@ -0,0 +1,8 @@ +{ + "strings": { + "joinSuccess": "join success", + "repeatJoin": "has joined" + }, + "Files": { + } +} \ No newline at end of file diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/i18n/zh-CN.json b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/i18n/zh-CN.json new file mode 100644 index 0000000000000000000000000000000000000000..024e1654a824e45b09cb66431ac9d353bb2952ad --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/i18n/zh-CN.json @@ -0,0 +1,8 @@ +{ + "strings": { + "joinSuccess": "加入组网成功", + "repeatJoin": "已经加入过" + }, + "Files": { + } +} \ No newline at end of file diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/add/add.css b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/add/add.css new file mode 100644 index 0000000000000000000000000000000000000000..73fb19f80d50559c06c323bc0acbb6dc3645fd9d --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/add/add.css @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.container { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + left: 0px; + top: 0px; + width: 100%; + height: 100%; +} + +.title { + font-size: 60px; + text-align: center; + width: 100%; + height: 40%; + margin: 10px; +} + +.btn { + width: 50%; + height: 100px; + font-size: 40px; +} diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/add/add.hml b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/add/add.hml new file mode 100644 index 0000000000000000000000000000000000000000..9c83f71a611358383932210ce77490578c6579ec --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/add/add.hml @@ -0,0 +1,27 @@ + + +
+ 添加条目 +
+ 标题 + {{ title }} +
+
+ 内容 + {{ content }} +
+ 确定 +
diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/add/add.js b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/add/add.js new file mode 100644 index 0000000000000000000000000000000000000000..1dcc4d7aea1dd7cb4c506275091f82119914036c --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/add/add.js @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import router from '@system.router' +import * as distr from '../../../model/DistributedDataModel.js' +export default { + data: { + title: "天气不错奥", + content: "今天天气不错" + }, + onInit() { + console.info("objectstore in add page"); + }, + doAdd: function () { + console.info("doAdd " + JSON.stringify(distr.g_dataModel)); + distr.g_dataModel.add(this.title, this.content); + router.replace({ + uri: "pages/index/index", + params: { + dataModel: distr.g_dataModel + } + }) + }, + changeTitle: function (e) { + this.title = e.text; + }, + changeContent: function (e) { + this.content = e.text; + } +} diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/detail/detail.css b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/detail/detail.css new file mode 100644 index 0000000000000000000000000000000000000000..73fb19f80d50559c06c323bc0acbb6dc3645fd9d --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/detail/detail.css @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.container { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + left: 0px; + top: 0px; + width: 100%; + height: 100%; +} + +.title { + font-size: 60px; + text-align: center; + width: 100%; + height: 40%; + margin: 10px; +} + +.btn { + width: 50%; + height: 100px; + font-size: 40px; +} diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/detail/detail.hml b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/detail/detail.hml new file mode 100644 index 0000000000000000000000000000000000000000..fad308f36b526e9ab86944d06a6a42456e423d8d --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/detail/detail.hml @@ -0,0 +1,28 @@ + + +
+ 备忘录详情 +
+ {{ title }} +
+
+ {{ content }} +
+
+ {{edit}} + 返回 +
+
diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/detail/detail.js b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/detail/detail.js new file mode 100644 index 0000000000000000000000000000000000000000..461f1798e8fa629c68e36587ef067ef67184663e --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/detail/detail.js @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import router from '@system.router' +import * as distr from '../../../model/DistributedDataModel.js' + +export default { + data: { + title: "天气不错奥", + content: "今天天气不错", + edit: "保存" + }, + onInit() { + console.info("objectstore in detail page"); + }, + back: function () { + router.replace({ + uri: "pages/index/index", + params: { + dataModel: distr.g_dataModel + } + }) + }, + change: function (e) { + this.title = e.text; + }, + changeContent: function (e) { + this.content = e.text; + }, + save: function () { + console.info("start save "+ JSON.stringify(this.data)); + distr.g_dataModel.update(this.editIndex, this.title, this.content); + router.replace({ + uri: "pages/index/index", + params: { + dataModel: distr.g_dataModel + } + }) + } +} diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/index/index.css b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/index/index.css new file mode 100644 index 0000000000000000000000000000000000000000..d5d35b9c277b5e3a770976a61012b05f5b6a3363 --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/index/index.css @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.container { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + background-color: aqua; +} + +.documents { + width: 100%; + height: 100%; +} + +.title { + font-size: 60px; + text-align: center; + width: 100%; + height: 40%; + margin: 10px; +} + +.list_item { + font-size: 30px; + text-align: left; + margin: 10px; +} + +.btn { + width: 50%; + height: 100px; + font-size: 40px; +} + +.dialog-main { + width: 500px; +} + +.dialog-div { + flex-direction: column; + align-items: center; +} + +.dialog_title_text { + width: 434px; + height: 80px; + font-size: 32px; + font-weight: 600; +} +.dialog_cancel_button { + width: 100%; + font-size: 32px; +} + +.dialog_device_list { + width: 434px; + max-height: 150px; +} +.device_list_item { + width: 434px; + height: 80px; + flex-direction: row; + align-items: center; +} + +.device_item_radio { +} + +.device_item_title { + width: 80%; + height: 80px; + text-align: start; +} \ No newline at end of file diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/index/index.hml b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/index/index.hml new file mode 100644 index 0000000000000000000000000000000000000000..f6f13507d1a4581312c6e9a7557a148e8c68e844 --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/index/index.hml @@ -0,0 +1,39 @@ + + +
+
+ 备忘录 + + +
+ + +
+ {{ $item.title }} + {{ $item.content }} +
+
+
+
+ 总数: + {{dataModel.distributedObject.documentSize}} +
+
+ 添加 + 清空 +
+
diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/index/index.js b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/index/index.js new file mode 100644 index 0000000000000000000000000000000000000000..0818c2968a644129f92346bebed99764e1dc1a8d --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/MainAbility/pages/index/index.js @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import router from '@system.router' +import * as distr from '../../../model/DistributedDataModel.js' + +export default { + data: { + dataModel: distr.g_dataModel + }, + changeCallback(sessionId, changeData) { + changeData.forEach(element => { + if (element == "documentList") { + console.info("newest data " + JSON.stringify(this.dataModel.distributedObject.documentList)); + // 触发界面刷新 + this.dataModel.documentList = distr.g_dataModel.distributedObject.documentList; + } else if (element == "documentSize") { + let size = distr.g_dataModel.distributedObject.documentSize; + console.info("newest size " + size); + // 触发界面刷新 + this.dataModel.distributedObject.documentSize = size; + } + }); + }, + onInit() { + console.info("objectstore in index page "); + console.info(JSON.stringify(this.dataModel.documentList)); + console.info(JSON.stringify(distr.g_dataModel.distributedObject.documentList)); + distr.g_dataModel.setCallback(this.changeCallback.bind(this)); + distr.g_dataModel.setStatusCallback((sessionId, networkId, status) => { + console.info("objectstore status change ${networkId} ${status}"); + if (status == "online") { + this.dataModel.imgSrc = "common/green.png"; + } else { + this.dataModel.imgSrc = "common/red.png"; + } + }) + }, + onDestroy() { + console.info("objectstore exit index page"); + distr.g_dataModel.clearCallback(); + }, + add: function () { + router.replace({ + uri: "pages/add/add" + }) + }, + clear: function () { + // 触发界面刷新 + this.dataModel.documentList = []; + this.dataModel.distributedObject.documentSize = 0; + distr.g_dataModel.clear(); + }, + detail: function (msg) { + router.replace({ + uri: "pages/detail/detail", + params: { + title: msg.target.dataSet.title, + content: msg.target.dataSet.content, + oriTitle: msg.target.dataSet.title, + oriContent: msg.target.dataSet.content, + editIndex: msg.target.dataSet.index + } + }) + } +} + + + diff --git a/data_object/samples/distributedNotepad/entry/src/main/js/model/DistributedDataModel.js b/data_object/samples/distributedNotepad/entry/src/main/js/model/DistributedDataModel.js new file mode 100644 index 0000000000000000000000000000000000000000..9881d32c38a625457277a849035b037eab3d13f5 --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/js/model/DistributedDataModel.js @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import distributedObject from '@ohos.data.distributedDataObject' +import featureAbility from '@ohos.ability.featureAbility'; + +function grantPermission() { + console.info('grantPermission'); + let context = featureAbility.getContext(); + context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666, function (result) { + console.info(`result.requestCode=${result.requestCode}`) + + }) + console.info('end grantPermission'); +} +export default class DistributedDataModel { + documentList = []; + distributedObject; // distributed proxy + imgSrc = "common/red.png"; + #callback; + #statusCallback; + + constructor() { + this.distributedObject = distributedObject.createDistributedObject({ + documentList: this.documentList, + documentSize: 0 + }); + this.share(); + } + + clearCallback() { + this.distributedObject.off("change"); + this.#callback = undefined; + this.distributedObject.off("status"); + this.#statusCallback = undefined; + } + + setCallback(callback) { + if (this.#callback == callback) { + console.info("same callback"); + return; + } + console.info("start off"); + if (this.#callback != undefined) { + this.distributedObject.off("change", this.#callback); + } + this.#callback = callback; + console.info("start watch change"); + this.distributedObject.on("change", this.#callback); + } + + setStatusCallback(callback) { + if (this.#statusCallback == callback) { + console.info("same callback"); + return; + } + console.info("start off"); + if (this.#statusCallback != undefined) { + this.distributedObject.off("status", this.#statusCallback); + } + this.#statusCallback = callback; + console.info("start watch change"); + this.distributedObject.on("status", this.#statusCallback); + } + + share() { + console.info("start share"); + if (this.distributedObject.__sessionId == undefined) { + grantPermission() + this.distributedObject.setSessionId("123456") + } + } + + update(index, title, content) { + console.info("doUpdate " + title + index); + this.documentList = this.distributedObject.documentList; + this.documentList[index] = { + index: index, title: title, content: content + }; + this.distributedObject.documentList = this.documentList; + console.info("update my documentList " + JSON.stringify(this.documentList)); + } + + add(title, content) { + console.info("doAdd " + title + content); + console.info("documentList " + JSON.stringify(this.documentList)); + this.documentList = this.distributedObject.documentList; + this.documentList[this.distributedObject.documentSize] = { + index: this.distributedObject.documentSize, title: title, content: content + }; + this.distributedObject.documentList = this.documentList; + this.distributedObject.documentSize++; + console.info("add my documentList " + JSON.stringify(this.documentList)); + } + + + clear() { + console.info("doClear "); + this.documentList = []; + this.distributedObject.documentList = this.documentList; + this.distributedObject.documentSize = 0; + console.info("doClear finish"); + } +} + +export var g_dataModel = new DistributedDataModel(); \ No newline at end of file diff --git a/data_object/samples/distributedNotepad/entry/src/main/resources/base/element/string.json b/data_object/samples/distributedNotepad/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..610d89afcb84069982cf1fd49782a75e2d0ef089 --- /dev/null +++ b/data_object/samples/distributedNotepad/entry/src/main/resources/base/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "entry_MainAbility", + "value": "entry_MainAbility" + }, + { + "name": "description_mainability", + "value": "JS_Empty Ability" + } + ] +} \ No newline at end of file diff --git a/data_object/samples/distributedNotepad/entry/src/main/resources/base/media/icon.png b/data_object/samples/distributedNotepad/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/data_object/samples/distributedNotepad/entry/src/main/resources/base/media/icon.png differ diff --git "a/data_object/samples/distributedNotepad/pictures/\344\270\212\347\272\277\347\212\266\346\200\201\346\217\220\347\244\272.png" "b/data_object/samples/distributedNotepad/pictures/\344\270\212\347\272\277\347\212\266\346\200\201\346\217\220\347\244\272.png" new file mode 100644 index 0000000000000000000000000000000000000000..4b725e338672c08aca38f5eac657ce14483a0362 Binary files /dev/null and "b/data_object/samples/distributedNotepad/pictures/\344\270\212\347\272\277\347\212\266\346\200\201\346\217\220\347\244\272.png" differ diff --git "a/data_object/samples/distributedNotepad/pictures/\344\270\213\347\272\277\347\212\266\346\200\201\346\217\220\347\244\272.png" "b/data_object/samples/distributedNotepad/pictures/\344\270\213\347\272\277\347\212\266\346\200\201\346\217\220\347\244\272.png" new file mode 100644 index 0000000000000000000000000000000000000000..6729341a48896f61111a58465e59b82820d7023a Binary files /dev/null and "b/data_object/samples/distributedNotepad/pictures/\344\270\213\347\272\277\347\212\266\346\200\201\346\217\220\347\244\272.png" differ diff --git "a/data_object/samples/distributedNotepad/pictures/\345\272\224\347\224\250\345\261\225\347\244\272.gif" "b/data_object/samples/distributedNotepad/pictures/\345\272\224\347\224\250\345\261\225\347\244\272.gif" new file mode 100644 index 0000000000000000000000000000000000000000..f5b3fe8c4290c84e7548ac7154abb67a540f3593 Binary files /dev/null and "b/data_object/samples/distributedNotepad/pictures/\345\272\224\347\224\250\345\261\225\347\244\272.gif" differ diff --git "a/data_object/samples/distributedNotepad/pictures/\346\225\260\346\215\256\345\206\205\345\256\271\345\210\227\350\241\250\345\261\225\347\244\272.png" "b/data_object/samples/distributedNotepad/pictures/\346\225\260\346\215\256\345\206\205\345\256\271\345\210\227\350\241\250\345\261\225\347\244\272.png" new file mode 100644 index 0000000000000000000000000000000000000000..b6a84118a70254183d291c607a3af0bdce745f7b Binary files /dev/null and "b/data_object/samples/distributedNotepad/pictures/\346\225\260\346\215\256\345\206\205\345\256\271\345\210\227\350\241\250\345\261\225\347\244\272.png" differ diff --git "a/data_object/samples/distributedNotepad/pictures/\350\256\276\345\244\207\347\273\204\347\275\221.gif" "b/data_object/samples/distributedNotepad/pictures/\350\256\276\345\244\207\347\273\204\347\275\221.gif" new file mode 100644 index 0000000000000000000000000000000000000000..b692cd4e60194df18924bbb8a1c6ae95a32024e9 Binary files /dev/null and "b/data_object/samples/distributedNotepad/pictures/\350\256\276\345\244\207\347\273\204\347\275\221.gif" differ diff --git "a/data_object/samples/distributedNotepad/pictures/\351\246\226\351\241\265\345\261\225\347\244\272.jpg" "b/data_object/samples/distributedNotepad/pictures/\351\246\226\351\241\265\345\261\225\347\244\272.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..f1c583f8db6359446cecec142587f958ee20ae16 Binary files /dev/null and "b/data_object/samples/distributedNotepad/pictures/\351\246\226\351\241\265\345\261\225\347\244\272.jpg" differ diff --git a/data_object/samples/distributedNotepad/settings.gradle b/data_object/samples/distributedNotepad/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..4773db73233a570c2d0c01a22e75321acfbf7a07 --- /dev/null +++ b/data_object/samples/distributedNotepad/settings.gradle @@ -0,0 +1 @@ +include ':entry' diff --git a/data_share/CMakeLists.txt b/data_share/CMakeLists.txt index 39b62fe3e3a0ff111c0735ead28e92dcdbea02a9..014e807462dd9a61f6039732c37f0e2d17a25022 100644 --- a/data_share/CMakeLists.txt +++ b/data_share/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS "-std=c++1y -fno-rtti -fvisibility=default -D_GNU_SOURCE") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -fPIC -fpic -ffunction-sections -D_GLIBC_MOCK") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-as-needed -ldl") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=0") set(MOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../mock) add_definitions(-DNAPI_EXPERIMENTAL) diff --git a/kv_store/CMakeLists.txt b/kv_store/CMakeLists.txt index 6df8e63a8435c0f08bdf790a17a51f3794095919..0350156cabd05e66986d414dea450fd40fc954ae 100644 --- a/kv_store/CMakeLists.txt +++ b/kv_store/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS "-std=c++1y -fno-rtti -fvisibility=default -D_GNU_SOURCE") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -fPIC -fpic -ffunction-sections -D_GLIBC_MOCK") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-as-needed -ldl") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=0") set(MOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../mock) #aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/frameworks/native/kv_store/src/kvstore_common kv_store_src) diff --git a/mock/CMakeLists.txt b/mock/CMakeLists.txt index afb87cf869481db94377ff95a461c8f6dab69d6b..77639735b7f2a3d87161b0acf4781eaef3601406 100644 --- a/mock/CMakeLists.txt +++ b/mock/CMakeLists.txt @@ -7,6 +7,7 @@ set(CMAKE_CXX_FLAGS "-std=c++1y -fno-rtti -fvisibility=default -D_GNU_SOURCE") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fpic -fdata-sections -ffunction-sections -D_GLIBC_MOCK") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-Bsymbolic -Wl,--no-as-needed -ldl") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-macro-redefined -Wno-constant-conversion -Wno-sign-compare") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=0") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types") add_definitions(-DNDEBUG=1 -DHAVE_USLEEP=1 -DSQLITE_HAVE_ISNAN -DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576) add_definitions(-DSQLITE_THREADSAFE=2 -DSQLITE_TEMP_STORE=3 -DSQLITE_POWERSAFE_OVERWRITE=1) @@ -21,7 +22,6 @@ add_definitions(-DL_ENDIAN -DUNICODE -D_UNICODE -DWIN32_LEAN_AND_MEAN -D_MT -DWI add_definitions(-DNAPI_EXPERIMENTAL -DSQLITE_DISTRIBUTE_RELATIONAL) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src mockSrc) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/sqlite/src mockSrc) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/napi) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/napi/src) diff --git a/mock/distributeddb/BUILD.gn b/mock/distributeddb/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..deb0bbbe346b986d0c8dd4d8dcde83a1096f059d --- /dev/null +++ b/mock/distributeddb/BUILD.gn @@ -0,0 +1,280 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import("//build/ohos.gni") + +config("distrdb_config") { + visibility = [ ":*" ] + include_dirs = [ + "include", + "interfaces/include", + "interfaces/src", + "interfaces/src/relational", + "common/include", + "common/include/relational", + "communicator/include", + "storage/include", + "storage/src", + "storage/src/multiver", + "storage/src/operation", + "storage/src/sqlite", + "storage/src/sqlite/relational", + "storage/src/upgrader", + "syncer/include", + "syncer/src", + "//third_party/openssl/include/", + ] + + defines = [ + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + "USE_DFX_ABILITY", + ] + if (is_debug) { + defines += [ "TRACE_SQLITE_EXECUTE" ] + } +} + +config("distrdb_public_config") { + visibility = [ "*:*" ] + include_dirs = [ + "interfaces/include", + "interfaces/include/relational", + "include", + ] +} + +group("build_module") { + deps = [ ":distributeddb" ] +} + +ohos_shared_library("distributeddb") { + sources = [ + "common/src/auto_launch.cpp", + "common/src/data_compression.cpp", + "common/src/data_value.cpp", + "common/src/db_common.cpp", + "common/src/db_constant.cpp", + "common/src/db_dfx_adapter.cpp", + "common/src/db_dump_helper.cpp", + "common/src/evloop/src/event_impl.cpp", + "common/src/evloop/src/event_loop_epoll.cpp", + "common/src/evloop/src/event_loop_impl.cpp", + "common/src/evloop/src/event_loop_select.cpp", + "common/src/evloop/src/ievent.cpp", + "common/src/evloop/src/ievent_loop.cpp", + "common/src/flatbuffer_schema.cpp", + "common/src/hash.cpp", + "common/src/json_object.cpp", + "common/src/lock_status_observer.cpp", + "common/src/log_print.cpp", + "common/src/notification_chain.cpp", + "common/src/param_check_utils.cpp", + "common/src/parcel.cpp", + "common/src/performance_analysis.cpp", + "common/src/platform_specific.cpp", + "common/src/query.cpp", + "common/src/query_expression.cpp", + "common/src/ref_object.cpp", + "common/src/relational/relational_schema_object.cpp", + "common/src/runtime_context.cpp", + "common/src/runtime_context_impl.cpp", + "common/src/schema_constant.cpp", + "common/src/schema_negotiate.cpp", + "common/src/schema_object.cpp", + "common/src/schema_utils.cpp", + "common/src/semaphore_utils.cpp", + "common/src/task_pool.cpp", + "common/src/task_pool_impl.cpp", + "common/src/task_queue.cpp", + "common/src/time_tick_monitor.cpp", + "common/src/types_export.cpp", + "common/src/user_change_monitor.cpp", + "common/src/value_object.cpp", + "common/src/zlib_compression.cpp", + "communicator/src/combine_status.cpp", + "communicator/src/communicator.cpp", + "communicator/src/communicator_aggregator.cpp", + "communicator/src/communicator_linker.cpp", + "communicator/src/frame_combiner.cpp", + "communicator/src/frame_retainer.cpp", + "communicator/src/header_converter.cpp", + "communicator/src/message_transform.cpp", + "communicator/src/network_adapter.cpp", + "communicator/src/protocol_proto.cpp", + "communicator/src/send_task_scheduler.cpp", + "communicator/src/serial_buffer.cpp", + "interfaces/src/intercepted_data_impl.cpp", + "interfaces/src/kv_store_changed_data_impl.cpp", + "interfaces/src/kv_store_delegate_impl.cpp", + "interfaces/src/kv_store_delegate_manager.cpp", + "interfaces/src/kv_store_errno.cpp", + "interfaces/src/kv_store_nb_conflict_data_impl.cpp", + "interfaces/src/kv_store_nb_delegate_impl.cpp", + "interfaces/src/kv_store_result_set_impl.cpp", + "interfaces/src/kv_store_snapshot_delegate_impl.cpp", + "interfaces/src/relational/relational_store_changed_data_impl.cpp", + "interfaces/src/relational/relational_store_delegate_impl.cpp", + "interfaces/src/relational/relational_store_manager.cpp", + "interfaces/src/relational/relational_store_sqlite_ext.cpp", + "interfaces/src/relational/runtime_config.cpp", + "storage/src/data_transformer.cpp", + "storage/src/db_properties.cpp", + "storage/src/default_factory.cpp", + "storage/src/generic_kvdb.cpp", + "storage/src/generic_kvdb_connection.cpp", + "storage/src/generic_single_ver_kv_entry.cpp", + "storage/src/iconnection.cpp", + "storage/src/ikvdb_factory.cpp", + "storage/src/kvdb_commit_notify_filterable_data.cpp", + "storage/src/kvdb_manager.cpp", + "storage/src/kvdb_observer_handle.cpp", + "storage/src/kvdb_properties.cpp", + "storage/src/kvdb_utils.cpp", + "storage/src/kvdb_windowed_result_set.cpp", + "storage/src/multiver/generic_multi_ver_kv_entry.cpp", + "storage/src/multiver/multi_ver_commit.cpp", + "storage/src/multiver/multi_ver_kvdata_storage.cpp", + "storage/src/multiver/multi_ver_natural_store.cpp", + "storage/src/multiver/multi_ver_natural_store_commit_notify_data.cpp", + "storage/src/multiver/multi_ver_natural_store_commit_storage.cpp", + "storage/src/multiver/multi_ver_natural_store_connection.cpp", + "storage/src/multiver/multi_ver_natural_store_snapshot.cpp", + "storage/src/multiver/multi_ver_natural_store_transfer_data.cpp", + "storage/src/multiver/multi_ver_storage_engine.cpp", + "storage/src/multiver/multi_ver_storage_executor.cpp", + "storage/src/multiver/multi_ver_vacuum.cpp", + "storage/src/multiver/multi_ver_vacuum_executor_impl.cpp", + "storage/src/multiver/multi_ver_value_object.cpp", + "storage/src/operation/database_oper.cpp", + "storage/src/operation/local_database_oper.cpp", + "storage/src/operation/multi_ver_database_oper.cpp", + "storage/src/operation/single_ver_database_oper.cpp", + "storage/src/package_file.cpp", + "storage/src/relational_store_connection.cpp", + "storage/src/relational_store_instance.cpp", + "storage/src/relational_sync_able_storage.cpp", + "storage/src/relationaldb_properties.cpp", + "storage/src/result_entries_window.cpp", + "storage/src/single_ver_natural_store_commit_notify_data.cpp", + "storage/src/sqlite/query_object.cpp", + "storage/src/sqlite/query_sync_object.cpp", + "storage/src/sqlite/relational/sqlite_relational_store.cpp", + "storage/src/sqlite/relational/sqlite_relational_store_connection.cpp", + "storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp", + "storage/src/sqlite/sqlite_local_kvdb.cpp", + "storage/src/sqlite/sqlite_local_kvdb_connection.cpp", + "storage/src/sqlite/sqlite_local_kvdb_snapshot.cpp", + "storage/src/sqlite/sqlite_local_storage_engine.cpp", + "storage/src/sqlite/sqlite_local_storage_executor.cpp", + "storage/src/sqlite/sqlite_multi_ver_data_storage.cpp", + "storage/src/sqlite/sqlite_multi_ver_transaction.cpp", + "storage/src/sqlite/sqlite_query_helper.cpp", + "storage/src/sqlite/sqlite_single_ver_continue_token.cpp", + "storage/src/sqlite/sqlite_single_ver_database_upgrader.cpp", + "storage/src/sqlite/sqlite_single_ver_forward_cursor.cpp", + "storage/src/sqlite/sqlite_single_ver_natural_store.cpp", + "storage/src/sqlite/sqlite_single_ver_natural_store_connection.cpp", + "storage/src/sqlite/sqlite_single_ver_relational_continue_token.cpp", + "storage/src/sqlite/sqlite_single_ver_relational_storage_executor.cpp", + "storage/src/sqlite/sqlite_single_ver_result_set.cpp", + "storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.cpp", + "storage/src/sqlite/sqlite_single_ver_storage_engine.cpp", + "storage/src/sqlite/sqlite_single_ver_storage_executor.cpp", + "storage/src/sqlite/sqlite_single_ver_storage_executor_cache.cpp", + "storage/src/sqlite/sqlite_single_ver_storage_executor_subscribe.cpp", + "storage/src/sqlite/sqlite_storage_engine.cpp", + "storage/src/sqlite/sqlite_storage_executor.cpp", + "storage/src/sqlite/sqlite_utils.cpp", + "storage/src/storage_engine.cpp", + "storage/src/storage_engine_manager.cpp", + "storage/src/storage_executor.cpp", + "storage/src/sync_able_engine.cpp", + "storage/src/sync_able_kvdb.cpp", + "storage/src/sync_able_kvdb_connection.cpp", + "storage/src/upgrader/single_ver_database_upgrader.cpp", + "storage/src/upgrader/single_ver_schema_database_upgrader.cpp", + "syncer/src/ability_sync.cpp", + "syncer/src/commit_history_sync.cpp", + "syncer/src/communicator_proxy.cpp", + "syncer/src/db_ability.cpp", + "syncer/src/device_manager.cpp", + "syncer/src/generic_syncer.cpp", + "syncer/src/meta_data.cpp", + "syncer/src/multi_ver_data_sync.cpp", + "syncer/src/multi_ver_sync_engine.cpp", + "syncer/src/multi_ver_sync_state_machine.cpp", + "syncer/src/multi_ver_sync_task_context.cpp", + "syncer/src/multi_ver_syncer.cpp", + "syncer/src/query_sync_water_mark_helper.cpp", + "syncer/src/single_ver_data_message_schedule.cpp", + "syncer/src/single_ver_data_packet.cpp", + "syncer/src/single_ver_data_sync.cpp", + "syncer/src/single_ver_data_sync_utils.cpp", + "syncer/src/single_ver_kv_sync_task_context.cpp", + "syncer/src/single_ver_kv_syncer.cpp", + "syncer/src/single_ver_relational_sync_task_context.cpp", + "syncer/src/single_ver_relational_syncer.cpp", + "syncer/src/single_ver_serialize_manager.cpp", + "syncer/src/single_ver_sync_engine.cpp", + "syncer/src/single_ver_sync_state_machine.cpp", + "syncer/src/single_ver_sync_target.cpp", + "syncer/src/single_ver_sync_task_context.cpp", + "syncer/src/single_ver_syncer.cpp", + "syncer/src/subscribe_manager.cpp", + "syncer/src/sync_config.cpp", + "syncer/src/sync_engine.cpp", + "syncer/src/sync_operation.cpp", + "syncer/src/sync_state_machine.cpp", + "syncer/src/sync_target.cpp", + "syncer/src/sync_task_context.cpp", + "syncer/src/syncer_factory.cpp", + "syncer/src/syncer_proxy.cpp", + "syncer/src/time_helper.cpp", + "syncer/src/time_sync.cpp", + "syncer/src/value_slice_sync.cpp", + ] + + configs = [ ":distrdb_config" ] + public_configs = [ ":distrdb_public_config" ] + + deps = [ + "//third_party/sqlite:sqlite", + "//third_party/zlib:libz", + "//utils/native/base:utils", + ] + + configs += [ "//third_party/jsoncpp:jsoncpp_config" ] + ldflags = [ "-Wl,--exclude-libs,ALL" ] + deps += [ + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + ] + + external_deps = [ + "hisysevent_native:libhisysevent", + "hitrace_native:hitrace_meter", + "hiviewdfx_hilog_native:libhilog", + ] + + subsystem_name = "distributeddatamgr" + part_name = "distributeddatamgr" +} diff --git a/mock/distributeddb/CMakeLists.txt b/mock/distributeddb/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9a3901b134815a8aac9641d14c023f09572b8a62 --- /dev/null +++ b/mock/distributeddb/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.10.2) +project(libs) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -fno-rtti -fvisibility=default -D_GNU_SOURCE") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -fPIC -fpic -ffunction-sections -D_GLIBC_MOCK") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-as-needed -ldl") +set(MOCK_DIR ../../mock) +add_definitions(-DUSE_HARMONY_SQLITE -DUSING_HILOG_LOGGER -DOMIT_FLATBUFFER -DEVLOOP_TIMER_ONLY) #-DOMIT_JSON +add_definitions(-DUSING_DB_JSON_EXTRACT_AUTOMATICALLY -DSQLITE_ENABLE_JSON1 -DRELATIONAL_STORE -DOMIT_ZLIB) +add_definitions(-DWINDOWS_PLATFORM -DSQLITE_HAS_CODEC -DJSONCPP_USE_BUILDER -DEKEYREVOKED=128) +add_definitions(-Dfseeko64=fseeko) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/common/src libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/common/src/evloop/src libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/common/src/relational libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/communicator/src libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/interfaces/src libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/interfaces/src/relational libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/storage/src libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/multiver libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/operation libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/sqlite libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/sqlite/relational libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/upgrader libsSrc) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/syncer/src libsSrc) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces/include/relational) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces/src) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces/src/relational) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common/include/relational) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/storage/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/storage/src) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/multiver) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/sqlite) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/sqlite/relational) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/operation) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/storage/src/upgrader) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/syncer/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/syncer/src) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/communicator/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../utils_native/base/include) +include_directories(/usr/include/jsoncpp) +include_directories(${MOCK_DIR}) +include_directories(${MOCK_DIR}/sqlite/include) +include_directories(${MOCK_DIR}/innerkits/hilog_native/libhilog/include) + +set(links secure mock jsoncpp crypto) +add_library(libs SHARED ${libsSrc}) +target_link_libraries(libs ${links}) diff --git a/mock/distributeddb/common/include/auto_launch.h b/mock/distributeddb/common/include/auto_launch.h new file mode 100644 index 0000000000000000000000000000000000000000..0aa1bcb818eb3f6b92016ef8e61693d021740d84 --- /dev/null +++ b/mock/distributeddb/common/include/auto_launch.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUTO_LAUNCH_H +#define AUTO_LAUNCH_H + +#include +#include +#include +#include "auto_launch_export.h" +#include "db_properties.h" +#include "ikvdb_connection.h" +#include "icommunicator_aggregator.h" +#include "kv_store_observer.h" +#include "kvdb_properties.h" +#include "types_export.h" +#include "relational_store_connection.h" +#include "relationaldb_properties.h" +#include "store_observer.h" + +namespace DistributedDB { +enum class AutoLaunchItemState { + UN_INITIAL = 0, + IN_ENABLE, + IN_LIFE_CYCLE_CALL_BACK, // in LifeCycleCallback + IN_COMMUNICATOR_CALL_BACK, // in OnConnectCallback or CommunicatorLackCallback + IDLE, +}; + +enum class DBType { + DB_KV = 0, + DB_RELATION, + DB_INVALID, +}; + +struct AutoLaunchItem { + std::shared_ptr propertiesPtr; + AutoLaunchNotifier notifier; + KvStoreObserver *observer = nullptr; + int conflictType = 0; + KvStoreNbConflictNotifier conflictNotifier; + void *conn = nullptr; + KvDBObserverHandle *observerHandle = nullptr; + bool isWriteOpenNotified = false; + AutoLaunchItemState state = AutoLaunchItemState::UN_INITIAL; + bool isDisable = false; + bool inObserver = false; + bool isAutoSync = true; + DBType type = DBType::DB_INVALID; + StoreObserver *storeObserver = nullptr; +}; + +class AutoLaunch { +public: + static int GetAutoLaunchProperties(const AutoLaunchParam ¶m, const DBType &openType, bool checkDir, + std::shared_ptr &propertiesPtr); + + AutoLaunch() = default; + + virtual ~AutoLaunch(); + + DISABLE_COPY_ASSIGN_MOVE(AutoLaunch); + + void SetCommunicatorAggregator(ICommunicatorAggregator *aggregator); + + int EnableKvStoreAutoLaunch(const KvDBProperties &properties, AutoLaunchNotifier notifier, + const AutoLaunchOption &option); + + int DisableKvStoreAutoLaunch(const std::string &normalIdentifier, const std::string &dualTupleIdentifier, + const std::string &userId); + + void GetAutoLaunchSyncDevices(const std::string &identifier, std::vector &devices) const; + + void SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback, DBType type); + + void Dump(int fd); + +protected: + static int OpenOneConnection(AutoLaunchItem &autoLaunchItem); + + // we will return errCode, if errCode != E_OK + static int CloseConnectionStrict(AutoLaunchItem &autoLaunchItem); + + static void CloseNotifier(const AutoLaunchItem &autoLaunchItem); + + static int SetConflictNotifier(AutoLaunchItem &autoLaunchItem); + + static int GetAutoLaunchKVProperties(const AutoLaunchParam ¶m, + const std::shared_ptr &propertiesPtr, bool checkDir); + + static int GetAutoLaunchRelationProperties(const AutoLaunchParam ¶m, + const std::shared_ptr &propertiesPtr); + + static int OpenKvConnection(AutoLaunchItem &autoLaunchItem); + + static int OpenRelationalConnection(AutoLaunchItem &autoLaunchItem); + + static int PragmaAutoSync(AutoLaunchItem &autoLaunchItem); + + int EnableKvStoreAutoLaunchParmCheck(AutoLaunchItem &autoLaunchItem, const std::string &normalIdentifier, + const std::string &dualTupleIdentifier, bool isDualTupleMode); + + int GetKVConnectionInEnable(AutoLaunchItem &autoLaunchItem, const std::string &identifier); + + // before ReleaseDatabaseConnection, if errCode != E_OK, we not return, we try close more + virtual void TryCloseConnection(AutoLaunchItem &autoLaunchItem); + + int RegisterObserverAndLifeCycleCallback(AutoLaunchItem &autoLaunchItem, const std::string &identifier, + bool isExt); + + int RegisterObserver(AutoLaunchItem &autoLaunchItem, const std::string &identifier, bool isExt); + + void ObserverFunc(const KvDBCommitNotifyData ¬ifyData, const std::string &identifier, + const std::string &userId); + + void ConnectionLifeCycleCallbackTask(const std::string &identifier, const std::string &userId); + + void OnlineCallBackTask(); + + void GetDoOpenMap(std::map> &doOpenMap); + + void GetConnInDoOpenMap(std::map> &doOpenMap); + + void UpdateGlobalMap(std::map> &doOpenMap); + + void ReceiveUnknownIdentifierCallBackTask(const std::string &identifier, const std::string &userId); + + void ConnectionLifeCycleCallback(const std::string &identifier, const std::string &userId); + + void OnlineCallBack(const std::string &device, bool isConnect); + + int ReceiveUnknownIdentifierCallBack(const LabelType &label, const std::string &originalUserId); + + int AutoLaunchExt(const std::string &identifier, const std::string &userId); + + void AutoLaunchExtTask(const std::string &identifier, const std::string &userId, AutoLaunchItem &autoLaunchItem); + + void ExtObserverFunc(const KvDBCommitNotifyData ¬ifyData, const std::string &identifier, + const std::string &userId); + + void ExtConnectionLifeCycleCallback(const std::string &identifier, const std::string &userId); + + void ExtConnectionLifeCycleCallbackTask(const std::string &identifier, const std::string &userId); + + int ExtAutoLaunchRequestCallBack(const std::string &identifier, AutoLaunchParam ¶m, DBType &openType); + + int RegisterLifeCycleCallback(AutoLaunchItem &autoLaunchItem, const std::string &identifier, bool isExt); + + void TryCloseKvConnection(AutoLaunchItem &autoLaunchItem); + + void TryCloseRelationConnection(AutoLaunchItem &autoLaunchItem); + + void EraseAutoLauchItem(const std::string &identifier, const std::string &userId); + + void NotifyInvalidParam(const AutoLaunchItem &autoLaunchItem); + + int CheckAutoLaunchRealPath(const AutoLaunchItem &autoLaunchItem); + + mutable std::mutex dataLock_; + mutable std::mutex communicatorLock_; + std::set onlineDevices_; + // key: label, value: + std::map> autoLaunchItemMap_; + ICommunicatorAggregator *communicatorAggregator_ = nullptr; + std::condition_variable cv_; + + std::mutex extLock_; + std::map autoLaunchRequestCallbackMap_; + // key: label, value: + std::map> extItemMap_; +}; +} // namespace DistributedDB +#endif // AUTO_LAUNCH_H diff --git a/mock/distributeddb/common/include/data_compression.h b/mock/distributeddb/common/include/data_compression.h new file mode 100644 index 0000000000000000000000000000000000000000..449fe7a37ccec50fb88299725722f7854f78e942 --- /dev/null +++ b/mock/distributeddb/common/include/data_compression.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef DATA_COMPRESSION_H +#define DATA_COMPRESSION_H + +#include +#include +#include + +#include "types_export.h" + +namespace DistributedDB { +class DataCompression { +public: + static DataCompression *GetInstance(CompressAlgorithm algo); + static void GetCompressionAlgo(std::set &algorithmSet); + static int TransferCompressionAlgo(uint32_t compressAlgoType, CompressAlgorithm &algoType); + + virtual int Compress(const std::vector &srcData, std::vector &destData) const = 0; + virtual int Uncompress(const std::vector &srcData, std::vector &destData, uint32_t destLen) + const = 0; + +protected: + DataCompression() = default; + virtual ~DataCompression() = default; + DataCompression(const DataCompression& compression) = delete; + DataCompression& operator= (const DataCompression& compression) = delete; + + static void Register(CompressAlgorithm algo, DataCompression *compression); + +private: + static std::map &GetCompressionAlgos(); + static std::map &GetTransMap(); +}; +} // namespace DistributedDB +#endif // DATA_COMPRESSION_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/data_value.h b/mock/distributeddb/common/include/data_value.h new file mode 100644 index 0000000000000000000000000000000000000000..f355f7f55c2e251a10f49246c23c4fd406632cfa --- /dev/null +++ b/mock/distributeddb/common/include/data_value.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTED_DB_DATA_VALUE_H +#define DISTRIBUTED_DB_DATA_VALUE_H + +#include +#include +#include +#include +namespace DistributedDB { +// field types stored in sqlite +enum class StorageType { + STORAGE_TYPE_NONE = 0, + STORAGE_TYPE_NULL, + STORAGE_TYPE_INTEGER, + STORAGE_TYPE_REAL, + STORAGE_TYPE_TEXT, + STORAGE_TYPE_BLOB +}; + +class Blob { +public: + Blob(); + ~Blob(); + + Blob(Blob &&); + Blob(const Blob &) = delete; + Blob &operator=(Blob &&) noexcept; + Blob &operator=(const Blob &) = delete; + + const uint8_t* GetData() const; + uint32_t GetSize() const; + + int WriteBlob(const uint8_t *ptrArray, const uint32_t &size); + +private: + uint8_t* ptr_; + uint32_t size_; +}; + +class DataValue { +public: + DataValue(); + ~DataValue(); + + // copy constructor + DataValue(const DataValue &dataValue); + DataValue &operator=(const DataValue &dataValue); + // move constructor + DataValue(DataValue &&dataValue) noexcept; + DataValue &operator=(DataValue &&dataValue) noexcept; + DataValue &operator=(int64_t intVal); + DataValue &operator=(double doubleVal); + DataValue &operator=(const Blob &blob); + DataValue &operator=(const std::string &string); + int Set(Blob *&blob); + + // equals + bool operator==(const DataValue &dataValue) const; + bool operator!=(const DataValue &dataValue) const; + + StorageType GetType() const; + int GetInt64(int64_t &outVal) const; + int GetDouble(double &outVal) const; + int GetBlob(Blob *&outVal) const; + int SetBlob(const Blob &val); + int GetBlob(Blob &outVal) const; + int SetText(const std::string &val); + int SetText(const uint8_t *val, uint32_t length); + int GetText(std::string &outVal) const; + void ResetValue(); + int GetBlobLength(uint32_t &length) const; + std::string ToString() const; + +private: + StorageType type_ = StorageType::STORAGE_TYPE_NULL; + union { + void* zeroMem; + Blob* blobPtr; + double dValue; + int64_t iValue; + } value_{}; +}; +} +#endif // DISTRIBUTED_DB_DATA_VALUE_H diff --git a/mock/distributeddb/common/include/db_common.h b/mock/distributeddb/common/include/db_common.h new file mode 100644 index 0000000000000000000000000000000000000000..338475c67eb538a1e4538f15bb2df055e3df9254 --- /dev/null +++ b/mock/distributeddb/common/include/db_common.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_COMMON_H +#define DISTRIBUTEDDB_COMMON_H + +#include +#include +#include "db_types.h" +#include "store_types.h" +#include "kvdb_properties.h" + +namespace DistributedDB { +class DBCommon final { +public: + static int CreateDirectory(const std::string &directory); + + static void StringToVector(const std::string &src, std::vector &dst); + static void VectorToString(const std::vector &src, std::string &dst); + + static std::string VectorToHexString(const std::vector &inVec, const std::string &separator = ""); + + static void PrintHexVector(const std::vector &data, int line = 0, const std::string &tag = ""); + + static std::string TransferStringToHex(const std::string &origStr); + + static std::string TransferHashString(const std::string &devName); + + static int CalcValueHash(const std::vector &Value, std::vector &hashValue); + + static int CreateStoreDirectory(const std::string &directory, const std::string &identifierName, + const std::string &subDir, bool isCreate); + + static int CopyFile(const std::string &srcFile, const std::string &dstFile); + + static int RemoveAllFilesOfDirectory(const std::string &dir, bool isNeedRemoveDir = true); + + static std::string GenerateIdentifierId(const std::string &storeId, + const std::string &appId, const std::string &userId); + + static std::string GenerateDualTupleIdentifierId(const std::string &storeId, const std::string &appId); + + static void SetDatabaseIds(KvDBProperties &properties, const std::string &appId, const std::string &userId, + const std::string &storeId); + + static std::string StringMasking(const std::string &oriStr, size_t remain = 3); // remain 3 unmask + + static std::string GetDistributedTableName(const std::string &device, const std::string &tableName); + + static void GetDeviceFromName(const std::string &deviceTableName, std::string &deviceHash, std::string &tableName); +}; + +// Define short macro substitute for original long expression for convenience of using +#define VEC_TO_STR(x) DBCommon::VectorToHexString(x).c_str() +#define STR_MASK(x) DBCommon::StringMasking(x).c_str() +#define STR_TO_HEX(x) DBCommon::TransferStringToHex(x).c_str() +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_COMMON_H diff --git a/mock/distributeddb/common/include/db_constant.h b/mock/distributeddb/common/include/db_constant.h new file mode 100644 index 0000000000000000000000000000000000000000..6ecd3149f5fe1ebe1b1de3908f7dbe579f471110 --- /dev/null +++ b/mock/distributeddb/common/include/db_constant.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_CONSTANT_H +#define DISTRIBUTEDDB_CONSTANT_H + +#include + +namespace DistributedDB { +class DBConstant { +public: + static constexpr size_t MAX_KEY_SIZE = 1024; + static constexpr size_t MAX_VALUE_SIZE = 4194304; + static constexpr size_t MAX_BATCH_SIZE = 128; + static constexpr size_t MAX_DEV_LENGTH = 128; + static constexpr size_t MAX_TRANSACTION_ENTRY_SIZE = 128; + + static constexpr size_t MAX_DATA_DIR_LENGTH = 512; + + static constexpr size_t MAX_INKEYS_SIZE = 128; + + static constexpr int DB_TYPE_LOCAL = 1; + static constexpr int DB_TYPE_MULTI_VER = 2; + static constexpr int DB_TYPE_SINGLE_VER = 3; + + static constexpr int QUEUED_SYNC_LIMIT_DEFAULT = 32; + static constexpr int QUEUED_SYNC_LIMIT_MIN = 1; + static constexpr int QUEUED_SYNC_LIMIT_MAX = 4096; + + static constexpr int MAX_DEVICES_SIZE = 100; + static constexpr int MAX_COMMIT_SIZE = 1000000; + static constexpr int MAX_ENTRIES_SIZE = 1000000; + + static constexpr uint32_t MAX_COLUMN = 32767; + + // In querySync, when getting query data finished, + // if the block size reach the half of max block size, will get deleted data next; + // if the block size not reach the half of max block size, will not get deleted data. + static constexpr float QUERY_SYNC_THRESHOLD = 0.50; + + static constexpr uint64_t MAX_USER_ID_LENGTH = 128; + static constexpr uint64_t MAX_APP_ID_LENGTH = 128; + static constexpr uint64_t MAX_STORE_ID_LENGTH = 128; + + static const std::string MULTI_SUB_DIR; + static const std::string SINGLE_SUB_DIR; + static const std::string LOCAL_SUB_DIR; + + static const std::string MAINDB_DIR; + static const std::string METADB_DIR; + static const std::string CACHEDB_DIR; + + static const std::string LOCAL_DATABASE_NAME; + static const std::string MULTI_VER_DATA_STORE; + static const std::string MULTI_VER_COMMIT_STORE; + static const std::string MULTI_VER_VALUE_STORE; + static const std::string MULTI_VER_META_STORE; + static const std::string SINGLE_VER_DATA_STORE; + static const std::string SINGLE_VER_META_STORE; + static const std::string SINGLE_VER_CACHE_STORE; + + static const std::string SQLITE_URL_PRE; + static const std::string SQLITE_DB_EXTENSION; + static const std::string SQLITE_MEMDB_IDENTIFY; + static const std::string SCHEMA_KEY; + + static const std::string PATH_POSTFIX_UNPACKED; + static const std::string PATH_POSTFIX_IMPORT_BACKUP; + static const std::string PATH_POSTFIX_IMPORT_ORIGIN; + static const std::string PATH_POSTFIX_IMPORT_DUP; + static const std::string PATH_POSTFIX_EXPORT_BACKUP; + static const std::string PATH_POSTFIX_DB_INCOMPLETE; // use for make sure create datebase and set label complete + + static const std::string REKEY_FILENAME_POSTFIX_PRE; + static const std::string REKEY_FILENAME_POSTFIX_OK; + static const std::string UPGRADE_POSTFIX; + static const std::string SET_SECOPT_POSTFIX; // used for make sure meta split upgrade atomically + + static const std::string PATH_BACKUP_POSTFIX; + + static const std::string ID_CONNECTOR; + + static const std::string DELETE_KVSTORE_REMOVING; + static const std::string DB_LOCK_POSTFIX; + + static const std::string SUBSCRIBE_QUERY_PREFIX; + static const std::string TRIGGER_REFERENCES_NEW; + static const std::string TRIGGER_REFERENCES_OLD; + + static const std::string UPDATE_META_FUNC; + + static const std::string SYSTEM_TABLE_PREFIX; + + static constexpr uint32_t AUTO_SYNC_TIMEOUT = 5000; // 5s + static constexpr uint32_t MANUAL_SYNC_TIMEOUT = 5000; // 5s + + static const size_t MAX_NORMAL_PACK_ITEM_SIZE = 4000; + static const size_t MAX_HPMODE_PACK_ITEM_SIZE = 2000; // slide window mode to reduce last ack transfer time + + static constexpr uint32_t MIN_MTU_SIZE = 1024; // 1KB + static constexpr uint32_t MAX_MTU_SIZE = 5242880; // 5MB + + static constexpr uint32_t MIN_TIMEOUT = 5000; // 5s + static constexpr uint32_t MAX_TIMEOUT = 60000; // 60s + + static constexpr uint8_t DEFAULT_COMPTRESS_RATE = 100; + + static constexpr size_t MAX_SYNC_BLOCK_SIZE = 31457280; // 30MB + + static constexpr int DOUBLE_PRECISION = 15; + static constexpr int MAX_DISTRIBUTED_TABLE_COUNT = 32; + + static constexpr uint64_t MAX_LOG_SIZE_HIGH = 0x400000000ULL; // 16GB + static constexpr uint64_t MAX_LOG_SIZE_LOW = 0x400000ULL; // 4MB + static constexpr uint64_t MAX_LOG_SIZE_DEFAULT = 0x40000000ULL; // 1GB + + static constexpr int DEF_LIFE_CYCLE_TIME = 60000; // 60S + + static constexpr int RELATIONAL_LOG_TABLE_FIELD_NUM = 7; // field num is relational distributed log table + + static constexpr uint64_t IGNORE_CONNECTION_ID = 0; + // For relational + static const std::string RELATIONAL_PREFIX; + static const std::string TIMESTAMP_ALIAS; +}; +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_CONSTANT_H diff --git a/mock/distributeddb/common/include/db_dfx_adapter.h b/mock/distributeddb/common/include/db_dfx_adapter.h new file mode 100644 index 0000000000000000000000000000000000000000..cc4f40cd06587eca4b6c7a86fc6acf548c28643a --- /dev/null +++ b/mock/distributeddb/common/include/db_dfx_adapter.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DB_DFX_ADAPTER_H +#define DB_DFX_ADAPTER_H + +#include +#include + +namespace DistributedDB { +enum DBEventType { + FAULT = 1, + STATISTIC = 2, + SECURITY = 3, + BEHAVIOR = 4 +}; +struct ReportTask { + std::string eventName; + std::string appId; + std::string userId; + std::string storeId; + int errCode = 0; +}; +class DBDfxAdapter { +public: + static void Dump(int fd, const std::vector &args); + + static void ReportFault(const ReportTask &reportTask); + + static void StartTrace(const std::string &action); + static void FinishTrace(); + + static void StartTraceSQL(); + static void FinishTraceSQL(); + + static void StartAsyncTrace(const std::string &action, int32_t taskId); + static void FinishAsyncTrace(const std::string &action, int32_t taskId); + + static const std::string SYNC_ACTION; + static const std::string EVENT_OPEN_DATABASE_FAILED; +private: + static const std::string EVENT_CODE; + static const std::string APP_ID; + static const std::string USER_ID; + static const std::string STORE_ID; + static const std::string SQLITE_EXECUTE; +}; +} // namespace DistributedDB + +#endif // DB_DFX_ADAPTER_H diff --git a/mock/distributeddb/common/include/db_dump_helper.h b/mock/distributeddb/common/include/db_dump_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..9dcf8dcfd0400c88042efdee593d23f8f4c1d2c3 --- /dev/null +++ b/mock/distributeddb/common/include/db_dump_helper.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DB_DUMP_HELPER_H +#define DB_DUMP_HELPER_H + +namespace DistributedDB { +class DBDumpHelper { +public: + static void Dump(int fd, const char *format, ...); +}; +} // namespace DistributedDB + +#endif // DB_DFX_ADAPTER_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/db_errno.h b/mock/distributeddb/common/include/db_errno.h new file mode 100644 index 0000000000000000000000000000000000000000..63a1974827fbf30c92265d7fc0ac032e14509d95 --- /dev/null +++ b/mock/distributeddb/common/include/db_errno.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_ERRNO_H +#define DISTRIBUTEDDB_ERRNO_H + +#include + +namespace DistributedDB { +constexpr int E_OK = 0; +constexpr int E_BASE = 1000; // different from the other errno. +constexpr int E_NOT_SUPPORT = (E_BASE + 1); // not support currently. +constexpr int E_INVALID_DB = (E_BASE + 2); // invalid db or connection. +constexpr int E_NOT_FOUND = (E_BASE + 3); // not found the resource. +constexpr int E_BUSY = (E_BASE + 4); // the db is busy +constexpr int E_UNEXPECTED_DATA = (E_BASE + 5); // Data does not match expectation. +constexpr int E_STALE = (E_BASE + 6); // Resource has been stopped, killed or destroyed. +constexpr int E_INVALID_ARGS = (E_BASE + 7); // the input args is invalid. +constexpr int E_REGISTER_OBSERVER = (E_BASE + 8); // error in register observer related function. +constexpr int E_TRANSACT_STATE = (E_BASE + 9); // transaction state error. +constexpr int E_SECUREC_ERROR = (E_BASE + 10); // security interface returns error +constexpr int E_OUT_OF_MEMORY = (E_BASE + 11); // out of memory +constexpr int E_NOT_PERMIT = (E_BASE + 12); // operation is not permitted +constexpr int E_ALREADY_REGISTER = (E_BASE + 13); // function or handle already registered and not allowed replace +constexpr int E_ALREADY_ALLOC = (E_BASE + 14); // Object had already been allocated +constexpr int E_ALREADY_RELEASE = (E_BASE + 15); // Object had already been released +constexpr int E_CONTAINER_FULL = (E_BASE + 16); // container full +constexpr int E_CONTAINER_EMPTY = (E_BASE + 17); // container empty +constexpr int E_CONTAINER_FULL_TO_NOTFULL = (E_BASE + 18); // container status changed from full to not full +constexpr int E_CONTAINER_NOTEMPTY_TO_EMPTY = (E_BASE + 19); // container status changed from full to not full +constexpr int E_WAIT_RETRY = (E_BASE + 20); // wait and retry later +constexpr int E_PARSE_FAIL = (E_BASE + 21); // parse packet or frame fail +constexpr int E_TIMEOUT = (E_BASE + 22); // time out +constexpr int E_SERIALIZE_ERROR = (E_BASE + 23); // serialize error +constexpr int E_DESERIALIZE_ERROR = (E_BASE + 24); // deserialize error +constexpr int E_NOT_REGISTER = (E_BASE + 25); // handler or function not registered +constexpr int E_LENGTH_ERROR = (E_BASE + 26); // error relative to length +constexpr int E_UNFINISHED = (E_BASE + 27); // get sync data unfinished. +constexpr int E_FINISHED = (E_BASE + 28); // get sync data finished. +constexpr int E_INVALID_MESSAGE_ID = (E_BASE + 29); // invalid messageId error +constexpr int E_MESSAGE_ID_ERROR = (E_BASE + 30); // messageId is not expected +constexpr int E_MESSAGE_TYPE_ERROR = (E_BASE + 31); // messageType is not expected +constexpr int E_PERIPHERAL_INTERFACE_FAIL = (E_BASE + 32); // peripheral interface fail +constexpr int E_NOT_INIT = (E_BASE + 33); // module may not init +constexpr int E_MAX_LIMITS = (E_BASE + 34); // over max limits. +constexpr int E_INVALID_CONNECTION = (E_BASE + 35); // invalid db connection. +constexpr int E_NO_SUCH_ENTRY = (E_BASE + 36); // invalid db connection. +constexpr int E_INTERNAL_ERROR = (E_BASE + 37); // an error due to code logic that is a bug +constexpr int E_CONTAINER_ONLY_DELAY_TASK = (E_BASE + 38); // only delay task left in the container +constexpr int E_SUM_CALCULATE_FAIL = (E_BASE + 39); // only delay task left in the container +constexpr int E_SUM_MISMATCH = (E_BASE + 40); // check sum mismatch +constexpr int E_OUT_OF_DATE = (E_BASE + 41); // things is out of date +constexpr int E_OBJ_IS_KILLED = (E_BASE + 42); // the refObject has been killed. +constexpr int E_SYSTEM_API_FAIL = (E_BASE + 43); // call the system api failed +constexpr int E_INVALID_DATA = (E_BASE + 44); // invalid data +constexpr int E_OUT_OF_IDS = (E_BASE + 45); // out of ids. +constexpr int E_SEND_DATA = (E_BASE + 46); // need send data +constexpr int E_NEED_TIMER = (E_BASE + 47); // timer is still need +constexpr int E_NO_NEED_TIMER = (E_BASE + 48); // timer no longer need +constexpr int E_COMBINE_FAIL = (E_BASE + 49); // fail in combining a frame +constexpr int E_END_TIMER = (E_BASE + 50); // timer no longer needed +constexpr int E_CALC_HASH = (E_BASE + 51); // calc hash error +constexpr int E_REMOVE_FILE = (E_BASE + 52); // remove file failed +constexpr int E_STATE_MACHINE_ERROR = (E_BASE + 53); // sync state machine error +constexpr int E_NO_DATA_SEND = (E_BASE + 54); // no data to send +constexpr int E_RECV_FINISHED = (E_BASE + 55); // recv finished +constexpr int E_NEED_PULL_REPONSE = (E_BASE + 56); // need to response pull request +constexpr int E_NO_SYNC_TASK = (E_BASE + 57); // no sync task to do +constexpr int E_INVALID_PASSWD_OR_CORRUPTED_DB = (E_BASE + 58); // invalid password or corrupted database. +constexpr int E_RESULT_SET_STATUS_INVALID = (E_BASE + 59); // status of result set is invalid. +constexpr int E_RESULT_SET_EMPTY = (E_BASE + 60); // the result set is empty. +constexpr int E_UPGRADE_FAILED = (E_BASE + 61); // the upgrade failed. +constexpr int E_INVALID_FILE = (E_BASE + 62); // import invalid file. +constexpr int E_INVALID_PATH = (E_BASE + 63); // the path is invalid. +constexpr int E_EMPTY_PATH = (E_BASE + 64); // the path is empty. +constexpr int E_TASK_BREAK_OFF = (E_BASE + 65); // task quit due to normal break off or error happen +constexpr int E_INCORRECT_DATA = (E_BASE + 66); // data in the database is incorrect +constexpr int E_NO_RESOURCE_FOR_USE = (E_BASE + 67); // no resource such as dbhandle for use +constexpr int E_LAST_SYNC_FRAME = (E_BASE + 68); // this frame is the last frame for this sync +constexpr int E_VERSION_NOT_SUPPORT = (E_BASE + 69); // version not support in any layer +constexpr int E_FRAME_TYPE_NOT_SUPPORT = (E_BASE + 70); // frame type not support +constexpr int E_INVALID_TIME = (E_BASE + 71); // the time is invalid +constexpr int E_INVALID_VERSION = (E_BASE + 72); // sqlite storage version is invalid +constexpr int E_SCHEMA_NOTEXIST = (E_BASE + 73); // schema does not exist +constexpr int E_INVALID_SCHEMA = (E_BASE + 74); // the schema is invalid +constexpr int E_SCHEMA_MISMATCH = (E_BASE + 75); // the schema is mismatch +constexpr int E_INVALID_FORMAT = (E_BASE + 76); // the value is invalid json or mismatch with the schema. +constexpr int E_READ_ONLY = (E_BASE + 77); // only have the read permission. +constexpr int E_NEED_ABILITY_SYNC = (E_BASE + 78); // ability sync has not done +constexpr int E_WAIT_NEXT_MESSAGE = (E_BASE + 79); // need remote device send a next message. +constexpr int E_LOCAL_DELETED = (E_BASE + 80); // local data is deleted by the unpublish. +constexpr int E_LOCAL_DEFEAT = (E_BASE + 81); // local data defeat the sync data while unpublish. +constexpr int E_LOCAL_COVERED = (E_BASE + 82); // local data is covered by the sync data while unpublish. +constexpr int E_INVALID_QUERY_FORMAT = (E_BASE + 83); // query format is not valid. +constexpr int E_INVALID_QUERY_FIELD = (E_BASE + 84); // query field is not valid. +constexpr int E_ALREADY_OPENED = (E_BASE + 85); // the database is already opened. +constexpr int E_ALREADY_SET = (E_BASE + 86); // already set. +constexpr int E_SAVE_DATA_NOTIFY = (E_BASE + 87); // notify remote device to keep alive, don't timeout +constexpr int E_RE_SEND_DATA = (E_BASE + 88); // need re send data +constexpr int E_EKEYREVOKED = (E_BASE + 89); // the EKEYREVOKED error +constexpr int E_SECURITY_OPTION_CHECK_ERROR = (E_BASE + 90); // remote device's SecurityOption not equal to local +constexpr int E_SYSTEM_API_ADAPTER_CALL_FAILED = (E_BASE + 91); // Adapter call failed +constexpr int E_NOT_NEED_DELETE_MSG = (E_BASE + 92); // not need delete msg, will be delete by sliding window receiver +constexpr int E_SLIDING_WINDOW_SENDER_ERR = (E_BASE + 93); // sliding window sender err +constexpr int E_SLIDING_WINDOW_RECEIVER_INVALID_MSG = (E_BASE + 94); // sliding window receiver invalid msg +constexpr int E_IGNORE_DATA = (E_BASE + 95); // ignore the data changed by other devices and ignore the same data. +constexpr int E_FORBID_CACHEDB = (E_BASE + 96); // such after rekey can not check passwd due to file control. +constexpr int E_INTERCEPT_DATA_FAIL = (E_BASE + 97); // Intercept push data failed. +constexpr int E_INVALID_COMPRESS_ALGO = (E_BASE + 98); // The algo is defined, but there's no implement for the algo. +constexpr int E_LOG_OVER_LIMITS = (E_BASE + 99); // The log file size is over the limits. +constexpr int E_MODE_MISMATCH = (E_BASE + 100); // dual sync mode mismatch +constexpr int E_NO_NEED_ACTIVE = (E_BASE + 101); // no need to active sync mode +// Num 150+ is reserved for schema related errno, since it may be added regularly +constexpr int E_JSON_PARSE_FAIL = (E_BASE + 150); // Parse json fail in grammatical level +constexpr int E_JSON_INSERT_PATH_EXIST = (E_BASE + 151); // Path already exist before insert +constexpr int E_JSON_INSERT_PATH_CONFLICT = (E_BASE + 152); // Nearest path ends with type not object +constexpr int E_JSON_DELETE_PATH_NOT_FOUND = (E_BASE + 153); // Path to delete not found +constexpr int E_SCHEMA_PARSE_FAIL = (E_BASE + 160); // Parse schema fail in content level +constexpr int E_SCHEMA_EQUAL_EXACTLY = (E_BASE + 161); // Two schemas are exactly the same +constexpr int E_SCHEMA_UNEQUAL_COMPATIBLE = (E_BASE + 162); // New schema contain different index +constexpr int E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE = (E_BASE + 163); // New schema contain more field(index may differ) +constexpr int E_SCHEMA_UNEQUAL_INCOMPATIBLE = (E_BASE + 164); // New schema contain more field or index +constexpr int E_SCHEMA_VIOLATE_VALUE = (E_BASE + 165); // New schema violate values already exist in dbFile +constexpr int E_FLATBUFFER_VERIFY_FAIL = (E_BASE + 170); // Verify flatbuffer content(schema or value) fail. +constexpr int E_VALUE_MATCH = (E_BASE + 180); // Value match schema(strict or compatible) without amend +constexpr int E_VALUE_MATCH_AMENDED = (E_BASE + 181); // Value match schema(strict or compatible) with amend +constexpr int E_VALUE_MISMATCH_FEILD_COUNT = (E_BASE + 182); // Value mismatch schema in field count +constexpr int E_VALUE_MISMATCH_FEILD_TYPE = (E_BASE + 183); // Value mismatch schema in field type +constexpr int E_VALUE_MISMATCH_CONSTRAINT = (E_BASE + 184); // Value mismatch schema in constraint +constexpr int E_VALUE_MISMATCH_OTHER_REASON = (E_BASE + 185); // Value mismatch schema in other reason +constexpr int E_RELATIONAL_TABLE_EQUAL = (E_BASE + 186); // In table is same +constexpr int E_RELATIONAL_TABLE_COMPATIBLE = (E_BASE + 187); // In table is compatible +constexpr int E_RELATIONAL_TABLE_COMPATIBLE_UPGRADE = (E_BASE + 188); // In table has more fields with default value +constexpr int E_RELATIONAL_TABLE_INCOMPATIBLE = (E_BASE + 189); // In table is incompatible +// Num 200+ is reserved for fixed value errno, which should not be changed between time +// Message with errorNo of Feedback-type is generated by CommunicatorAggregator without data part(No deserial if exist) +constexpr int E_FEEDBACK_UNKNOWN_MESSAGE = (E_BASE + 200); // Unknown message feedback from remote device +constexpr int E_FEEDBACK_COMMUNICATOR_NOT_FOUND = (E_BASE + 201); // Communicator not found feedback from remote device +constexpr int E_DISTRIBUTED_SCHEMA_NOT_FOUND = (E_BASE + 202); // Schema was not found in relational distributed tables +constexpr int E_DISTRIBUTED_SCHEMA_CHANGED = (E_BASE + 203); // Schema has change when do sync +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_ERRNO_H diff --git a/mock/distributeddb/common/include/db_types.h b/mock/distributeddb/common/include/db_types.h new file mode 100644 index 0000000000000000000000000000000000000000..b8dabd5962182a8d4d34cc34bec918d9b4d618fa --- /dev/null +++ b/mock/distributeddb/common/include/db_types.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_TYPES_H +#define DISTRIBUTEDDB_TYPES_H + +#include +#include +#include +#include + +#include "types_export.h" +#include "db_constant.h" + +namespace DistributedDB { +using Timestamp = uint64_t; +using ContinueToken = void *; +using DeviceID = std::string; +using TimeOffset = int64_t; +using ErrorCode = int; +using SyncId = uint64_t; +using WaterMark = uint64_t; +using DatabaseCorruptHandler = std::function; +using DatabaseLifeCycleNotifier = std::function; +const uint32_t MTU_SIZE = 5 * 1024 * 1024; // 5 M, 1024 is scale + +struct DataItem { + Key key; + Value value; + Timestamp timestamp = 0; + uint64_t flag = 0; + std::string origDev; + Timestamp writeTimestamp = 0; + std::string dev; + bool neglect = false; + Key hashKey{}; + static constexpr uint64_t DELETE_FLAG = 0x01; + static constexpr uint64_t LOCAL_FLAG = 0x02; + static constexpr uint64_t REMOVE_DEVICE_DATA_FLAG = 0x04; // only use for cachedb + static constexpr uint64_t REMOVE_DEVICE_DATA_NOTIFY_FLAG = 0x08; // only use for cachedb + // Only use for query sync and subscribe. ATTENTION!!! this flag should not write into mainDB. + // Mark the changed row data does not match with query sync(or subscribe) condition. + static constexpr uint64_t REMOTE_DEVICE_DATA_MISS_QUERY = 0x10; + static constexpr uint64_t UPDATE_FLAG = 0X20; +}; + +struct PragmaPublishInfo { + Key key; + bool deleteLocal = false; + bool updateTimestamp = false; + KvStoreNbPublishAction action; +}; + +struct PragmaUnpublishInfo { + Key key; + bool isDeleteSync = false; + bool isUpdateTime = false; +}; + +struct IOption { + static constexpr int LOCAL_DATA = 1; + static constexpr int SYNC_DATA = 2; + int dataType = LOCAL_DATA; +}; + +struct DataSizeSpecInfo { + uint32_t blockSize = MTU_SIZE; + size_t packetSize = DBConstant::MAX_HPMODE_PACK_ITEM_SIZE; +}; + +enum NotificationEventType { + DATABASE_COMMIT_EVENT = 0 +}; + +// Following are schema related common definition +using FieldName = std::string; +using FieldPath = std::vector; +// Normally, LEAF_FIELD_NULL will not appear in valid schema. LEAF_FIELD_LONG contain LEAF_FIELD_INTEGER, both are +// signed type and LEAF_FIELD_DOUBLE contain LEAF_FIELD_LONG. We don't parse into an array, so array are always leaf +// type. We parse into an object, LEAF_FIELD_OBJECT means an empty object, INTERNAL_FIELD_OBJECT however not empty. +enum class FieldType { + LEAF_FIELD_NULL, + LEAF_FIELD_BOOL, + LEAF_FIELD_INTEGER, + LEAF_FIELD_LONG, + LEAF_FIELD_DOUBLE, + LEAF_FIELD_STRING, + LEAF_FIELD_ARRAY, + LEAF_FIELD_OBJECT, + INTERNAL_FIELD_OBJECT, +}; +using TypeValue = std::pair; // Define for parameter convenience + +// Schema compatibility check behave differently for different value source +enum class ValueSource { + FROM_LOCAL, + FROM_SYNC, + FROM_DBFILE, +}; +// Represent raw-value from database to avoid copy. the first is the value start pointer, the second is the length. +using RawValue = std::pair; +using RawString = const std::string::value_type *; + +enum class OperatePerm { + NORMAL_PERM, + REKEY_MONOPOLIZE_PERM, + IMPORT_MONOPOLIZE_PERM, + DISABLE_PERM, +}; + +enum SingleVerConflictResolvePolicy { + DEFAULT_LAST_WIN = 0, + DENY_OTHER_DEV_AMEND_CUR_DEV_DATA = 1, +}; + +struct SyncTimeRange { + Timestamp beginTime = 0; + Timestamp deleteBeginTime = 0; + Timestamp endTime = static_cast(INT64_MAX); + Timestamp deleteEndTime = static_cast(INT64_MAX); + Timestamp lastQueryTime = 0; + bool IsValid() const + { + return (beginTime <= endTime && deleteBeginTime <= deleteEndTime); + } +}; +} // namespace DistributedDB +#endif // DISTRIBUTEDDB_TYPES_H diff --git a/mock/distributeddb/common/include/endian_convert.h b/mock/distributeddb/common/include/endian_convert.h new file mode 100644 index 0000000000000000000000000000000000000000..97060c201ea34eb031a00bd78afff0c4704a1373 --- /dev/null +++ b/mock/distributeddb/common/include/endian_convert.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ENDIAN_CONVERT_H +#define ENDIAN_CONVERT_H + +#include +#include + +namespace DistributedDB { +inline bool IsBigEndian() +{ + uint32_t data = 0x12345678; // 0x12345678 only used here, for endian test + uint8_t *firstByte = reinterpret_cast(&data); + if (*firstByte == 0x12) { // 0x12 only used here, for endian test + return true; + } + return false; +} + +template T HostToNet(const T &from) +{ + if (IsBigEndian()) { + return from; + } else { + T to; + size_t typeLen = sizeof(T); + const uint8_t *fromByte = reinterpret_cast(&from); + uint8_t *toByte = reinterpret_cast(&to); + for (size_t i = 0; i < typeLen; i++) { + toByte[i] = fromByte[typeLen - i - 1]; // 1 is for index boundary + } + return to; + } +} + +template T NetToHost(const T &from) +{ + return HostToNet(from); +} +} + +#endif \ No newline at end of file diff --git a/mock/distributeddb/common/include/hash.h b/mock/distributeddb/common/include/hash.h new file mode 100644 index 0000000000000000000000000000000000000000..d2ee53327266d81824942cb9ae5c6a318e4e99d8 --- /dev/null +++ b/mock/distributeddb/common/include/hash.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HASH_H +#define HASH_H + +#include +#include + +namespace DistributedDB { +class Hash { + const static uint64_t PRIME_SEED = 33; // 33 is a prime seed +public: + static uint64_t HashFunc(const std::string &input); + static uint32_t Hash32Func(const std::string &input); +}; +} + +#endif diff --git a/mock/distributeddb/common/include/ischema.h b/mock/distributeddb/common/include/ischema.h new file mode 100644 index 0000000000000000000000000000000000000000..f4413b25c8a98f86b96c0216239e5f372cd16217 --- /dev/null +++ b/mock/distributeddb/common/include/ischema.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_SCHEMA_H +#define I_SCHEMA_H + +#include + +#include "db_types.h" + +namespace DistributedDB { +// SchemaType::NONE represent for KV database which do not have schema. Only invalid SchemaObject is NONE type. +// Enum value must not be changed except SchemaType::UNRECOGNIZED. +enum class SchemaType : uint8_t { + NONE = 0, + JSON = 1, + FLATBUFFER = 2, + RELATIVE = 3, + UNRECOGNIZED = 4 +}; + +inline SchemaType ReadSchemaType(uint8_t inType) +{ + if (inType >= static_cast(SchemaType::UNRECOGNIZED)) { + return SchemaType::UNRECOGNIZED; + } + return static_cast(inType); +} + +struct SchemaAttribute { + FieldType type = FieldType::LEAF_FIELD_NULL; + bool isIndexable = false; + bool hasNotNullConstraint = false; + bool hasDefaultValue = false; + FieldValue defaultValue; // Has default value in union part and default construction in string part + std::string customFieldType {}; // Custom field type like BIGINT, DECIMAL, CHARACTER ... +}; + +class ISchema { +public: + ISchema() = default; + virtual ~ISchema() = default; + virtual int ParseFromSchemaString(const std::string &inSchemaString) = 0; + virtual bool IsSchemaValid() const = 0; + virtual SchemaType GetSchemaType() const = 0; + virtual std::string ToSchemaString() const = 0; +}; +} +#endif // I_SCHEMA_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/json_object.h b/mock/distributeddb/common/include/json_object.h new file mode 100644 index 0000000000000000000000000000000000000000..3d79c31acb5c8fa1b80cce393e44eb69c0bb329f --- /dev/null +++ b/mock/distributeddb/common/include/json_object.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JSON_OBJECT_H +#define JSON_OBJECT_H + +#include +#include +#include +#include +#ifndef OMIT_JSON +#include +#endif +#include "db_types.h" + +namespace DistributedDB { +// JsonObject is the abstraction of JsonString, it hides the JsonLib that we use and other messy details. +// JsonObject do not support concurrence inherently, use it locally or under mutex protection. +class JsonObject { +public: + // Set max allowed nest depth and return the value before set. + static uint32_t SetMaxNestDepth(uint32_t nestDepth); + + // Calculate nest depth when json string is legal or estimate depth by legal part from illegal json. + static uint32_t CalculateNestDepth(const std::string &inString, int &errCode); + static uint32_t CalculateNestDepth(const uint8_t *dataBegin, const uint8_t *dataEnd, int &errCode); + + // Support default constructor, copy constructor and copy assignment + JsonObject() = default; + ~JsonObject() = default; + JsonObject(const JsonObject &); + JsonObject& operator=(const JsonObject &); + + explicit JsonObject(const Json::Value &value); + + // Should be called on an invalid JsonObject, create new JsonObject if need to reparse + // Require the type of the root to be JsonObject, otherwise parse fail + int Parse(const std::string &inString); + int Parse(const std::vector &inData); // Whether ends with '\0' in vector is OK + + // The end refer to the byte after the last valid byte + int Parse(const uint8_t *dataBegin, const uint8_t *dataEnd); + + bool IsValid() const; + + // Unnecessary spacing will be removed and fieldName resorted by lexicographical order + std::string ToString() const; + + bool IsFieldPathExist(const FieldPath &inPath) const; + int GetFieldTypeByFieldPath(const FieldPath &inPath, FieldType &outType) const; + int GetFieldValueByFieldPath(const FieldPath &inPath, FieldValue &outValue) const; + + int GetObjectArrayByFieldPath(const FieldPath &inPath, std::vector &outArray) const; + int GetStringArrayByFieldPath(const FieldPath &inPath, std::vector &outArray) const; + + int GetObjectByFieldPath(const FieldPath &inPath, JsonObject &outObj) const; + + // An empty fieldPath indicate the root, the outSubPath should be empty before call, we will not empty it at first. + // If inPath is of multiple path, then outSubPath is combination of result of each inPath. + int GetSubFieldPath(const FieldPath &inPath, std::set &outSubPath) const; + int GetSubFieldPath(const std::set &inPath, std::set &outSubPath) const; + int GetSubFieldPathAndType(const FieldPath &inPath, std::map &outSubPathType) const; + int GetSubFieldPathAndType(const std::set &inPath, std::map &outSubPathType) const; + + // If inPath not refer to an array, return error. + int GetArraySize(const FieldPath &inPath, uint32_t &outSize) const; + + // If inPath not refer to an array, return error. If not all members are string or array type, return error. + // If array-type member is empty, ignore. If not all members of the array-type member are string, return error. + int GetArrayContentOfStringOrStringArray(const FieldPath &inPath, + std::vector> &outContent) const; + + // Can be called no matter JsonObject valid or not. Invalid turn into valid after call(insert on invalid never fail + // if parameter is valid). An empty inPath is not allowed. LEAF_FIELD_ARRAY and INTERNAL_FIELD_OBJECT is not + // supported. infinite double is not support. inValue is ignored for LEAF_FIELD_NULL. + // When inPath already exist, append value if it's an arrayObject, otherwise returns -E_JSON_INSERT_PATH_EXIST + // if nearest path ends with type not object, rets -E_JSON_INSERT_PATH_CONFLICT. + // Otherwise, insert field as well as filling up intermediate field, then returns E_OK; + // isAppend: when it's true, append inValue as path is an arrayObject if path not exist. + int InsertField(const FieldPath &inPath, FieldType inType, const FieldValue &inValue, bool isAppend = false); + + // Add json object to an array field. should be called on an valid JsonObject. Never turn into invalid after call. + // If inPath not refer to an array, return error. + int InsertField(const FieldPath &inPath, const JsonObject &inValue, bool isAppend = false); + + // Should be called on an valid JsonObject. Never turn into invalid after call. An empty inPath is not allowed. + // If inPath not exist, returns -E_JSON_DELETE_PATH_NOT_FOUND. Otherwise, delete field from its parent returns E_OK; + int DeleteField(const FieldPath &inPath); +private: +#ifndef OMIT_JSON + // Auxiliary Method: If inPath not refer to an array, return error. If not all members are string, return error. + int GetStringArrayContentByJsonValue(const Json::Value &value, std::vector &outStringArray) const; + + // Common Type Judgement Logic + int GetFieldTypeByJsonValue(const Json::Value &value, FieldType &outType) const; + + // Return E_OK if JsonValueNode found at exact the path, otherwise not E_OK + const Json::Value &GetJsonValueByFieldPath(const FieldPath &inPath, int &errCode) const; + + // REQUIRE: JsonObject is valid(Root value is object type). + // If inPath empty(means root), set exact and nearest to root value and nearDepth to 0, then ret E_OK; + // If JsonValue exist at exact path, set exact to this JsonValue, set nearest to its parent JsonValue, set nearDepth + // to the depth of this parent JsonValue, then ret E_OK; + // If exact path no exist, set exact to nullptr, set nearest to nearest JsonValue that can be found, set nearDepth + // to the depth of this nearest JsonValue, then ret -E_NOT_FOUND; + int LocateJsonValueByFieldPath(const FieldPath &inPath, Json::Value *&exact, + Json::Value *&nearest, uint32_t &nearDepth); + + // create if path not exist + int MoveToPath(const FieldPath &inPath, Json::Value *&exact, Json::Value *&nearest); + + static uint32_t maxNestDepth_; + + bool isValid_ = false; + Json::Value value_; +#endif +}; +} // namespace DistributedDB +#endif // JSON_OBJECT_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/log_print.h b/mock/distributeddb/common/include/log_print.h new file mode 100644 index 0000000000000000000000000000000000000000..0c164d3efb4d8e1d4c91ac3fc2317183d906d5e0 --- /dev/null +++ b/mock/distributeddb/common/include/log_print.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_LOG_PRINT_H +#define DISTRIBUTEDDB_LOG_PRINT_H + +#include +#include +#include +#include + +namespace DistributedDB { +const std::string LOG_TAG_KV = "DistributedDB"; + +class Logger { +public: + enum class Level { + LEVEL_DEBUG, + LEVEL_INFO, + LEVEL_WARN, + LEVEL_ERROR, + LEVEL_FATAL + }; + + virtual ~Logger() {}; + static Logger *GetInstance(); + static void RegisterLogger(Logger *logger); + static void Log(Level level, const std::string &tag, const char *func, int line, const char *format, ...); + +private: + virtual void Print(Level level, const std::string &tag, const std::string &msg) = 0; + static void PreparePrivateLog(const char *format, std::string &outStrFormat); + static Logger *logHandler; + static const std::string PRIVATE_TAG; +}; + +#define NO_LOG(...) // No log in normal and release. Used for convenience when deep debugging +#define LOGD(...) Logger::Log(Logger::Level::LEVEL_DEBUG, LOG_TAG_KV, __FUNCTION__, __LINE__, __VA_ARGS__) +#define LOGI(...) Logger::Log(Logger::Level::LEVEL_INFO, LOG_TAG_KV, __FUNCTION__, __LINE__, __VA_ARGS__) +#define LOGW(...) Logger::Log(Logger::Level::LEVEL_WARN, LOG_TAG_KV, __FUNCTION__, __LINE__, __VA_ARGS__) +#define LOGE(...) Logger::Log(Logger::Level::LEVEL_ERROR, LOG_TAG_KV, __FUNCTION__, __LINE__, __VA_ARGS__) +#define LOGF(...) Logger::Log(Logger::Level::LEVEL_FATAL, LOG_TAG_KV, __FUNCTION__, __LINE__, __VA_ARGS__) +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_LOG_PRINT_H diff --git a/mock/distributeddb/common/include/macro_utils.h b/mock/distributeddb/common/include/macro_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..ec2ccd61cb4d90ce479cd0c2b9dd06203568b9b7 --- /dev/null +++ b/mock/distributeddb/common/include/macro_utils.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MACRO_UTILS_H +#define MACRO_UTILS_H + +namespace DistributedDB { +#define DISABLE_COPY_ASSIGN_MOVE(ClassName) \ + ClassName(const ClassName &) = delete; \ + ClassName(ClassName &&) = delete; \ + ClassName& operator=(const ClassName &) = delete; \ + ClassName& operator=(ClassName &&) = delete + +#define DECLARE_OBJECT_TAG(ClassName) \ + std::string GetObjectTag() const override; \ + constexpr static const char * const classTag = "Class-"#ClassName + +#define DEFINE_OBJECT_TAG_FACILITIES(ClassName) \ + std::string ClassName::GetObjectTag() const \ + { \ + return ClassName::classTag; \ + } + +#define BYTE_8_ALIGN(x) (((x) + (8 - 1)) & ~(8 - 1)) + +#define BITX(x) (1 << (x)) + +#define ULL(x) (static_cast(x)) + +// Convert var or enum to variable name for printf +#define VNAME(name) (#name) +} +#endif // MACRO_UTILS_H diff --git a/mock/distributeddb/common/include/notification_chain.h b/mock/distributeddb/common/include/notification_chain.h new file mode 100644 index 0000000000000000000000000000000000000000..8c0d50b5a9f035f522994839c67951016ebf507a --- /dev/null +++ b/mock/distributeddb/common/include/notification_chain.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NOTIFICATION_CHAIN_H +#define NOTIFICATION_CHAIN_H + +#include +#include +#include +#include + +#include "ref_object.h" + +namespace DistributedDB { +using EventType = unsigned int; + +class NotificationChain final : public RefObject { +private: + class ListenerChain; + +public: + class Listener final : public RefObject { + public: + using OnEvent = std::function; + using OnFinalize = std::function; + + // Called by ListenerChain.callbackListeners, it will call the OnEvent + void NotifyListener(void *arg); + + // Drop this listener. after call this function, the listener will be destroy + int Drop(bool wait = false); + + // Enter kill-waiting state if 'onEvent()' is invoking when 'KillObj()'. + void KillWait(); + + // Set the listener chain we belong to. + void SetOwner(ListenerChain *listenerChain); + + Listener(const OnEvent &onEvent, const OnFinalize &onFinalize); + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(Listener); + + protected: + ~Listener() override; + + private: + // will be call when this listener destroy + void Finalize() const; + bool EnterEventAction(); + void LeaveEventAction(); + + DECLARE_OBJECT_TAG(Listener); + + constexpr static int KILL_WAIT_SECONDS = 5; // wait only 5 seconds when killing to avoid dead-lock. + OnEvent onEvent_; + OnFinalize onFinalize_; + ListenerChain *listenerChain_; + std::thread::id eventRunningThread_; + std::condition_variable safeKill_; + }; + + // Add a listener from the NotificationChain. it will return a Listener handle + // The param type should match the RegisterEventsType + // The param onEvent will be call when events happened. + // The param onFinalize will be call when this listener destroy + Listener *RegisterListener(EventType type, const Listener::OnEvent &onEvent, + const Listener::OnFinalize &onFinalize, int &errCode); + + // User to register an events type to the NotificationChain, needs to call at init + int RegisterEventType(EventType type); + + // User to unregister an events type. + int UnRegisterEventType(EventType type); + + // Should be call when events happened. + void NotifyEvent(EventType type, void *arg); + + NotificationChain() = default; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(NotificationChain); + +protected: + ~NotificationChain() override; + +private: + class ListenerChain final : public RefObject { + public: + // Add a listener to the ListenerChain + int RegisterListener(Listener *listener); + + // Remove a listener to the ListenerChain + int UnRegisterListener(Listener *listener, bool wait = false); + + // Callback all the listeners + void NotifyListeners(void *arg); + + // Clear all listeners + void ClearListeners(); + + ListenerChain(); + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(ListenerChain); + protected: + ~ListenerChain() override; + + private: + // Used to back up listenerSet_, need to lock + void BackupListenerSet(std::set &backupSet) const; + + DECLARE_OBJECT_TAG(ListenerChain); + + std::set listenerSet_; + }; + + // Find a ListenerChain from the eventChains_ with given type, + // this function needs to lock. + ListenerChain *FindAndGetListenerChainLocked(EventType type); + + // Find a ListenerChain from the eventChains_ with given type, + ListenerChain *FindListenerChain(EventType type) const; + + DECLARE_OBJECT_TAG(NotificationChain); + + std::map eventChains_; +}; +} // namespace DistributedDB + +#endif // NOTIFICATION_CHAIN_H diff --git a/mock/distributeddb/common/include/param_check_utils.h b/mock/distributeddb/common/include/param_check_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..f55e66a4622c7aa372859ffb9835c318dceda1f7 --- /dev/null +++ b/mock/distributeddb/common/include/param_check_utils.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PARAM_CHECK_UTILS_H +#define PARAM_CHECK_UTILS_H + +#include + +#include "db_types.h" +#include "auto_launch_export.h" +#include "schema_object.h" + +namespace DistributedDB { +class ParamCheckUtils final { +public: + + static bool CheckDataDir(const std::string &dir, std::string &canonicalDir); + + // Check if the storeID is a safe arg. + static bool IsStoreIdSafe(const std::string &storeId); + + // check appId, userId, storeId. + static bool CheckStoreParameter(const std::string &storeId, const std::string &appId, const std::string &userId, + bool isIgnoreUserIdCheck = false); + + // check encrypted args for KvStore. + static bool CheckEncryptedParameter(CipherType cipher, const CipherPassword &passwd); + + static bool CheckConflictNotifierType(int conflictType); + + static bool CheckSecOption(const SecurityOption &secOption); + + static bool CheckObserver(const Key &key, unsigned int mode); + + static bool IsS3SECEOpt(const SecurityOption &secOpt); + + static int CheckAndTransferAutoLaunchParam(const AutoLaunchParam ¶m, bool checkDir, + SchemaObject &schemaObject, std::string &canonicalDir); + + static uint8_t GetValidCompressionRate(uint8_t compressionRate); + + static bool CheckRelationalTableName(const std::string &tableName); +}; +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_PARAM_CHECK_UTILS_H diff --git a/mock/distributeddb/common/include/parcel.h b/mock/distributeddb/common/include/parcel.h new file mode 100644 index 0000000000000000000000000000000000000000..fb42ce19bb8be2a50974f78f6aa7bd4ee346f4b3 --- /dev/null +++ b/mock/distributeddb/common/include/parcel.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PARCEL_H +#define PARCEL_H + +#include +#include +#include +#include + +#include "endian_convert.h" +#include "securec.h" +#include "macro_utils.h" +#include "db_errno.h" +#include "log_print.h" +#ifndef OMIT_MULTI_VER +#include "multi_ver_def.h" +#endif + +namespace DistributedDB { +class Parcel { +public: + Parcel(uint8_t *inBuf, uint32_t length); + ~Parcel(); + bool IsError() const; + int WriteBool(bool data); + uint32_t ReadBool(bool &data); + int WriteInt(int data); + uint32_t ReadInt(int &val); + int WriteUInt8(uint8_t data); + uint32_t ReadUInt8(uint8_t &val); + int WriteDouble(double data); + uint32_t ReadDouble(double &val); + int WriteInt64(int64_t data); + uint32_t ReadInt64(int64_t &val); + int WriteUInt32(uint32_t data); + uint32_t ReadUInt32(uint32_t &val); + int WriteUInt64(uint64_t data); + uint32_t ReadUInt64(uint64_t &val); + int WriteVectorChar(const std::vector &data); + uint32_t ReadVectorChar(std::vector &val); + int WriteString(const std::string &inVal); + uint32_t ReadString(std::string &outVal); + bool IsContinueRead(); +#ifndef OMIT_MULTI_VER + int WriteMultiVerCommit(const MultiVerCommitNode &commit); + uint32_t ReadMultiVerCommit(MultiVerCommitNode &commit); + int WriteMultiVerCommits(const std::vector &commits); + uint32_t ReadMultiVerCommits(std::vector &commits); +#endif + + template + int WriteVector(const std::vector &data) + { + static_assert(std::is_pod::value, "type T is not pod"); + if (data.size() > INT32_MAX || sizeof(T) > INT32_MAX) { + LOGE("[WriteVector] invalid vector. vec.size:%zu, sizeof(T):%zu", data.size(), sizeof(T)); + isError_ = true; + return -E_PARSE_FAIL; + } + if (IsError()) { + return -E_PARSE_FAIL; + } + uint32_t len = data.size(); + uint64_t stepLen = static_cast(data.size()) * sizeof(T) + sizeof(uint32_t); + len = HostToNet(len); + if (bufPtr_ == nullptr || stepLen > INT32_MAX || parcelLen_ + BYTE_8_ALIGN(stepLen) > totalLen_) { + LOGE("[WriteVector] bufPtr:%d, stepLen:%llu, totalLen:%llu, parcelLen:%llu", + bufPtr_ != nullptr, ULL(stepLen), ULL(totalLen_), ULL(parcelLen_)); + isError_ = true; + return -E_PARSE_FAIL; + } + errno_t errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_, &len, sizeof(uint32_t)); + if (errCode != EOK) { + LOGE("[ReadVector] totalLen:%llu, parcelLen:%llu", ULL(totalLen_), ULL(parcelLen_)); + isError_ = true; + return -E_SECUREC_ERROR; + } + bufPtr_ += sizeof(uint32_t); + for (auto iter : data) { + *(reinterpret_cast(bufPtr_)) = HostToNet(iter); + bufPtr_ += sizeof(T); + } + bufPtr_ += BYTE_8_ALIGN(stepLen) - stepLen; + parcelLen_ += BYTE_8_ALIGN(stepLen); + return errCode; + } + + template + uint32_t ReadVector(std::vector &val) + { + static_assert(std::is_pod::value, "type T is not pod"); + if (IsError()) { + return 0; + } + if (bufPtr_ == nullptr || parcelLen_ + sizeof(uint32_t) > totalLen_ || sizeof(T) > INT32_MAX) { + LOGE("[ReadVector] bufPtr:%d, totalLen:%llu, parcelLen:%llu, sizeof(T):%zu", + bufPtr_ != nullptr, ULL(totalLen_), ULL(parcelLen_), sizeof(T)); + isError_ = true; + return 0; + } + uint32_t len = *(reinterpret_cast(bufPtr_)); + len = NetToHost(len); + if (len > INT32_MAX) { + LOGE("[ReadVector] invalid length:%u", len); + isError_ = true; + return 0; + } + uint64_t stepLen = static_cast(len) * sizeof(T) + sizeof(uint32_t); + if (stepLen > INT32_MAX || parcelLen_ + BYTE_8_ALIGN(stepLen) > totalLen_) { + LOGE("[ReadVector] stepLen:%llu, totalLen:%llu, parcelLen:%llu", ULL(stepLen), ULL(totalLen_), + ULL(parcelLen_)); + isError_ = true; + return 0; + } + bufPtr_ += sizeof(uint32_t); + val.resize(len); + for (uint32_t i = 0; i < len; i++) { + val[i] = NetToHost(*(reinterpret_cast(bufPtr_))); + bufPtr_ += sizeof(T); + } + bufPtr_ += BYTE_8_ALIGN(stepLen) - stepLen; + parcelLen_ += BYTE_8_ALIGN(stepLen); + stepLen = BYTE_8_ALIGN(stepLen); + return static_cast(stepLen); + } + + int WriteBlob(const char *buffer, uint32_t bufLen); + uint32_t ReadBlob(char *buffer, uint32_t bufLen); + void EightByteAlign(); // Avoid reading a single data type across 8 bytes + static uint32_t GetBoolLen(); + static uint32_t GetIntLen(); + static uint32_t GetUInt8Len(); + static uint32_t GetUInt32Len(); + static uint32_t GetUInt64Len(); + static uint32_t GetInt64Len(); + static uint32_t GetDoubleLen(); + static uint32_t GetVectorCharLen(const std::vector &data); + + template + static uint32_t GetVectorLen(const std::vector &data) + { + if (data.size() > INT32_MAX || sizeof(T) > INT32_MAX) { + return 0; + } + uint64_t len = sizeof(uint32_t) + static_cast(data.size()) * sizeof(T); + len = BYTE_8_ALIGN(len); + if (len > INT32_MAX) { + return 0; + } + return static_cast(len); + } + + static uint32_t GetEightByteAlign(uint32_t len); + static uint32_t GetStringLen(const std::string &data); +#ifndef OMIT_MULTI_VER + static uint32_t GetMultiVerCommitLen(const MultiVerCommitNode &commit); + static uint32_t GetMultiVerCommitsLen(const std::vector &commits); +#endif + static uint32_t GetAppendedLen(); + +private: + template + int WriteInteger(T integer); + template + uint32_t ReadInteger(T &integer); + + bool isError_ = false; + uint8_t *buf_ = nullptr; + uint8_t *bufPtr_ = nullptr; + uint64_t parcelLen_ = 0; + uint64_t totalLen_ = 0; +}; + +template +uint32_t Parcel::ReadInteger(T &integer) +{ + if (IsError()) { + return 0; + } + if (bufPtr_ == nullptr || parcelLen_ + sizeof(T) > totalLen_) { + LOGE("[ReadInteger] bufPtr:%d, totalLen:%llu, parcelLen:%llu, sizeof(T):%zu", + bufPtr_ != nullptr, ULL(totalLen_), ULL(parcelLen_), sizeof(T)); + isError_ = true; + return 0; + } + integer = *(reinterpret_cast(bufPtr_)); + bufPtr_ += sizeof(T); + parcelLen_ += sizeof(T); + integer = NetToHost(integer); + return sizeof(T); +} + +template +int Parcel::WriteInteger(T integer) +{ + if (IsError()) { + return -E_PARSE_FAIL; + } + T inData = HostToNet(integer); + if (parcelLen_ + sizeof(T) > totalLen_) { + LOGE("[WriteInteger] totalLen:%llu, parcelLen:%llu, sizeof(T):%zu", ULL(totalLen_), ULL(parcelLen_), sizeof(T)); + isError_ = true; + return -E_PARSE_FAIL; + } + errno_t errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_, &inData, sizeof(T)); + if (errCode != EOK) { + LOGE("[WriteInteger] bufPtr:%d, totalLen:%llu, parcelLen:%llu, sizeof(T):%zu", + bufPtr_ != nullptr, ULL(totalLen_), ULL(parcelLen_), sizeof(T)); + isError_ = true; + return -E_SECUREC_ERROR; + } + bufPtr_ += sizeof(T); + parcelLen_ += sizeof(T); + return errCode; +} +} // namespace DistributedDB + +#endif // PARCEL_H + diff --git a/mock/distributeddb/common/include/performance_analysis.h b/mock/distributeddb/common/include/performance_analysis.h new file mode 100644 index 0000000000000000000000000000000000000000..4a89f22f088651070cd5c16e4fefce499b11b757 --- /dev/null +++ b/mock/distributeddb/common/include/performance_analysis.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PERFORMANCE_ANALYSIS_H +#define PERFORMANCE_ANALYSIS_H + +#include +#include + +#include "db_types.h" + +namespace DistributedDB { +enum PT_TEST_RECORDS : uint32_t { + RECORD_PUT_DATA = 1, + RECORD_SYNC_TOTAL, + RECORD_WATERMARK_SYNC, + RECORD_READ_DATA, + RECORD_SAVE_DATA, + RECORD_SAVE_LOCAL_WATERMARK, + RECORD_SAVE_PEER_WATERMARK, + RECORD_DATA_SEND_REQUEST_TO_ACK_RECV, + RECORD_DATA_REQUEST_RECV_TO_SEND_ACK, + RECORD_MACHINE_START_TO_PUSH_SEND, + RECORD_ACK_RECV_TO_USER_CALL_BACK, +}; + +enum MV_TEST_RECORDS : uint32_t { + RECORD_SEND_LOCAL_DATA_CHANGED_TO_COMMIT_REQUEST_RECV = 3, + RECORD_GET_DEVICE_LATEST_COMMIT, + RECORD_COMMIT_SEND_REQUEST_TO_ACK_RECV, + RECORD_GET_COMMIT_TREE, + RECORD_DATA_GET_VALID_COMMIT, + RECORD_DATA_ENTRY_SEND_REQUEST_TO_ACK_RECV, + RECORD_GET_COMMIT_DATA, + RECORD_GET_VALUE_SLICE_NODE, + RECORD_VALUE_SLICE_SEND_REQUEST_TO_ACK_RECV, + RECORD_READ_VALUE_SLICE, + RECORD_SAVE_VALUE_SLICE, + RECORD_PUT_COMMIT_DATA, + RECORD_MERGE, +}; + +struct TimePair { + Timestamp startTime = 0; + Timestamp endTime = 0; +}; + +struct StatisticsInfo { + Timestamp max = 0; + Timestamp min = 0; + float average = 0.0; +}; + +struct SingleStatistics { + std::vector timeInfo; +}; + +class PerformanceAnalysis { +public: + explicit PerformanceAnalysis(uint32_t step); + ~PerformanceAnalysis(); + + static PerformanceAnalysis *GetInstance(int stepNum = 20); + + void TimeRecordStart(); + + void TimeRecordEnd(); + + void StepTimeRecordStart(uint32_t step); + + void StepTimeRecordEnd(uint32_t step); + + std::string GetStatistics(); + + void OpenPerformanceAnalysis(); + + void ClosePerformanceAnalysis(); + + void SetFileNumber(const std::string &FileID); + +private: + + bool IsStepValid(uint32_t step) const; + + bool IsOpen() const; + + bool InsertTimeRecord(const TimePair &timePair, uint32_t step); + + bool GetTimeRecord(uint32_t step, TimePair &timePair) const; + + void OutStatistics(); + + void Clear(); + + void Close(); + + const static int MAX_TIMERECORD_STEP_NUM = 200; + const static std::string STATISTICAL_DATA_FILE_NAME_HEADER; + const static std::string CSV_FILE_EXTENSION; + const static std::string DEFAULT_FILE_NAME; + SingleStatistics timeRecordData_; + std::vector stepTimeRecordInfo_; + std::vector counts_; + uint32_t stepNum_; + bool isOpen_; + std::ofstream outFile; + int fileNumber_; + std::string fileID_; +}; +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/common/include/platform_specific.h b/mock/distributeddb/common/include/platform_specific.h new file mode 100644 index 0000000000000000000000000000000000000000..0c2773f013a164b185a319d01e86fc6af66e99d8 --- /dev/null +++ b/mock/distributeddb/common/include/platform_specific.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PLATFORM_SPECIFIC_H +#define PLATFORM_SPECIFIC_H + +#include +#include +#include + +namespace DistributedDB { +namespace OS { +enum FileType { + FILE = 0, + PATH = 1, + OTHER = 2, +}; + +struct FileAttr { + std::string fileName; + FileType fileType; + uint64_t fileLen; +}; + +// Shield the representation method of file handles on different platforms +struct FileHandle { + int handle = -1; +}; + +int CalFileSize(const std::string &fileUrl, uint64_t &size); + +bool CheckPathExistence(const std::string &filePath); + +int MakeDBDirectory(const std::string &directory); + +int RemoveFile(const std::string &filePath); +// Can only remove empty directory +int RemoveDBDirectory(const std::string &directory); + +int GetRealPath(const std::string &inOriPath, std::string &outRealPath); + +int GetCurrentSysTimeInMicrosecond(uint64_t &outTime); + +int GetMonotonicRelativeTimeInMicrosecond(uint64_t &outTime); + +int CreateFileByFileName(const std::string &fileName); + +void SplitFilePath(const std::string &filePath, std::string &fileDir, std::string &fileName); + +int GetFileAttrFromPath(const std::string &filePath, std::list &files, bool isNeedAllPath = false); + +int GetFilePermissions(const std::string &fileName, uint32_t &permissions); + +int SetFilePermissions(const std::string &fileName, uint32_t permissions); + +int RenameFilePath(const std::string &oldFilePath, const std::string &newFilePath); + +int OpenFile(const std::string &fileName, FileHandle &handle); +int CloseFile(FileHandle &handle); + +int FileLock(const FileHandle &handle, bool isBlock); // be careful use block=true, may block process +int FileUnlock(FileHandle &handle); +} // namespace OS +} // namespace DistributedDB + +#endif // PLATFORM_SPECIFIC_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/ref_object.h b/mock/distributeddb/common/include/ref_object.h new file mode 100644 index 0000000000000000000000000000000000000000..7fce0ea1e73036ef0a43e9ddd526903f3f9231f8 --- /dev/null +++ b/mock/distributeddb/common/include/ref_object.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_DB_REF_OBJECT_H +#define KV_DB_REF_OBJECT_H + +#include +#include +#include +#include +#include +#include "macro_utils.h" + +namespace DistributedDB { +class RefObject { +public: + class AutoLock final { + public: + AutoLock(const RefObject *obj, bool unlocked = true); + ~AutoLock(); + void Lock(); + void Unlock(); + + private: + DISABLE_COPY_ASSIGN_MOVE(AutoLock); + const RefObject *refObj_; + bool IsLocked_; + }; + + RefObject(); + + /* Invoked before this object deleted. */ + void OnLastRef(const std::function &callback) const; + + /* Invoked when kill object, with lock held. */ + void OnKill(const std::function &callback); + + bool IsKilled() const; + void KillObj(); + void LockObj() const; + void UnlockObj() const; + bool WaitLockedUntil(std::condition_variable &cv, + const std::function &condition, int seconds = 0); + + /* Work as static members, avoid to 'delete this' */ + static void IncObjRef(const RefObject *obj); + static void DecObjRef(const RefObject *obj); + static void KillAndDecObjRef(RefObject *obj); + +protected: + virtual ~RefObject(); + virtual std::string GetObjectTag() const; + +private: + constexpr static const char * const classTag = "Class-RefObject"; + + DISABLE_COPY_ASSIGN_MOVE(RefObject); + + /* A const object can also be locked/unlocked/ref()/unref() */ + mutable std::atomic refCount_; + mutable std::mutex objLock_; + std::atomic isKilled_; + mutable std::function onLast_; + std::function onKill_; +}; +} // namespace DistributedDB + +#endif // KV_DB_REF_OBJECT_H diff --git a/mock/distributeddb/common/include/relational/relational_schema_object.h b/mock/distributeddb/common/include/relational/relational_schema_object.h new file mode 100644 index 0000000000000000000000000000000000000000..884dcd4cee5db6bf490633d1b35e326ac55bc500 --- /dev/null +++ b/mock/distributeddb/common/include/relational/relational_schema_object.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_SCHEMA_OBJECT_H +#define RELATIONAL_SCHEMA_OBJECT_H +#ifdef RELATIONAL_STORE +#include +#include "data_value.h" +#include "json_object.h" +#include "parcel.h" +#include "ischema.h" + +namespace DistributedDB { +using CompositeFields = std::vector; +class FieldInfo { +public: + const std::string &GetFieldName() const; + void SetFieldName(const std::string &fileName); + const std::string &GetDataType() const; + void SetDataType(const std::string &dataType); + bool IsNotNull() const; + void SetNotNull(bool isNotNull); + // Use string type to save the default value define in the create table sql. + // No need to use the real value because sqlite will complete them. + bool HasDefaultValue() const; + const std::string &GetDefaultValue() const; + void SetDefaultValue(const std::string &value); + // convert to StorageType according "Determination Of Column Affinity" + StorageType GetStorageType() const; + void SetStorageType(StorageType storageType); + + int GetColumnId() const; + void SetColumnId(int cid); + + // return field define string like ("fieldName": "MY INT(21), NOT NULL, DEFAULT 123") + std::string ToAttributeString() const; + + int CompareWithField(const FieldInfo &inField) const; +private: + std::string fieldName_; + std::string dataType_; // Type may be null + StorageType storageType_ = StorageType::STORAGE_TYPE_NONE; + bool isNotNull_ = false; + bool hasDefaultValue_ = false; + std::string defaultValue_; + int64_t cid_ = -1; +}; + +class TableInfo { +public: + const std::string &GetTableName() const; + bool GetAutoIncrement() const; + const std::string &GetCreateTableSql() const; + const std::map &GetFields() const; // + const std::map &GetIndexDefine() const; + const FieldName &GetPrimaryKey() const; + + void SetTableName(const std::string &tableName); + void SetAutoIncrement(bool autoInc); + void SetCreateTableSql(std::string sql); // set 'autoInc_' flag when set sql + void AddField(const FieldInfo &field); + void AddIndexDefine(const std::string &indexName, const CompositeFields &indexDefine); + void SetPrimaryKey(const FieldName &fieldName); // not support composite index now + std::string ToTableInfoString() const; + + int CompareWithTable(const TableInfo &inTableInfo) const; + std::map GetSchemaDefine() const; + std::string GetFieldName(uint32_t cid) const; // cid begin with 0 + const std::vector &GetFieldInfos() const; // Sort by cid + bool IsValid() const; + +private: + void AddFieldDefineString(std::string &attrStr) const; + void AddIndexDefineString(std::string &attrStr) const; + + int CompareWithTableFields(const std::map &inTableFields) const; + int CompareWithTableIndex(const std::map &inTableIndex) const; + + std::string tableName_; + bool autoInc_ = false; // only 'INTEGER PRIMARY KEY' could be defined as 'AUTOINCREMENT' + std::string sql_; + std::map fields_; + FieldName primaryKey_; + std::map indexDefines_; + mutable std::vector fieldInfos_; +}; + +class RelationalSchemaObject : public ISchema { +public: + RelationalSchemaObject() = default; + ~RelationalSchemaObject() override = default; + + bool IsSchemaValid() const override; + + SchemaType GetSchemaType() const override; + + std::string ToSchemaString() const override; + + // Should be called on an invalid SchemaObject, create new SchemaObject if need to reparse + int ParseFromSchemaString(const std::string &inSchemaString) override; + + void AddRelationalTable(const TableInfo& tb); + + void RemoveRelationalTable(const std::string &tableName); + + const std::map &GetTables() const; + + std::vector GetTableNames() const; + + TableInfo GetTable(const std::string& tableName) const; + +private: + int CompareAgainstSchemaObject(const std::string &inSchemaString, std::map &cmpRst) const; + + int CompareAgainstSchemaObject(const RelationalSchemaObject &inSchemaObject, + std::map &cmpRst) const; + + int ParseRelationalSchema(const JsonObject &inJsonObject); + int ParseCheckSchemaType(const JsonObject &inJsonObject); + int ParseCheckSchemaVersion(const JsonObject &inJsonObject); + int ParseCheckSchemaTableDefine(const JsonObject &inJsonObject); + int ParseCheckTableInfo(const JsonObject &inJsonObject); + int ParseCheckTableName(const JsonObject &inJsonObject, TableInfo &resultTable); + int ParseCheckTableDefine(const JsonObject &inJsonObject, TableInfo &resultTable); + int ParseCheckTableFieldInfo(const JsonObject &inJsonObject, const FieldPath &path, FieldInfo &table); + int ParseCheckTableAutoInc(const JsonObject &inJsonObject, TableInfo &resultTable); + int ParseCheckTableIndex(const JsonObject &inJsonObject, TableInfo &resultTable); + int ParseCheckTablePrimaryKey(const JsonObject &inJsonObject, TableInfo &resultTable); + + void GenerateSchemaString(); + + bool isValid_ = false; // set to true after parse success from string or add at least one relational table + SchemaType schemaType_ = SchemaType::RELATIVE; // Default RELATIVE + std::string schemaString_; // The minified and valid schemaString + std::string schemaVersion_; + std::map tables_; +}; +} // namespace DistributedDB +#endif // RELATIONAL_STORE +#endif // RELATIONAL_SCHEMA_OBJECT_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/res_finalizer.h b/mock/distributeddb/common/include/res_finalizer.h new file mode 100644 index 0000000000000000000000000000000000000000..ba9764de21e8f1a14974d24ac35e66dc176b65b9 --- /dev/null +++ b/mock/distributeddb/common/include/res_finalizer.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RES_FINALIZER_H +#define RES_FINALIZER_H + +#include +#include "macro_utils.h" + +namespace DistributedDB { +// RAII style resource finalizer for using in functions where the resource should be finalized before each return after +// the resource had been allocated. Just create an instance as function local stack variable and provide finalizer +// function after where the resource allocated. Suggest using this RAII style instead of using goto statement. +class ResFinalizer { +public: + explicit ResFinalizer(const std::function &inFinalizer) : finalizer_(inFinalizer) {} + ~ResFinalizer() + { + if (finalizer_) { + finalizer_(); + } + } + + DISABLE_COPY_ASSIGN_MOVE(ResFinalizer); +private: + std::function finalizer_; +}; +} // namespace DistributedDB +#endif // RES_FINALIZER_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/runtime_context.h b/mock/distributeddb/common/include/runtime_context.h new file mode 100644 index 0000000000000000000000000000000000000000..a03926e094117315810c1ebb19258f9b1a90b218 --- /dev/null +++ b/mock/distributeddb/common/include/runtime_context.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_CONTEXT_H +#define RUNTIME_CONTEXT_H + +#include +#include +#include + +#include "auto_launch.h" +#include "auto_launch_export.h" +#include "icommunicator_aggregator.h" +#include "iprocess_system_api_adapter.h" +#include "kv_store_observer.h" +#include "kvdb_properties.h" +#include "macro_utils.h" +#include "notification_chain.h" +#include "types_export.h" + +namespace DistributedDB { +using TimerId = uint64_t; +using TimerAction = std::function; +using TimerFinalizer = std::function; +using TaskAction = std::function; +using TimeChangedAction = std::function; +using LockStatusNotifier = std::function; +using UserChangedAction = std::function; + +class RuntimeContext { +public: + DISABLE_COPY_ASSIGN_MOVE(RuntimeContext); + + // Global setting interfaces. + virtual void SetProcessLabel(const std::string &label) = 0; + virtual std::string GetProcessLabel() const = 0; + + // If the pre adapter is not nullptr, set new adapter will release the pre adapter, + // must be called after SetCommunicatorAggregator + virtual int SetCommunicatorAdapter(IAdapter *adapter) = 0; + virtual int GetCommunicatorAggregator(ICommunicatorAggregator *&outAggregator) = 0; + virtual void SetCommunicatorAggregator(ICommunicatorAggregator *inAggregator) = 0; + virtual int GetLocalIdentity(std::string &outTarget) = 0; + + // Timer interfaces. + virtual int SetTimer(int milliSeconds, const TimerAction &action, + const TimerFinalizer &finalizer, TimerId &timerId) = 0; + virtual int ModifyTimer(TimerId timerId, int milliSeconds) = 0; + virtual void RemoveTimer(TimerId timerId, bool wait = false) = 0; + + // Task interfaces. + virtual int ScheduleTask(const TaskAction &task) = 0; + virtual int ScheduleQueuedTask(const std::string &queueTag, + const TaskAction &task) = 0; + + // Shrink as much memory as possible. + virtual void ShrinkMemory(const std::string &description) = 0; + + // Register a time changed lister, it will be callback when local time changed. + virtual NotificationChain::Listener *RegisterTimeChangedLister(const TimeChangedAction &action, int &errCode) = 0; + + // Get the global context object(singleton), never return nullptr. + static RuntimeContext *GetInstance(); + + virtual int SetPermissionCheckCallback(const PermissionCheckCallback &callback) = 0; + + virtual int SetPermissionCheckCallback(const PermissionCheckCallbackV2 &callback) = 0; + + virtual int RunPermissionCheck(const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) const = 0; + + virtual int EnableKvStoreAutoLaunch(const KvDBProperties &properties, AutoLaunchNotifier notifier, + const AutoLaunchOption &option) = 0; + + virtual int DisableKvStoreAutoLaunch(const std::string &normalIdentifier, const std::string &dualTupleIdentifier, + const std::string &userId) = 0; + + virtual void GetAutoLaunchSyncDevices(const std::string &identifier, std::vector &devices) const = 0; + + virtual void SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback, DBType type) = 0; + + virtual NotificationChain::Listener *RegisterLockStatusLister(const LockStatusNotifier &action, int &errCode) = 0; + + virtual bool IsAccessControlled() const = 0; + + virtual int SetSecurityOption(const std::string &filePath, const SecurityOption &option) const = 0; + + virtual int GetSecurityOption(const std::string &filePath, SecurityOption &option) const = 0; + + virtual bool CheckDeviceSecurityAbility(const std::string &devId, const SecurityOption &option) const = 0; + + virtual int SetProcessSystemApiAdapter(const std::shared_ptr &adapter) = 0; + + virtual bool IsProcessSystemApiAdapterValid() const = 0; + + virtual bool IsCommunicatorAggregatorValid() const = 0; + + // Notify TIME_CHANGE_EVENT. + virtual void NotifyTimestampChanged(TimeOffset offset) const = 0; + + virtual void SetStoreStatusNotifier(const StoreStatusNotifier ¬ifier) = 0; + + virtual void NotifyDatabaseStatusChange(const std::string &userId, const std::string &appId, + const std::string &storeId, const std::string &deviceId, bool onlineStatus) = 0; + + virtual int SetSyncActivationCheckCallback(const SyncActivationCheckCallback &callback) = 0; + + virtual bool IsSyncerNeedActive(std::string &userId, std::string &appId, std::string &storeId) const = 0; + + virtual NotificationChain::Listener *RegisterUserChangedListerner(const UserChangedAction &action, + EventType event) = 0; + + virtual int NotifyUserChanged() const = 0; + + // Generate global sessionId in current process + virtual uint32_t GenerateSessionId() = 0; + + virtual void DumpCommonInfo(int fd) = 0; +protected: + RuntimeContext() = default; + virtual ~RuntimeContext() {} +}; +} // namespace DistributedDB + +#endif // RUNTIME_CONTEXT_H diff --git a/mock/distributeddb/common/include/schema_constant.h b/mock/distributeddb/common/include/schema_constant.h new file mode 100644 index 0000000000000000000000000000000000000000..81077bc838ac5a76f020685ce137498c6ceae881 --- /dev/null +++ b/mock/distributeddb/common/include/schema_constant.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCHEMA_CONSTANT_H +#define SCHEMA_CONSTANT_H + +#include + +// This header is supposed to be included only in source files. Do not include it in any header files. +namespace DistributedDB { +class SchemaConstant final { +public: + static const std::string KEYWORD_SCHEMA_VERSION; + static const std::string KEYWORD_SCHEMA_MODE; + static const std::string KEYWORD_SCHEMA_DEFINE; + static const std::string KEYWORD_SCHEMA_INDEXES; + static const std::string KEYWORD_SCHEMA_SKIPSIZE; + static const std::string KEYWORD_SCHEMA_TYPE; + static const std::string KEYWORD_SCHEMA_TABLE; + static const std::string KEYWORD_INDEX; // For FlatBuffer-Schema + + static const std::string KEYWORD_MODE_STRICT; + static const std::string KEYWORD_MODE_COMPATIBLE; + + static const std::string KEYWORD_TYPE_BOOL; + static const std::string KEYWORD_TYPE_INTEGER; + static const std::string KEYWORD_TYPE_LONG; + static const std::string KEYWORD_TYPE_DOUBLE; + static const std::string KEYWORD_TYPE_STRING; + + static const std::string KEYWORD_ATTR_NOT_NULL; + static const std::string KEYWORD_ATTR_DEFAULT; + static const std::string KEYWORD_ATTR_VALUE_NULL; + static const std::string KEYWORD_ATTR_VALUE_TRUE; + static const std::string KEYWORD_ATTR_VALUE_FALSE; + + static const std::string KEYWORD_TYPE_RELATIVE; + static const std::string SCHEMA_SUPPORT_VERSION; + static const std::string SCHEMA_SUPPORT_VERSION_V2; + + static const uint32_t SCHEMA_META_FEILD_COUNT_MAX; + static const uint32_t SCHEMA_META_FEILD_COUNT_MIN; + static const uint32_t SCHEMA_FEILD_NAME_LENGTH_MAX; + static const uint32_t SCHEMA_FEILD_NAME_LENGTH_MIN; + static const uint32_t SCHEMA_FEILD_NAME_COUNT_MAX; + static const uint32_t SCHEMA_FEILD_NAME_COUNT_MIN; + static const uint32_t SCHEMA_FEILD_PATH_DEPTH_MAX; + static const uint32_t SCHEMA_INDEX_COUNT_MAX; + static const uint32_t SCHEMA_STRING_SIZE_LIMIT; + static const uint32_t SCHEMA_DEFAULT_STRING_SIZE_LIMIT; + static const uint32_t SCHEMA_SKIPSIZE_MAX; + + static const uint32_t SECURE_BYTE_ALIGN; +}; +} // namespace DistributedDB +#endif // SCHEMA_CONSTANT_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/schema_negotiate.h b/mock/distributeddb/common/include/schema_negotiate.h new file mode 100644 index 0000000000000000000000000000000000000000..cddb6d6c85da7001fea32e53ecb7b6711a1a2ade --- /dev/null +++ b/mock/distributeddb/common/include/schema_negotiate.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SCHEMA_NEGOTIATE_H +#define SCHEMA_NEGOTIATE_H + +#include "schema_object.h" +#include "relational_schema_object.h" + +namespace DistributedDB { +struct SyncOpinion { + bool permitSync = false; + bool requirePeerConvert = false; + bool checkOnReceive = false; +}; + +struct SyncStrategy { + bool permitSync = false; + bool convertOnSend = false; + bool convertOnReceive = false; + bool checkOnReceive = false; +}; + +using RelationalSyncOpinion = std::map; +using RelationalSyncStrategy = std::map; + +class SchemaNegotiate { +public: + // The remoteSchemaType may beyond local SchemaType definition + static SyncOpinion MakeLocalSyncOpinion(const SchemaObject &localSchema, const std::string &remoteSchema, + uint8_t remoteSchemaType); + + // The remoteOpinion.checkOnReceive is ignored + static SyncStrategy ConcludeSyncStrategy(const SyncOpinion &localOpinion, const SyncOpinion &remoteOpinion); + + static RelationalSyncOpinion MakeLocalSyncOpinion(const RelationalSchemaObject &localSchema, + const std::string &remoteSchema, uint8_t remoteSchemaType); + + // The remoteOpinion.checkOnReceive is ignored + static RelationalSyncStrategy ConcludeSyncStrategy(const RelationalSyncOpinion &localOpinion, + const RelationalSyncOpinion &remoteOpinion); + + static uint32_t CalculateParcelLen(const RelationalSyncOpinion &opinions); + static int SerializeData(const RelationalSyncOpinion &opinions, Parcel &parcel); + static int DeserializeData(Parcel &parcel, RelationalSyncOpinion &opinion); + +private: + SchemaNegotiate() = default; + ~SchemaNegotiate() = default; +}; +} + +#endif // SCHEMA_NEGOTIATE_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/schema_object.h b/mock/distributeddb/common/include/schema_object.h new file mode 100644 index 0000000000000000000000000000000000000000..2cf9091a6783a115cf4476fe7a75e680156a38ef --- /dev/null +++ b/mock/distributeddb/common/include/schema_object.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCHEMA_OBJECT_H +#define SCHEMA_OBJECT_H + +#include +#include +#ifndef OMIT_FLATBUFFER +#include +#endif // OMIT_FLATBUFFER +#include "db_types.h" +#include "macro_utils.h" +#include "relational_schema_object.h" +#include "ischema.h" +#include "value_object.h" + +namespace DistributedDB { +using IndexName = FieldPath; +using IndexFieldInfo = std::pair; +using IndexInfo = std::vector; +template using PairConstPointer = std::pair; + +struct IndexDifference { + std::map change; + std::map increase; + std::set decrease; +}; + +class SchemaObject : public ISchema { +public: + static std::string GetExtractFuncName(SchemaType inSchemaType); + static std::string GenerateExtractSQL(SchemaType inSchemaType, const FieldPath &inFieldpath, FieldType inFieldType, + uint32_t skipSize, const std::string &accessStr = ""); + + // Support default constructor, copy constructor and copy assignment + SchemaObject(); + ~SchemaObject() = default; + SchemaObject(const SchemaObject &); + SchemaObject& operator=(const SchemaObject &); +#ifdef RELATIONAL_STORE + explicit SchemaObject(const TableInfo &tableInfo); // The construct func can only be used for query. +#endif // RELATIONAL_STORE + + // Move constructor and move assignment is not need currently + SchemaObject(SchemaObject &&) = delete; + SchemaObject& operator=(SchemaObject &&) = delete; + + // Should be called on an invalid SchemaObject, create new SchemaObject if need to reparse + int ParseFromSchemaString(const std::string &inSchemaString) override; + + bool IsSchemaValid() const override; + SchemaType GetSchemaType() const override; + + // For Json-Schema : Unnecessary spacing will be removed and fieldname resorted by lexicographical order + // For FlatBuffer-Schema : Original binary schema(Base64 decoded if need) + std::string ToSchemaString() const override; + + uint32_t GetSkipSize() const; + std::map GetIndexInfo() const; + bool IsIndexExist(const IndexName &indexName) const; + + // Return E_OK if queryale. outType will be set if path exist no matter binary or not + int CheckQueryableAndGetFieldType(const FieldPath &inPath, FieldType &outType) const; + + // Attention: it doesn't return E_OK. instead: + // E_JSON_PARSE_FAIL : the inSchemaString is not an valid json + // E_SCHEMA_PARSE_FAIL : the inSchemaString is not an valid schema + // E_SCHEMA_EQUAL_EXACTLY : the inSchema is exactly equal to this SchemaObject + // E_SCHEMA_UNEQUAL_COMPATIBLE : the inSchema is not equal to but only index differ with this SchemaObject + // E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE : the inSchema is not equal to but can upgrade from this SchemaObject + // E_SCHEMA_UNEQUAL_INCOMPATIBLE : the inSchema is not equal to and can not upgrade from this SchemaObject + int CompareAgainstSchemaString(const std::string &inSchemaString) const; + int CompareAgainstSchemaString(const std::string &inSchemaString, IndexDifference &indexDiffer) const; + int CompareAgainstSchemaObject(const SchemaObject &inSchemaObject) const; + int CompareAgainstSchemaObject(const SchemaObject &inSchemaObject, IndexDifference &indexDiffer) const; + + // Attention: it doesn't return E_OK. instead: + // E_VALUE_MATCH : Value match schema(no matter strict or compatible mode) without any change + // E_VALUE_MATCH_AMENDED : Value match schema(no matter strict or compatible mode) with some amendment + // E_VALUE_MISMATCH_FEILD_COUNT : Value contain more field then schema when in strict mode + // E_VALUE_MISMATCH_FEILD_TYPE : Type of some fields of value mismatch schema + // E_VALUE_MISMATCH_CONSTRAINT : Some fields of value violate the NotNull constraint against schema + // E_VALUE_MISMATCH_OTHER_REASON : Value mismatch schema because of other reason unmentioned + int CheckValueAndAmendIfNeed(ValueSource sourceType, ValueObject &inValue) const; + + // Currently only for flatBuffer-type schema and value. + // Accept the original entry-value, return E_OK or E_FLATBUFFER_VERIFY_FAIL. + int VerifyValue(ValueSource sourceType, const Value &inValue) const; + int VerifyValue(ValueSource sourceType, const RawValue &inValue) const; + + // Accept the original value from database. The cache will not be expanded. Return E_OK if nothing error. + // The ExtractValue is with nice performance by carefully not use std-class to avoid memory allocation. + // But currently it can only deal with path with $. prefix and only one depth. However, meet current demand. + int ExtractValue(ValueSource sourceType, RawString inPath, const RawValue &inValue, TypeValue &outExtract, + std::vector *cache) const; +private: + enum class SchemaMode { + STRICT, + COMPATIBLE, + }; + using SchemaDefine = std::map; + + // For Json-Schema : Parsing related methods. + int ParseJsonSchema(const JsonObject &inJsonObject); + int CheckMetaFieldCountAndType(const JsonObject &inJsonObject) const; + int ParseCheckSchemaVersionMode(const JsonObject &inJsonObject); + int ParseCheckSchemaDefine(const JsonObject &inJsonObject); + int CheckSchemaDefineItemDecideAttribute(const JsonObject &inJsonObject, const FieldPath &inPath, FieldType inType, + SchemaAttribute &outAttr) const; + int ParseCheckSchemaIndexes(const JsonObject &inJsonObject); + int ParseCheckSchemaSkipSize(const JsonObject &inJsonObject); + + // For both Json-Schema and FlatBuffer-Schema. + int ParseCheckEachIndexFromStringArray(const std::vector &inStrArray); + int CheckFieldPathIndexableThenSave(const std::vector &inPathVec, IndexInfo &infoToSave); + + // CompareAgainstSchemaObject related sub methods + int CompareSchemaVersionMode(const SchemaObject &newSchema) const; + int CompareSchemaSkipSize(const SchemaObject &newSchema) const; + int CompareSchemaDefine(const SchemaObject &newSchema) const; + int CompareSchemaDefineByDepth(const SchemaDefine &oldDefine, const SchemaDefine &newDefine) const; + int CompareSchemaAttribute(const SchemaAttribute &oldAttr, const SchemaAttribute &newAttr) const; + int CompareSchemaDefaultValue(const SchemaAttribute &oldAttr, const SchemaAttribute &newAttr) const; + int CompareSchemaIndexes(const SchemaObject &newSchema, IndexDifference &indexDiffer) const; + + // CheckValueAndAmendIfNeed related sub methods + int CheckValue(const ValueObject &inValue, std::set &lackingPaths) const; + int AmendValueIfNeed(ValueObject &inValue, const std::set &lackingPaths, bool &amended) const; + + // It is better using a class to represent flatBuffer-Schema related other than more private method(As well as for + // Json-Schema in the future refactor). Delegation is chosen other than inheritance for accessing SchemaObject. + // Choose inner-class other than friend-class to avoid forward declaration and using pointer. + class FlatBufferSchema { + public: + explicit FlatBufferSchema(SchemaObject &owner) : owner_(owner) {}; + ~FlatBufferSchema() = default; + DISABLE_COPY_ASSIGN_MOVE(FlatBufferSchema); + // Copy-Constructor can not define due to Const-Ref member. Code standard require copy assignment be deleted. + void CopyFrom(const FlatBufferSchema &other); + + std::string GetDescription() const; + + // Judge whether it's flatbuffer type schema, no matter whether it is Base64 encoded, provide a decoded one. + static bool IsFlatBufferSchema(const std::string &inOriginal, std::string &outDecoded); + + // Accept a decoded and verified flatbuffer-schema, then parse its content + int ParseFlatBufferSchema(const std::string &inDecoded); + + // Compare based on self. + // return E_SCHEMA_EQUAL_EXACTLY or E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE or E_SCHEMA_UNEQUAL_INCOMPATIBLE + int CompareFlatBufferDefine(const FlatBufferSchema &other) const; + + // Accept a no-skipsize(so byte-aligned) value, return E_OK or E_FLATBUFFER_VERIFY_FAIL. + int VerifyFlatBufferValue(const RawValue &inValue, bool tryNoSizePrefix) const; + + // Accept a no-skipsize(so byte-aligned) value. + int ExtractFlatBufferValue(RawString inPath, const RawValue &inValue, TypeValue &outExtract, + bool tryNoSizePrefix) const; + private: +#ifndef OMIT_FLATBUFFER + using RawIndexInfos = std::map; // First the fieldName, second the index-attr value. + + const reflection::Schema *GetSchema() const; + + int ParseCheckRootTableAttribute(const reflection::Object &rootTable); + int ParseCheckRootTableDefine(const reflection::Schema &schema, const reflection::Object &rootTable, + RawIndexInfos &indexCollect); + int ParseCheckFieldInfo(const reflection::Schema &schema, const reflection::Field &field, + const FieldPath &path, RawIndexInfos &indexCollect); + void CollectRawIndexInfos(const reflection::Field &field, RawIndexInfos &indexCollect) const; + int ParseCheckStructDefine(const reflection::Schema &schema, const reflection::Field &field, + const FieldPath &path); + int ParseCheckIndexes(const RawIndexInfos &indexCollect); + + int CompareTableOrStructDefine(const PairConstPointer &bothSchema, + const PairConstPointer &bothObject, bool isRoot, std::set &compared) const; + int CompareStruct(const PairConstPointer &bothSchema, + const PairConstPointer &bothField, std::set &compared) const; +#endif + SchemaObject &owner_; + std::string description_; + }; + + bool isValid_ = false; + SchemaType schemaType_ = SchemaType::NONE; // Default NONE + FlatBufferSchema flatbufferSchema_; + std::string schemaString_; // The minified and valid schemaString + + std::string schemaVersion_; + SchemaMode schemaMode_ = SchemaMode::STRICT; // Only for Json-Schema, Consider refactor into JsonSchema class + uint32_t schemaSkipSize_ = 0; + std::map schemaIndexes_; + std::map schemaDefine_; // SchemaDefine classified by the depth of fieldpath +}; +} // namespace DistributedDB + +#endif // SCHEMA_OBJECT_H diff --git a/mock/distributeddb/common/include/schema_utils.h b/mock/distributeddb/common/include/schema_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..0f353c28a7447bf908a508ad978f7dbfc374cf1e --- /dev/null +++ b/mock/distributeddb/common/include/schema_utils.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCHEMA_UTILS_H +#define SCHEMA_UTILS_H + +#include "db_types.h" +#include "schema_object.h" + +// This header is supposed to be included only in source files. Do not include it in any header files. +namespace DistributedDB { +class SchemaUtils { +public: + // Check if any invalid exist, parse it into SchemaAttribute if totally valid and return E_OK + // Number don't support format of scientific notation. SchemaAttribute.isIndexable always set true. + // Prefix and postfix spaces or tabs is allowed. + // Customer FieldType will be accepted if useAffinity is true. + static int ParseAndCheckSchemaAttribute(const std::string &inAttrString, SchemaAttribute &outAttr, + bool useAffinity = false); + + // Check if any invalid exist, parse it into FieldPath if totally valid and return E_OK + // Each fieldName of the fieldPath will be check valid as well. Path depth will be check. + // Prefix and postfix spaces or tabs is allowed. Prefix $. can be not exist. + // Parameter permitPrefix means whether $. prefix is permited. If not, return E_SCHEMA_PARSE_FAIL. + static int ParseAndCheckFieldPath(const std::string &inPathString, FieldPath &outPath, bool permitPrefix = true); + + // Return E_OK if it is totally valid. Prefix and postfix spaces or tabs is not allowed. + static int CheckFieldName(const FieldName &inName); + + // Remove prefix and postfix spaces or tabs + static std::string Strip(const std::string &inString); + + // Strip the namespace from the full-name, this method mainly for flatbuffer-type schema + static std::string StripNameSpace(const std::string &inFullName); + + static std::string FieldTypeString(FieldType inType); + static std::string SchemaTypeString(SchemaType inType); + + // Restore to string representation of fieldPath with $. prefix + static std::string FieldPathString(const FieldPath &inPath); + + SchemaUtils() = delete; + ~SchemaUtils() = delete; + +private: + + static int SplitSchemaAttribute(const std::string &inAttrString, std::vector &outAttrString); + static int MakeTrans(const std::string &oriContent, size_t &pos); + + static int ParseSchemaAttribute(std::vector &attrContext, SchemaAttribute &outAttr, bool useAffinity); + + static int TransformDefaultValue(std::string &defaultContent, SchemaAttribute &outAttr); + + static int TransToDouble(const std::string &defaultContent, SchemaAttribute &outAttr); + + static int TransToInteger(const std::string &defaultContent, SchemaAttribute &outAttr); + + static int TransToLong(const std::string &defaultContent, SchemaAttribute &outAttr); + + static int TransToString(const std::string &defaultContent, SchemaAttribute &outAttr); + + static int TransToBool(const std::string &defaultContent, SchemaAttribute &outAttr); +}; +} // namespace DistributedDB +#endif // SCHEMA_UTILS_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/semaphore_utils.h b/mock/distributeddb/common/include/semaphore_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..9cdb3f561dc4bec5784670af7ab9c798b0dc957d --- /dev/null +++ b/mock/distributeddb/common/include/semaphore_utils.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SEMAPHORE_UTILS_H +#define SEMAPHORE_UTILS_H + +#include +#include +#include +#include +#include "macro_utils.h" + +namespace DistributedDB { +class SemaphoreUtils { +public: + explicit SemaphoreUtils(int count); + ~SemaphoreUtils(); + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SemaphoreUtils); + + bool WaitSemaphore(int waitSecond); + + void WaitSemaphore(); + + void SendSemaphore(); + +private: + bool CompareCount() const; + std::mutex lockMutex_; + std::condition_variable cv_; + int count_; +}; +} + +#endif diff --git a/mock/distributeddb/common/include/task_pool.h b/mock/distributeddb/common/include/task_pool.h new file mode 100644 index 0000000000000000000000000000000000000000..fe5d8c8bc5a20f4f00e30fc10fbe268d61617073 --- /dev/null +++ b/mock/distributeddb/common/include/task_pool.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TASK_POOL_H +#define TASK_POOL_H + +#include +#include +#include "macro_utils.h" + +namespace DistributedDB { +using Task = std::function; + +class TaskPool { +public: + // Start the task pool. + virtual int Start() = 0; + + // Stop the task pool. + virtual void Stop() = 0; + + // Schedule a task, the task can be ran in any thread. + virtual int Schedule(const Task &task) = 0; + + // Schedule tasks using FIFO policy(tasks with the same 'tag'). + virtual int Schedule(const std::string &tag, const Task &task) = 0; + + // Shrink memory associated with the given tag if possible. + virtual void ShrinkMemory(const std::string &tag) = 0; + + // Create/Destroy a task pool. + static TaskPool *Create(int maxThreads, int minThreads, int &errCode); + static void Release(TaskPool *&taskPool); + +protected: + TaskPool() = default; + virtual ~TaskPool() {} + DISABLE_COPY_ASSIGN_MOVE(TaskPool); +}; +} // namespace DistributedDB + +#endif // TASK_POOL_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/user_change_monitor.h b/mock/distributeddb/common/include/user_change_monitor.h new file mode 100644 index 0000000000000000000000000000000000000000..6591a40c4014d81d9f1a47efc85ac76eec990c15 --- /dev/null +++ b/mock/distributeddb/common/include/user_change_monitor.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USER_CHANGE_MONITOR_H +#define USER_CHANGE_MONITOR_H + +#include +#include "platform_specific.h" +#include "macro_utils.h" +#include "notification_chain.h" +#include "runtime_context.h" + +namespace DistributedDB { +class UserChangeMonitor final { +public: + UserChangeMonitor(); + ~UserChangeMonitor(); + + DISABLE_COPY_ASSIGN_MOVE(UserChangeMonitor); + + // Start the UserChangeMonitor + int Start(); + + // Stop the UserChangeMonitor + void Stop(); + + // Register a user changed lister, it will be callback when user changed. + NotificationChain::Listener *RegisterUserChangedListerner(const UserChangedAction &action, EventType event, + int &errCode); + + // Notify USER_CHANGE_EVENT. + void NotifyUserChanged() const; + static constexpr EventType USER_ACTIVE_EVENT = 3; + static constexpr EventType USER_NON_ACTIVE_EVENT = 4; + static constexpr EventType USER_ACTIVE_TO_NON_ACTIVE_EVENT = 5; +private: + // prepare notifier chain + int PrepareNotifierChain(); + + mutable std::shared_mutex userChangeMonitorLock_; + NotificationChain *userNotifier_; + bool isStarted_ = false; +}; +} // namespace DistributedDB + +#endif // USER_CHANGE_MONITOR_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/value_hash_calc.h b/mock/distributeddb/common/include/value_hash_calc.h new file mode 100644 index 0000000000000000000000000000000000000000..2a02ccc5e6be174a2fbbaf86367e8c4263c71355 --- /dev/null +++ b/mock/distributeddb/common/include/value_hash_calc.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VALUE_HASH_CALC_H +#define VALUE_HASH_CALC_H + +#include + +#include + +#include "db_types.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +class ValueHashCalc { +public: + ValueHashCalc() {}; + ~ValueHashCalc() + { + if (context_ != nullptr) { + delete context_; + context_ = nullptr; + } + } + + int Initialize() + { + context_ = new (std::nothrow) SHA256_CTX; + if (context_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + + int errCode = SHA256_Init(context_); + if (errCode == 0) { + LOGE("sha init failed:%d", errCode); + return -E_CALC_HASH; + } + return E_OK; + } + + int Update(const std::vector &value) + { + if (context_ == nullptr) { + return -E_CALC_HASH; + } + int errCode = SHA256_Update(context_, value.data(), value.size()); + if (errCode == 0) { + LOGE("sha update failed:%d", errCode); + return -E_CALC_HASH; + } + return E_OK; + } + + int GetResult(std::vector &value) + { + if (context_ == nullptr) { + return -E_CALC_HASH; + } + + value.resize(SHA256_DIGEST_LENGTH); + int errCode = SHA256_Final(value.data(), context_); + if (errCode == 0) { + LOGE("sha get result failed:%d", errCode); + return -E_CALC_HASH; + } + + return E_OK; + } + +private: + SHA256_CTX *context_ = nullptr; +}; +} + +#endif // VALUE_HASH_CALC_H diff --git a/mock/distributeddb/common/include/value_object.h b/mock/distributeddb/common/include/value_object.h new file mode 100644 index 0000000000000000000000000000000000000000..78ef91552d0e6798c0b9f65851c346ba0a5431ce --- /dev/null +++ b/mock/distributeddb/common/include/value_object.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VALUE_OBJECT_H +#define VALUE_OBJECT_H + +#include "json_object.h" + +namespace DistributedDB { +// ValueObject is the abstraction of value of KvEntry, a value is not always an json in different solutions. +// Thus, ValueObject can't just inherit JsonObject, although their methods are nearly the same. +class ValueObject { +public: + // Support default constructor, copy constructor and copy assignment + ValueObject() = default; + ~ValueObject() = default; + ValueObject(const ValueObject &); + ValueObject& operator=(const ValueObject &); + + // Move constructor and move assignment is not need currently + ValueObject(ValueObject &&) = delete; + ValueObject& operator=(ValueObject &&) = delete; + + // Should be called on an invalid ValueObject, create new ValueObject if need to reparse + int Parse(const std::string &inString); + int Parse(const std::vector &inData); // Whether ends with '\0' in vector is OK + + // The end refer to the byte after the last valid byte + int Parse(const uint8_t *dataBegin, const uint8_t *dataEnd, uint32_t offset = 0); + + bool IsValid() const; + + // Unnecessary spacing will be removed and fieldname resorted by lexicographical order + std::string ToString() const; + void WriteIntoVector(std::vector &outData) const; // An vector version ToString + + bool IsFieldPathExist(const FieldPath &inPath) const; + int GetFieldTypeByFieldPath(const FieldPath &inPath, FieldType &outType) const; + int GetFieldValueByFieldPath(const FieldPath &inPath, FieldValue &outValue) const; + + // An empty fieldpath indicate the root, the outSubPath should be empty before call, we will not empty it at first. + // If inPath is of multiple path, then outSubPath is combination of result of each inPath. + int GetSubFieldPath(const FieldPath &inPath, std::set &outSubPath) const; + int GetSubFieldPath(const std::set &inPath, std::set &outSubPath) const; + int GetSubFieldPathAndType(const FieldPath &inPath, std::map &outSubPathType) const; + int GetSubFieldPathAndType(const std::set &inPath, std::map &outSubPathType) const; + + // Can be called no matter ValueObject valid or not. Invalid turn into valid after successful call. An empty inPath + // is not allowed. LEAF_FIELD_ARRAY and INTERNAL_FIELD_OBJECT is not supported. infinite double is not support. + // inValue is ignored for LEAF_FIELD_NULL. If inPath already exist or nearest path ends with type not object, + // returns not E_OK. Otherwise insert field as well as filling up intermediate field, then returns E_OK; + int InsertField(const FieldPath &inPath, FieldType inType, const FieldValue &inValue); + + // Should be called on an valid ValueObject. Never turn into invalid after call. An empty inPath is not allowed. + // If inPath not exist, returns not E_OK. Otherwise delete field from its parent returns E_OK; + int DeleteField(const FieldPath &inPath); +private: + bool isValid_ = false; + JsonObject value_; + std::vector dataBeforeOffset_; +}; +} // namespace DistributedDB +#endif // VALUE_OBJECT_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/version.h b/mock/distributeddb/common/include/version.h new file mode 100644 index 0000000000000000000000000000000000000000..857d186f2412d7e423abd7d3f4e001612b9baee2 --- /dev/null +++ b/mock/distributeddb/common/include/version.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VERSION_H +#define VERSION_H + +#include +#include + +namespace DistributedDB { +// Version Regulation: +// Module version is always equal to the max version of its submodule. +// If a module or submodule upgrade to higher version, DO NOT simply increase current version by 1. +// First: you have to preserve current version by renaming it as a historical version. +// Second: Update the current version to the version to be release. +// Finally: Update its parent module's version if exist. +// Why we update the current version to the version to be release? For example, if module A has submodule B and C, +// if now version of B is 105, and C is 101, thus version of A is 105; if now release version is 106 and we upgrade +// submodule C, if we simply change version of C to 102 then version of A is still 105, but if we change version of C +// to 106 then version of A is now 106, so we can know that something had changed for module A. +const std::string SOFTWARE_VERSION_STRING = "1.1.5"; // DistributedDB current version string. +constexpr uint32_t SOFTWARE_VERSION_BASE = 100; // Software version base value, do not change it +constexpr uint32_t SOFTWARE_VERSION_RELEASE_1_0 = SOFTWARE_VERSION_BASE + 1; // 1 for first released version +constexpr uint32_t SOFTWARE_VERSION_RELEASE_2_0 = SOFTWARE_VERSION_BASE + 2; // 2 for second released version +constexpr uint32_t SOFTWARE_VERSION_RELEASE_3_0 = SOFTWARE_VERSION_BASE + 3; // 3 for third released version +constexpr uint32_t SOFTWARE_VERSION_RELEASE_4_0 = SOFTWARE_VERSION_BASE + 4; // 4 for fourth released version +constexpr uint32_t SOFTWARE_VERSION_RELEASE_5_0 = SOFTWARE_VERSION_BASE + 5; // 5 for fifth released version +constexpr uint32_t SOFTWARE_VERSION_RELEASE_6_0 = SOFTWARE_VERSION_BASE + 6; // 6 for sixth released version +constexpr uint32_t SOFTWARE_VERSION_EARLIEST = SOFTWARE_VERSION_RELEASE_1_0; +#ifdef RELATIONAL_STORE +constexpr uint32_t SOFTWARE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_6_0; +#else +constexpr uint32_t SOFTWARE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_5_0; +#endif +constexpr int VERSION_INVALID = INT32_MAX; + +// Storage Related Version +// LocalNaturalStore Related Version +constexpr int LOCAL_STORE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_1_0; +// SingleVerNaturalStore Related Version +constexpr int SINGLE_VER_STORE_VERSION_V1 = SOFTWARE_VERSION_RELEASE_1_0; +constexpr int SINGLE_VER_STORE_VERSION_V2 = SOFTWARE_VERSION_RELEASE_2_0; +constexpr int SINGLE_VER_STORE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_3_0; +// MultiVerNaturalStore Related Version +constexpr uint32_t VERSION_FILE_VERSION_CURRENT = 1; +constexpr uint32_t MULTI_VER_STORE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_1_0; +constexpr int MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_1_0; +constexpr int MULTI_VER_DATA_STORAGE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_1_0; +constexpr int MULTI_VER_METADATA_STORAGE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_1_0; +constexpr int MULTI_VER_VALUESLICE_STORAGE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_1_0; + +// Syncer Related Version +constexpr int TIME_SYNC_VERSION_V1 = SOFTWARE_VERSION_RELEASE_1_0; // time sync proctol added in version 101. +constexpr int ABILITY_SYNC_VERSION_V1 = SOFTWARE_VERSION_RELEASE_2_0; // Ability sync proctol added in version 102. +constexpr uint32_t SINGLE_VER_SYNC_PROCTOL_V1 = SOFTWARE_VERSION_RELEASE_1_0; // The 1st version num +constexpr uint32_t SINGLE_VER_SYNC_PROCTOL_V2 = SOFTWARE_VERSION_RELEASE_2_0; // The 2nd version num +constexpr uint32_t SINGLE_VER_SYNC_PROCTOL_V3 = SOFTWARE_VERSION_RELEASE_3_0; // The third version num +} // namespace DistributedDB + +#endif // VERSION_H \ No newline at end of file diff --git a/mock/distributeddb/common/include/zlib_compression.h b/mock/distributeddb/common/include/zlib_compression.h new file mode 100644 index 0000000000000000000000000000000000000000..4f14b2c7de09082a0a70c7f3a853c86cd9eed510 --- /dev/null +++ b/mock/distributeddb/common/include/zlib_compression.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ZLIB_COMPRESSION_H +#define ZLIB_COMPRESSION_H +#ifndef OMIT_ZLIB +#include +#include "data_compression.h" + +namespace DistributedDB { +class ZlibCompression final : public DataCompression { +public: + ZlibCompression(); + ~ZlibCompression() = default; + + int Compress(const std::vector &srcData, std::vector &destData) const override; + int Uncompress(const std::vector &srcData, std::vector &destData, uint32_t destLen) const + override; + +protected: + ZlibCompression(const ZlibCompression& compression) = delete; + ZlibCompression& operator= (const ZlibCompression& compression) = delete; +}; +} // namespace DistributedDB +#endif // OMIT_ZLIB +#endif // ZLIB_COMPRESSION_H \ No newline at end of file diff --git a/mock/distributeddb/common/src/auto_launch.cpp b/mock/distributeddb/common/src/auto_launch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c2337dd41dd53d45498b8a7afc10b8e4e114eab0 --- /dev/null +++ b/mock/distributeddb/common/src/auto_launch.cpp @@ -0,0 +1,1261 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "auto_launch.h" + +#include + +#include "db_common.h" +#include "db_dump_helper.h" +#include "db_dfx_adapter.h" +#include "db_errno.h" +#include "kv_store_changed_data_impl.h" +#include "kv_store_nb_conflict_data_impl.h" +#include "kvdb_manager.h" +#include "kvdb_pragma.h" +#include "log_print.h" +#include "param_check_utils.h" +#include "relational_store_instance.h" +#include "relational_store_changed_data_impl.h" +#include "runtime_context.h" +#include "semaphore_utils.h" +#include "sync_able_kvdb_connection.h" + +namespace DistributedDB { +namespace { + constexpr int MAX_AUTO_LAUNCH_ITEM_NUM = 8; +} + +void AutoLaunch::SetCommunicatorAggregator(ICommunicatorAggregator *aggregator) +{ + LOGI("[AutoLaunch] SetCommunicatorAggregator"); + std::lock_guard autoLock(communicatorLock_); + int errCode; + if (communicatorAggregator_ != nullptr) { + LOGI("[AutoLaunch] SetCommunicatorAggregator communicatorAggregator_ is not nullptr"); + errCode = communicatorAggregator_->RegOnConnectCallback(nullptr, nullptr); + if (errCode != E_OK) { + LOGW("[AutoLaunch] communicatorAggregator_->RegOnConnectCallback(nullptr, nullptr), errCode:%d", errCode); + } + errCode = communicatorAggregator_->RegCommunicatorLackCallback(nullptr, nullptr); + if (errCode != E_OK) { + LOGW("[AutoLaunch] communicatorAggregator_->RegCommunicatorLackCallback(nullptr, nullptr), errCode:%d", + errCode); + } + } + communicatorAggregator_ = aggregator; + if (aggregator == nullptr) { + LOGI("[AutoLaunch] SetCommunicatorAggregator aggregator is nullptr"); + return; + } + errCode = aggregator->RegOnConnectCallback(std::bind(&AutoLaunch::OnlineCallBack, this, + std::placeholders::_1, std::placeholders::_2), nullptr); + if (errCode != E_OK) { + LOGW("[AutoLaunch] aggregator->RegOnConnectCallback errCode:%d", errCode); + } + errCode = aggregator->RegCommunicatorLackCallback( + std::bind(&AutoLaunch::ReceiveUnknownIdentifierCallBack, this, std::placeholders::_1, std::placeholders::_2), + nullptr); + if (errCode != E_OK) { + LOGW("[AutoLaunch] aggregator->RegCommunicatorLackCallback errCode:%d", errCode); + } +} + +AutoLaunch::~AutoLaunch() +{ + { + std::lock_guard autoLock(communicatorLock_); + LOGI("[AutoLaunch] ~AutoLaunch()"); + if (communicatorAggregator_ != nullptr) { + communicatorAggregator_->RegOnConnectCallback(nullptr, nullptr); + communicatorAggregator_->RegCommunicatorLackCallback(nullptr, nullptr); + communicatorAggregator_ = nullptr; + } + } + // {identifier, userId} + std::set> inDisableSet; + std::set> inWaitIdleSet; + std::unique_lock autoLock(dataLock_); + for (auto &items : autoLaunchItemMap_) { + for (auto &iter : items.second) { + if (iter.second.isDisable) { + inDisableSet.insert({ items.first, iter.first }); + } else if (iter.second.state == AutoLaunchItemState::IDLE && (!iter.second.inObserver)) { + TryCloseConnection(iter.second); + } else { + inWaitIdleSet.insert({ items.first, iter.first }); + iter.second.isDisable = true; + } + } + } + for (const auto &identifierInfo : inDisableSet) { + cv_.wait(autoLock, [identifierInfo, this] { + return autoLaunchItemMap_.count(identifierInfo.first) == 0 || + autoLaunchItemMap_[identifierInfo.first].count(identifierInfo.second) == 0 || + (!autoLaunchItemMap_[identifierInfo.first][identifierInfo.second].isDisable); + }); + if (autoLaunchItemMap_.count(identifierInfo.first) != 0 && + autoLaunchItemMap_[identifierInfo.first].count(identifierInfo.second)) { + TryCloseConnection(autoLaunchItemMap_[identifierInfo.first][identifierInfo.second]); + } + } + for (const auto &info : inWaitIdleSet) { + cv_.wait(autoLock, [info, this] { + return (autoLaunchItemMap_[info.first][info.second].state == AutoLaunchItemState::IDLE) && + (!autoLaunchItemMap_[info.first][info.second].inObserver); + }); + TryCloseConnection(autoLaunchItemMap_[info.first][info.second]); + } +} + +int AutoLaunch::EnableKvStoreAutoLaunchParmCheck(AutoLaunchItem &autoLaunchItem, const std::string &normalIdentifier, + const std::string &dualTupleIdentifier, bool isDualTupleMode) +{ + std::lock_guard autoLock(dataLock_); + std::string userId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::USER_ID, ""); + if (isDualTupleMode && autoLaunchItemMap_.count(normalIdentifier) != 0 && + autoLaunchItemMap_[normalIdentifier].count(userId) != 0) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck identifier is already enabled in normal tuple mode"); + return -E_ALREADY_SET; + } + if (!isDualTupleMode && autoLaunchItemMap_.count(dualTupleIdentifier) != 0 && + autoLaunchItemMap_[dualTupleIdentifier].count(userId) != 0) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck identifier is already enabled in dual tuple mode"); + return -E_ALREADY_SET; + } + std::string identifier = isDualTupleMode ? dualTupleIdentifier : normalIdentifier; + if (identifier.empty()) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck identifier is invalid"); + return -E_INVALID_ARGS; + } + if (autoLaunchItemMap_.count(identifier) != 0 && autoLaunchItemMap_[identifier].count(userId) != 0) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck identifier is already enabled!"); + return -E_ALREADY_SET; + } + uint32_t autoLaunchItemSize = 0; + for (const auto &item : autoLaunchItemMap_) { + autoLaunchItemSize += item.second.size(); + } + if (autoLaunchItemSize == MAX_AUTO_LAUNCH_ITEM_NUM) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck size is max(8) now"); + return -E_MAX_LIMITS; + } + autoLaunchItem.state = AutoLaunchItemState::IN_ENABLE; + autoLaunchItemMap_[identifier][userId] = autoLaunchItem; + LOGI("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck ok identifier=%.6s, isDual=%d", + STR_TO_HEX(identifier), isDualTupleMode); + return E_OK; +} + +int AutoLaunch::EnableKvStoreAutoLaunch(const KvDBProperties &properties, AutoLaunchNotifier notifier, + const AutoLaunchOption &option) +{ + LOGI("[AutoLaunch] EnableKvStoreAutoLaunch"); + bool isDualTupleMode = properties.GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false); + std::string dualTupleIdentifier = properties.GetStringProp(KvDBProperties::DUAL_TUPLE_IDENTIFIER_DATA, ""); + std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + std::string userId = properties.GetStringProp(KvDBProperties::USER_ID, ""); + std::string appId = properties.GetStringProp(DBProperties::APP_ID, ""); + std::string storeId = properties.GetStringProp(DBProperties::STORE_ID, ""); + std::shared_ptr ptr = std::make_shared(properties); + AutoLaunchItem autoLaunchItem { ptr, notifier, option.observer, option.conflictType, option.notifier }; + autoLaunchItem.isAutoSync = option.isAutoSync; + autoLaunchItem.type = DBType::DB_KV; + int errCode = EnableKvStoreAutoLaunchParmCheck(autoLaunchItem, identifier, dualTupleIdentifier, isDualTupleMode); + if (errCode != E_OK) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunch failed errCode:%d", errCode); + return errCode; + } + if (isDualTupleMode && !RuntimeContext::GetInstance()->IsSyncerNeedActive(userId, appId, storeId)) { + std::lock_guard autoLock(dataLock_); + std::string tmpIdentifier = isDualTupleMode ? dualTupleIdentifier : identifier; + LOGI("[AutoLaunch] GetDoOpenMap identifier=%.6s no need to open", STR_TO_HEX(tmpIdentifier)); + autoLaunchItemMap_[tmpIdentifier][userId].state = AutoLaunchItemState::IDLE; + return errCode; + } + errCode = GetKVConnectionInEnable(autoLaunchItem, isDualTupleMode ? dualTupleIdentifier : identifier); + if (errCode == E_OK) { + LOGI("[AutoLaunch] EnableKvStoreAutoLaunch ok"); + } else { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunch failed errCode:%d", errCode); + } + return errCode; +} + +int AutoLaunch::GetKVConnectionInEnable(AutoLaunchItem &autoLaunchItem, const std::string &identifier) +{ + LOGI("[AutoLaunch] GetKVConnectionInEnable"); + int errCode; + std::shared_ptr properties = std::static_pointer_cast(autoLaunchItem.propertiesPtr); + std::string userId = properties->GetStringProp(KvDBProperties::USER_ID, ""); + autoLaunchItem.conn = KvDBManager::GetDatabaseConnection(*properties, errCode, false); + if (errCode == -E_ALREADY_OPENED) { + LOGI("[AutoLaunch] GetKVConnectionInEnable user already getkvstore by self"); + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IDLE; + return E_OK; + } + if (autoLaunchItem.conn == nullptr) { + EraseAutoLauchItem(identifier, userId); + return errCode; + } + bool isEmpty = false; + { + std::lock_guard onlineDevicesLock(dataLock_); + isEmpty = onlineDevices_.empty(); + } + if (isEmpty) { + LOGI("[AutoLaunch] GetKVConnectionInEnable no online device, ReleaseDatabaseConnection"); + IKvDBConnection *kvConn = static_cast(autoLaunchItem.conn); + errCode = KvDBManager::ReleaseDatabaseConnection(kvConn); + if (errCode != E_OK) { + LOGE("[AutoLaunch] GetKVConnectionInEnable ReleaseDatabaseConnection failed errCode:%d", errCode); + EraseAutoLauchItem(identifier, userId); + return errCode; + } + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IDLE; + return E_OK; + } + errCode = RegisterObserverAndLifeCycleCallback(autoLaunchItem, identifier, false); + if (errCode == E_OK) { + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IDLE; + autoLaunchItemMap_[identifier][userId].conn = autoLaunchItem.conn; + autoLaunchItemMap_[identifier][userId].observerHandle = autoLaunchItem.observerHandle; + } else { + LOGE("[AutoLaunch] GetKVConnectionInEnable RegisterObserverAndLifeCycleCallback err, do CloseConnection"); + TryCloseConnection(autoLaunchItem); // do nothing if failed + EraseAutoLauchItem(identifier, userId); + } + return errCode; +} + +// we will return errCode, if errCode != E_OK +int AutoLaunch::CloseConnectionStrict(AutoLaunchItem &autoLaunchItem) +{ + LOGI("[AutoLaunch] CloseConnectionStrict"); + if (autoLaunchItem.conn == nullptr) { + LOGI("[AutoLaunch] CloseConnectionStrict conn is nullptr, do nothing"); + return E_OK; + } + IKvDBConnection *kvConn = static_cast(autoLaunchItem.conn); + int errCode = kvConn->RegisterLifeCycleCallback(nullptr); + if (errCode != E_OK) { + LOGE("[AutoLaunch] CloseConnectionStrict RegisterLifeCycleCallback failed errCode:%d", errCode); + return errCode; + } + if (autoLaunchItem.observerHandle != nullptr) { + errCode = kvConn->UnRegisterObserver(autoLaunchItem.observerHandle); + if (errCode != E_OK) { + LOGE("[AutoLaunch] CloseConnectionStrict UnRegisterObserver failed errCode:%d", errCode); + return errCode; + } + autoLaunchItem.observerHandle = nullptr; + } + errCode = KvDBManager::ReleaseDatabaseConnection(kvConn); + if (errCode != E_OK) { + LOGE("[AutoLaunch] CloseConnectionStrict ReleaseDatabaseConnection failed errCode:%d", errCode); + } + return errCode; +} + +// before ReleaseDatabaseConnection, if errCode != E_OK, we not return, we try close more +void AutoLaunch::TryCloseConnection(AutoLaunchItem &autoLaunchItem) +{ + LOGI("[AutoLaunch] TryCloseConnection"); + switch (autoLaunchItem.type) { + case DBType::DB_KV: + TryCloseKvConnection(autoLaunchItem); + break; + case DBType::DB_RELATION: + TryCloseRelationConnection(autoLaunchItem); + break; + default: + LOGD("[AutoLaunch] Unknown type[%d] when try to close connection", static_cast(autoLaunchItem.type)); + break; + } +} + +int AutoLaunch::RegisterObserverAndLifeCycleCallback(AutoLaunchItem &autoLaunchItem, const std::string &identifier, + bool isExt) +{ + int errCode = RegisterObserver(autoLaunchItem, identifier, isExt); + if (errCode != E_OK) { + return errCode; + } + LOGI("[AutoLaunch] RegisterObserver ok"); + + errCode = RegisterLifeCycleCallback(autoLaunchItem, identifier, isExt); + if (errCode != E_OK) { + LOGE("[AutoLaunch] RegisterLifeCycleCallback failed, errCode:%d", errCode); + return errCode; + } + LOGI("[AutoLaunch] RegisterLifeCycleCallback ok"); + + errCode = SetConflictNotifier(autoLaunchItem); + if (errCode != E_OK) { + LOGE("[AutoLaunch] SetConflictNotifier failed, errCode:%d", errCode); + return errCode; + } + + return PragmaAutoSync(autoLaunchItem); +} + +int AutoLaunch::RegisterObserver(AutoLaunchItem &autoLaunchItem, const std::string &identifier, bool isExt) +{ + if (autoLaunchItem.conn == nullptr) { + LOGE("[AutoLaunch] autoLaunchItem.conn is nullptr"); + return -E_INTERNAL_ERROR; + } + LOGI("[AutoLaunch] RegisterObserver type=%d", static_cast(autoLaunchItem.type)); + if (autoLaunchItem.type == DBType::DB_RELATION) { + RelationalStoreConnection *conn = static_cast(autoLaunchItem.conn); + conn->RegisterObserverAction([autoLaunchItem](const std::string &changedDevice) { + RelationalStoreChangedDataImpl data(changedDevice); + if (autoLaunchItem.propertiesPtr != nullptr) { + data.SetStoreProperty({ + autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::USER_ID, ""), + autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::APP_ID, ""), + autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::STORE_ID, "") + }); + } + if (autoLaunchItem.storeObserver) { + LOGD("begin to observer onchange, changedDevice=%s", STR_MASK(changedDevice)); + autoLaunchItem.storeObserver->OnChange(data); + } + }); + return E_OK; + } + std::shared_ptr properties = + std::static_pointer_cast(autoLaunchItem.propertiesPtr); + std::string userId = properties->GetStringProp(KvDBProperties::USER_ID, ""); + int errCode; + Key key; + KvDBObserverHandle *observerHandle = nullptr; + IKvDBConnection *kvConn = static_cast(autoLaunchItem.conn); + if (isExt) { + observerHandle = kvConn->RegisterObserver(OBSERVER_CHANGES_FOREIGN, key, + std::bind(&AutoLaunch::ExtObserverFunc, this, std::placeholders::_1, identifier, userId), errCode); + } else { + observerHandle = kvConn->RegisterObserver(OBSERVER_CHANGES_FOREIGN, key, + std::bind(&AutoLaunch::ObserverFunc, this, std::placeholders::_1, identifier, userId), errCode); + } + + if (errCode != E_OK) { + LOGE("[AutoLaunch] RegisterObserver failed:%d!", errCode); + return errCode; + } + autoLaunchItem.observerHandle = observerHandle; + return E_OK; +} + +void AutoLaunch::ObserverFunc(const KvDBCommitNotifyData ¬ifyData, const std::string &identifier, + const std::string &userId) +{ + LOGD("[AutoLaunch] ObserverFunc identifier=%.6s", STR_TO_HEX(identifier)); + AutoLaunchItem autoLaunchItem; + std::string appId; + std::string storeId; + { + std::lock_guard autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) == 0 || autoLaunchItemMap_[identifier].count(userId) == 0) { + LOGE("[AutoLaunch] ObserverFunc err no this identifier in map"); + return; + } + if (autoLaunchItemMap_[identifier][userId].isDisable) { + LOGI("[AutoLaunch] ObserverFunc isDisable, do nothing"); + return; + } + autoLaunchItemMap_[identifier][userId].inObserver = true; + autoLaunchItem.observer = autoLaunchItemMap_[identifier][userId].observer; + autoLaunchItem.isWriteOpenNotified = autoLaunchItemMap_[identifier][userId].isWriteOpenNotified; + autoLaunchItem.notifier = autoLaunchItemMap_[identifier][userId].notifier; + + std::shared_ptr properties = + std::static_pointer_cast(autoLaunchItemMap_[identifier][userId].propertiesPtr); + appId = properties->GetStringProp(KvDBProperties::APP_ID, ""); + storeId = properties->GetStringProp(KvDBProperties::STORE_ID, ""); + } + if (autoLaunchItem.observer != nullptr) { + LOGI("[AutoLaunch] do user observer"); + KvStoreChangedDataImpl data(¬ifyData); + (autoLaunchItem.observer)->OnChange(data); + } + LOGI("[AutoLaunch] in observer autoLaunchItem.isWriteOpenNotified:%d", autoLaunchItem.isWriteOpenNotified); + + if (!autoLaunchItem.isWriteOpenNotified && autoLaunchItem.notifier != nullptr) { + { + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].isWriteOpenNotified = true; + } + AutoLaunchNotifier notifier = autoLaunchItem.notifier; + int retCode = RuntimeContext::GetInstance()->ScheduleTask([notifier, userId, appId, storeId] { + LOGI("[AutoLaunch] notify the user auto opened event"); + notifier(userId, appId, storeId, AutoLaunchStatus::WRITE_OPENED); + }); + if (retCode != E_OK) { + LOGE("[AutoLaunch] ObserverFunc notifier ScheduleTask retCode:%d", retCode); + } + } + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].inObserver = false; + cv_.notify_all(); +} + +int AutoLaunch::DisableKvStoreAutoLaunch(const std::string &normalIdentifier, const std::string &dualTupleIdentifier, + const std::string &userId) +{ + std::string identifier = (autoLaunchItemMap_.count(normalIdentifier) == 0) ? dualTupleIdentifier : normalIdentifier; + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch identifier=%.6s", STR_TO_HEX(identifier)); + AutoLaunchItem autoLaunchItem; + { + std::unique_lock autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) == 0 || autoLaunchItemMap_[identifier].count(userId) == 0) { + LOGE("[AutoLaunch] DisableKvStoreAutoLaunch identifier is not exist!"); + return -E_NOT_FOUND; + } + if (autoLaunchItemMap_[identifier][userId].isDisable) { + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch already disabling in another thread, do nothing here"); + return -E_BUSY; + } + if (autoLaunchItemMap_[identifier][userId].state == AutoLaunchItemState::IN_ENABLE) { + LOGE("[AutoLaunch] DisableKvStoreAutoLaunch enable not return, do not disable!"); + return -E_BUSY; + } + autoLaunchItemMap_[identifier][userId].isDisable = true; + if (autoLaunchItemMap_[identifier][userId].state != AutoLaunchItemState::IDLE) { + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch wait idle"); + cv_.wait(autoLock, [identifier, userId, this] { + return (autoLaunchItemMap_[identifier][userId].state == AutoLaunchItemState::IDLE) && + (!autoLaunchItemMap_[identifier][userId].inObserver); + }); + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch wait idle ok"); + } + autoLaunchItem = autoLaunchItemMap_[identifier][userId]; + } + + int errCode = CloseConnectionStrict(autoLaunchItem); + if (errCode != E_OK) { + LOGE("[AutoLaunch] DisableKvStoreAutoLaunch CloseConnection failed errCode:%d", errCode); + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].isDisable = false; + autoLaunchItemMap_[identifier][userId].observerHandle = autoLaunchItem.observerHandle; + cv_.notify_all(); + return errCode; + } + + EraseAutoLauchItem(identifier, userId); + cv_.notify_all(); + if (autoLaunchItem.isWriteOpenNotified && autoLaunchItem.notifier) { + RuntimeContext::GetInstance()->ScheduleTask([autoLaunchItem] { CloseNotifier(autoLaunchItem); }); + } + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch ok"); + return E_OK; +} + +void AutoLaunch::GetAutoLaunchSyncDevices(const std::string &identifier, std::vector &devices) const +{ + devices.clear(); + devices.shrink_to_fit(); + std::lock_guard autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) == 0) { + LOGD("[AutoLaunch] GetSyncDevices identifier is not exist!"); + return; + } + for (const auto &device : onlineDevices_) { + devices.push_back(device); + } +} + +void AutoLaunch::CloseNotifier(const AutoLaunchItem &autoLaunchItem) +{ + if (autoLaunchItem.notifier) { + std::string userId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::USER_ID, ""); + std::string appId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::APP_ID, ""); + std::string storeId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::STORE_ID, ""); + LOGI("[AutoLaunch] CloseNotifier do autoLaunchItem.notifier"); + autoLaunchItem.notifier(userId, appId, storeId, AutoLaunchStatus::WRITE_CLOSED); + LOGI("[AutoLaunch] CloseNotifier do autoLaunchItem.notifier finished"); + } else { + LOGI("[AutoLaunch] CloseNotifier autoLaunchItem.notifier is nullptr"); + } +} + +void AutoLaunch::ConnectionLifeCycleCallbackTask(const std::string &identifier, const std::string &userId) +{ + LOGI("[AutoLaunch] ConnectionLifeCycleCallbackTask identifier=%.6s", STR_TO_HEX(identifier)); + AutoLaunchItem autoLaunchItem; + { + std::lock_guard autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) == 0 || autoLaunchItemMap_[identifier].count(userId) == 0) { + LOGE("[AutoLaunch] ConnectionLifeCycleCallback identifier is not exist!"); + return; + } + if (autoLaunchItemMap_[identifier][userId].isDisable) { + LOGI("[AutoLaunch] ConnectionLifeCycleCallback isDisable, do nothing"); + return; + } + if (autoLaunchItemMap_[identifier][userId].state != AutoLaunchItemState::IDLE) { + LOGI("[AutoLaunch] ConnectionLifeCycleCallback state:%d is not idle, do nothing", + static_cast(autoLaunchItemMap_[identifier][userId].state)); + return; + } + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IN_LIFE_CYCLE_CALL_BACK; + autoLaunchItem = autoLaunchItemMap_[identifier][userId]; + } + LOGI("[AutoLaunch] ConnectionLifeCycleCallbackTask do CloseConnection"); + TryCloseConnection(autoLaunchItem); // do nothing if failed + LOGI("[AutoLaunch] ConnectionLifeCycleCallback do CloseConnection finished"); + { + std::lock_guard lock(dataLock_); + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IDLE; + autoLaunchItemMap_[identifier][userId].conn = nullptr; + autoLaunchItemMap_[identifier][userId].isWriteOpenNotified = false; + cv_.notify_all(); + LOGI("[AutoLaunch] ConnectionLifeCycleCallback notify_all"); + } + if (autoLaunchItem.isWriteOpenNotified) { + CloseNotifier(autoLaunchItem); + } +} + +void AutoLaunch::ConnectionLifeCycleCallback(const std::string &identifier, const std::string &userId) +{ + LOGI("[AutoLaunch] ConnectionLifeCycleCallback identifier=%.6s", STR_TO_HEX(identifier)); + int errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind(&AutoLaunch::ConnectionLifeCycleCallbackTask, + this, identifier, userId)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] ConnectionLifeCycleCallback ScheduleTask failed"); + } +} + +int AutoLaunch::OpenOneConnection(AutoLaunchItem &autoLaunchItem) +{ + LOGI("[AutoLaunch] GetOneConnection"); + int errCode; + switch (autoLaunchItem.type) { + case DBType::DB_KV: + errCode = OpenKvConnection(autoLaunchItem); + break; + case DBType::DB_RELATION: + errCode = OpenRelationalConnection(autoLaunchItem); + break; + default: + errCode = -E_INVALID_ARGS; + } + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + std::string userId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::USER_ID, ""); + std::string appId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::APP_ID, ""); + std::string storeId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::STORE_ID, ""); + DBDfxAdapter::ReportFault( { DBDfxAdapter::EVENT_OPEN_DATABASE_FAILED, userId, appId, storeId, errCode } ); + } + return errCode; +} + +void AutoLaunch::OnlineCallBack(const std::string &device, bool isConnect) +{ + LOGI("[AutoLaunch] OnlineCallBack device:%s{private}, isConnect:%d", device.c_str(), isConnect); + if (!isConnect) { + std::lock_guard autoLock(dataLock_); + onlineDevices_.erase(device); + return; + } + { + std::lock_guard autoLock(dataLock_); + onlineDevices_.insert(device); + } + + int errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind(&AutoLaunch::OnlineCallBackTask, this)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] OnlineCallBack ScheduleTask failed"); + } +} + +void AutoLaunch::OnlineCallBackTask() +{ + LOGI("[AutoLaunch] OnlineCallBackTask"); + // > + std::map> doOpenMap; + GetDoOpenMap(doOpenMap); + GetConnInDoOpenMap(doOpenMap); + UpdateGlobalMap(doOpenMap); +} + +void AutoLaunch::GetDoOpenMap(std::map> &doOpenMap) +{ + std::lock_guard autoLock(dataLock_); + LOGI("[AutoLaunch] GetDoOpenMap"); + for (auto &items : autoLaunchItemMap_) { + for (auto &iter : items.second) { + std::string userId = iter.second.propertiesPtr->GetStringProp(DBProperties::USER_ID, ""); + std::string appId = iter.second.propertiesPtr->GetStringProp(DBProperties::APP_ID, ""); + std::string storeId = iter.second.propertiesPtr->GetStringProp(DBProperties::STORE_ID, ""); + bool isDualTupleMode = iter.second.propertiesPtr->GetBoolProp(DBProperties::SYNC_DUAL_TUPLE_MODE, false); + if (iter.second.isDisable) { + LOGI("[AutoLaunch] GetDoOpenMap this item isDisable do nothing"); + continue; + } else if (iter.second.state != AutoLaunchItemState::IDLE) { + LOGI("[AutoLaunch] GetDoOpenMap this item state:%d is not idle do nothing", + static_cast(iter.second.state)); + continue; + } else if (iter.second.conn != nullptr) { + LOGI("[AutoLaunch] GetDoOpenMap this item is opened"); + continue; + } else if (isDualTupleMode && !RuntimeContext::GetInstance()->IsSyncerNeedActive(userId, appId, storeId)) { + LOGI("[AutoLaunch] GetDoOpenMap this item no need to open"); + continue; + } else { + doOpenMap[items.first][iter.first] = iter.second; + iter.second.state = AutoLaunchItemState::IN_COMMUNICATOR_CALL_BACK; + LOGI("[AutoLaunch] GetDoOpenMap this item in IN_COMMUNICATOR_CALL_BACK"); + } + } + } +} + +void AutoLaunch::GetConnInDoOpenMap(std::map> &doOpenMap) +{ + LOGI("[AutoLaunch] GetConnInDoOpenMap doOpenMap.size():%zu", doOpenMap.size()); + if (doOpenMap.empty()) { + return; + } + uint32_t totalSize = 0; + for (auto &items : doOpenMap) { + totalSize += items.second.size(); + } + SemaphoreUtils sema(1 - totalSize); + for (auto &items : doOpenMap) { + for (auto &iter : items.second) { + int errCode = RuntimeContext::GetInstance()->ScheduleTask([&sema, &iter, &items, this] { + int ret = OpenOneConnection(iter.second); + LOGI("[AutoLaunch] GetConnInDoOpenMap GetOneConnection errCode:%d\n", ret); + if (iter.second.conn == nullptr) { + sema.SendSemaphore(); + LOGI("[AutoLaunch] GetConnInDoOpenMap in open thread finish SendSemaphore"); + return; + } + ret = RegisterObserverAndLifeCycleCallback(iter.second, items.first, false); + if (ret != E_OK) { + LOGE("[AutoLaunch] GetConnInDoOpenMap failed, we do CloseConnection"); + TryCloseConnection(iter.second); // if here failed, do nothing + iter.second.conn = nullptr; + } + sema.SendSemaphore(); + LOGI("[AutoLaunch] GetConnInDoOpenMap in open thread finish SendSemaphore"); + }); + if (errCode != E_OK) { + LOGE("[AutoLaunch] GetConnInDoOpenMap ScheduleTask failed, SendSemaphore"); + sema.SendSemaphore(); + } + } + } + LOGI("[AutoLaunch] GetConnInDoOpenMap WaitSemaphore"); + sema.WaitSemaphore(); + LOGI("[AutoLaunch] GetConnInDoOpenMap WaitSemaphore ok"); +} + +void AutoLaunch::UpdateGlobalMap(std::map> &doOpenMap) +{ + std::lock_guard autoLock(dataLock_); + LOGI("[AutoLaunch] UpdateGlobalMap"); + for (auto &items : doOpenMap) { + for (auto &iter : items.second) { + if (iter.second.conn != nullptr) { + autoLaunchItemMap_[items.first][iter.first].conn = iter.second.conn; + autoLaunchItemMap_[items.first][iter.first].observerHandle = iter.second.observerHandle; + autoLaunchItemMap_[items.first][iter.first].isWriteOpenNotified = false; + LOGI("[AutoLaunch] UpdateGlobalMap opened conn update map"); + } + autoLaunchItemMap_[items.first][iter.first].state = AutoLaunchItemState::IDLE; + LOGI("[AutoLaunch] UpdateGlobalMap opened conn set state IDLE"); + } + } + cv_.notify_all(); + LOGI("[AutoLaunch] UpdateGlobalMap finish notify_all"); +} + +void AutoLaunch::ReceiveUnknownIdentifierCallBackTask(const std::string &identifier, const std::string &userId) +{ + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBackTask identifier=%.6s", STR_TO_HEX(identifier)); + AutoLaunchItem autoLaunchItem; + { + std::lock_guard autoLock(dataLock_); + autoLaunchItem = autoLaunchItemMap_[identifier][userId]; + } + int errCode = OpenOneConnection(autoLaunchItem); + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack GetOneConnection errCode:%d\n", errCode); + if (autoLaunchItem.conn == nullptr) { + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IDLE; + cv_.notify_all(); + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBackTask set state IDLE"); + return; + } + errCode = RegisterObserverAndLifeCycleCallback(autoLaunchItem, identifier, false); + if (errCode != E_OK) { + LOGE("[AutoLaunch] ReceiveUnknownIdentifierCallBackTask RegisterObserverAndLifeCycleCallback failed"); + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBackTask do CloseConnection"); + TryCloseConnection(autoLaunchItem); // if here failed, do nothing + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IDLE; + cv_.notify_all(); + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBackTask set state IDLE"); + return; + } + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].conn = autoLaunchItem.conn; + autoLaunchItemMap_[identifier][userId].observerHandle = autoLaunchItem.observerHandle; + autoLaunchItemMap_[identifier][userId].isWriteOpenNotified = false; + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IDLE; + cv_.notify_all(); + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBackTask conn opened set state IDLE"); +} + +int AutoLaunch::ReceiveUnknownIdentifierCallBack(const LabelType &label, const std::string &originalUserId) +{ + const std::string identifier(label.begin(), label.end()); + // originalUserId size maybe 0 + std::string userId = originalUserId; + if (originalUserId.size() == 0 && autoLaunchItemMap_.count(identifier) != 0 && + autoLaunchItemMap_[identifier].size() > 1) { + LOGE("[AutoLaunch] normal tuple mode userId larger than one userId"); + goto EXT; + } + if (originalUserId.size() == 0 && autoLaunchItemMap_.count(identifier) != 0 && + autoLaunchItemMap_[identifier].size() == 1) { + // normal tuple mode + userId = autoLaunchItemMap_[identifier].begin()->first; + } + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack identifier=%.6s", STR_TO_HEX(identifier)); + int errCode; + { + std::lock_guard autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) == 0 || autoLaunchItemMap_[identifier].count(userId) == 0) { + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack not find identifier"); + goto EXT; + } else if (autoLaunchItemMap_[identifier][userId].isDisable) { + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack isDisable ,do nothing"); + return -E_NOT_FOUND; // not E_OK is ok for communicator + } else if (autoLaunchItemMap_[identifier][userId].conn != nullptr) { + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack conn is not nullptr"); + return E_OK; + } else if (autoLaunchItemMap_[identifier][userId].state != AutoLaunchItemState::IDLE) { + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack state:%d is not idle, do nothing", + static_cast(autoLaunchItemMap_[identifier][userId].state)); + return E_OK; + } + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IN_COMMUNICATOR_CALL_BACK; + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack set state IN_COMMUNICATOR_CALL_BACK"); + } + + errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind( + &AutoLaunch::ReceiveUnknownIdentifierCallBackTask, this, identifier, userId)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] ReceiveUnknownIdentifierCallBack ScheduleTask failed"); + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier][userId].state = AutoLaunchItemState::IDLE; + } + return errCode; + +EXT: + return AutoLaunchExt(identifier, userId); +} + +void AutoLaunch::SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback, DBType type) +{ + LOGI("[AutoLaunch] SetAutoLaunchRequestCallback type[%d]", static_cast(type)); + std::lock_guard lock(extLock_); + if (callback) { + autoLaunchRequestCallbackMap_[type] = callback; + } else if (autoLaunchRequestCallbackMap_.find(type) != autoLaunchRequestCallbackMap_.end()) { + autoLaunchRequestCallbackMap_.erase(type); + } +} + +int AutoLaunch::AutoLaunchExt(const std::string &identifier, const std::string &userId) +{ + AutoLaunchParam param; + // for non dual tuple mode, userId is "" + param.userId = userId; + DBType openType = DBType::DB_INVALID; + int errCode = ExtAutoLaunchRequestCallBack(identifier, param, openType); + if (errCode != E_OK) { + return errCode; // not E_OK is ok for communicator + } + + std::shared_ptr ptr; + errCode = AutoLaunch::GetAutoLaunchProperties(param, openType, false, ptr); + if (errCode != E_OK) { + LOGE("[AutoLaunch] AutoLaunchExt param check fail errCode:%d", errCode); + if (!param.notifier) { + return errCode; + } + int retCode = RuntimeContext::GetInstance()->ScheduleTask([param] { + param.notifier(param.userId, param.appId, param.storeId, INVALID_PARAM); + }); + if (retCode != E_OK) { + LOGE("[AutoLaunch] AutoLaunchExt notifier ScheduleTask retCode:%d", retCode); + } + return errCode; + } + AutoLaunchItem autoLaunchItem{ptr, param.notifier, param.option.observer, param.option.conflictType, + param.option.notifier}; + autoLaunchItem.isAutoSync = param.option.isAutoSync; + autoLaunchItem.type = openType; + autoLaunchItem.storeObserver = param.option.storeObserver; + errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind(&AutoLaunch::AutoLaunchExtTask, this, + identifier, param.userId, autoLaunchItem)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] AutoLaunchExt ScheduleTask errCode:%d", errCode); + } + return errCode; +} + +void AutoLaunch::AutoLaunchExtTask(const std::string &identifier, const std::string &userId, + AutoLaunchItem &autoLaunchItem) +{ + { + std::lock_guard autoLock(extLock_); + if (extItemMap_.count(identifier) != 0 && extItemMap_[identifier].count(userId) != 0) { + LOGE("[AutoLaunch] extItemMap has this identifier"); + return; + } + extItemMap_[identifier][userId] = autoLaunchItem; + } + bool abort = false; + do { + int errCode = CheckAutoLaunchRealPath(autoLaunchItem); + if (errCode != E_OK) { + abort = true; + break; + } + errCode = OpenOneConnection(autoLaunchItem); + LOGI("[AutoLaunch] AutoLaunchExtTask GetOneConnection errCode:%d", errCode); + if (autoLaunchItem.conn == nullptr) { + abort = true; + break; + } + errCode = RegisterObserverAndLifeCycleCallback(autoLaunchItem, identifier, true); + if (errCode != E_OK) { + LOGE("[AutoLaunch] AutoLaunchExtTask RegisterObserverAndLifeCycleCallback failed"); + TryCloseConnection(autoLaunchItem); // if here failed, do nothing + abort = true; + } + } while (false); + if (abort) { + std::lock_guard autoLock(extLock_); + extItemMap_[identifier].erase(userId); + if (extItemMap_[identifier].size() == 0) { + extItemMap_.erase(identifier); + } + return; + } + std::lock_guard autoLock(extLock_); + extItemMap_[identifier][userId].conn = autoLaunchItem.conn; + extItemMap_[identifier][userId].observerHandle = autoLaunchItem.observerHandle; + extItemMap_[identifier][userId].isWriteOpenNotified = false; + LOGI("[AutoLaunch] AutoLaunchExtTask ok"); +} + +void AutoLaunch::ExtObserverFunc(const KvDBCommitNotifyData ¬ifyData, const std::string &identifier, + const std::string &userId) +{ + LOGD("[AutoLaunch] ExtObserverFunc identifier=%.6s", STR_TO_HEX(identifier)); + AutoLaunchItem autoLaunchItem; + AutoLaunchNotifier notifier; + { + std::lock_guard autoLock(extLock_); + if (extItemMap_.count(identifier) == 0 || extItemMap_[identifier].count(userId) == 0) { + LOGE("[AutoLaunch] ExtObserverFunc this identifier not in map"); + return; + } + autoLaunchItem = extItemMap_[identifier][userId]; + } + if (autoLaunchItem.observer != nullptr) { + LOGD("[AutoLaunch] do user observer"); + KvStoreChangedDataImpl data(¬ifyData); + autoLaunchItem.observer->OnChange(data); + } + + { + std::lock_guard autoLock(extLock_); + if (extItemMap_.count(identifier) != 0 && extItemMap_[identifier].count(userId) != 0 && + !extItemMap_[identifier][userId].isWriteOpenNotified && + autoLaunchItem.notifier != nullptr) { + extItemMap_[identifier][userId].isWriteOpenNotified = true; + notifier = autoLaunchItem.notifier; + } else { + return; + } + } + + std::string appId = autoLaunchItem.propertiesPtr->GetStringProp(KvDBProperties::APP_ID, ""); + std::string storeId = autoLaunchItem.propertiesPtr->GetStringProp(KvDBProperties::STORE_ID, ""); + int retCode = RuntimeContext::GetInstance()->ScheduleTask([notifier, userId, appId, storeId] { + LOGI("[AutoLaunch] ExtObserverFunc do user notifier WRITE_OPENED"); + notifier(userId, appId, storeId, AutoLaunchStatus::WRITE_OPENED); + }); + if (retCode != E_OK) { + LOGE("[AutoLaunch] ExtObserverFunc notifier ScheduleTask retCode:%d", retCode); + } +} + +void AutoLaunch::ExtConnectionLifeCycleCallback(const std::string &identifier, const std::string &userId) +{ + LOGI("[AutoLaunch] ExtConnectionLifeCycleCallback identifier=%.6s", STR_TO_HEX(identifier)); + int errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind( + &AutoLaunch::ExtConnectionLifeCycleCallbackTask, this, identifier, userId)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] ExtConnectionLifeCycleCallback ScheduleTask failed"); + } +} + +void AutoLaunch::ExtConnectionLifeCycleCallbackTask(const std::string &identifier, const std::string &userId) +{ + LOGI("[AutoLaunch] ExtConnectionLifeCycleCallbackTask identifier=%.6s", STR_TO_HEX(identifier)); + AutoLaunchItem autoLaunchItem; + { + std::lock_guard autoLock(extLock_); + if (extItemMap_.count(identifier) == 0 || extItemMap_[identifier].count(userId) == 0) { + LOGE("[AutoLaunch] ExtConnectionLifeCycleCallbackTask identifier is not exist!"); + return; + } + autoLaunchItem = extItemMap_[identifier][userId]; + extItemMap_[identifier].erase(userId); + if (extItemMap_[identifier].size() == 0) { + extItemMap_.erase(identifier); + } + } + LOGI("[AutoLaunch] ExtConnectionLifeCycleCallbackTask do CloseConnection"); + TryCloseConnection(autoLaunchItem); // do nothing if failed + if (autoLaunchItem.isWriteOpenNotified) { + CloseNotifier(autoLaunchItem); + } +} + +int AutoLaunch::SetConflictNotifier(AutoLaunchItem &autoLaunchItem) +{ + if (autoLaunchItem.type != DBType::DB_KV) { + LOGD("[AutoLaunch] Current Type[%d] Not Support ConflictNotifier Now", static_cast(autoLaunchItem.type)); + return E_OK; + } + + IKvDBConnection *kvConn = static_cast(autoLaunchItem.conn); + int conflictType = autoLaunchItem.conflictType; + const KvStoreNbConflictNotifier ¬ifier = autoLaunchItem.conflictNotifier; + if (conflictType == 0) { + return E_OK; + } + int errCode; + if (!notifier) { + errCode = kvConn->SetConflictNotifier(conflictType, nullptr); + goto END; + } + + errCode = kvConn->SetConflictNotifier(conflictType, + [conflictType, notifier](const KvDBCommitNotifyData &data) { + int resultCode; + const std::list entries = data.GetCommitConflicts(resultCode); + if (resultCode != E_OK) { + LOGE("Get commit conflicted entries failed:%d!", resultCode); + return; + } + + for (const auto &entry : entries) { + // Prohibit signed numbers to perform bit operations + uint32_t entryType = static_cast(entry.type); + uint32_t type = static_cast(conflictType); + if ((entryType & type) != 0) { + KvStoreNbConflictDataImpl dataImpl; + dataImpl.SetConflictData(entry); + notifier(dataImpl); + } + } + }); + +END: + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Register conflict failed:%d!", errCode); + } + return errCode; +} + +int AutoLaunch::GetAutoLaunchProperties(const AutoLaunchParam ¶m, const DBType &openType, bool checkDir, + std::shared_ptr &propertiesPtr) +{ + switch (openType) { + case DBType::DB_KV: { + propertiesPtr = std::make_shared(); + std::shared_ptr kvPtr = std::static_pointer_cast(propertiesPtr); + return GetAutoLaunchKVProperties(param, kvPtr, checkDir); + } + case DBType::DB_RELATION: { + propertiesPtr = std::make_shared(); + std::shared_ptr rdbPtr = + std::static_pointer_cast(propertiesPtr); + return GetAutoLaunchRelationProperties(param, rdbPtr); + } + default: + return -E_INVALID_ARGS; + } +} + +int AutoLaunch::GetAutoLaunchKVProperties(const AutoLaunchParam ¶m, + const std::shared_ptr &propertiesPtr, bool checkDir) +{ + SchemaObject schemaObject; + std::string canonicalDir; + int errCode = ParamCheckUtils::CheckAndTransferAutoLaunchParam(param, checkDir, schemaObject, canonicalDir); + if (errCode != E_OK) { + return errCode; + } + + if (param.option.isEncryptedDb) { + propertiesPtr->SetPassword(param.option.cipher, param.option.passwd); + } + propertiesPtr->SetStringProp(KvDBProperties::DATA_DIR, canonicalDir); + propertiesPtr->SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, param.option.createIfNecessary); + propertiesPtr->SetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, param.option.createDirByStoreIdOnly); + propertiesPtr->SetBoolProp(KvDBProperties::MEMORY_MODE, false); + propertiesPtr->SetBoolProp(KvDBProperties::ENCRYPTED_MODE, param.option.isEncryptedDb); + propertiesPtr->SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + propertiesPtr->SetSchema(schemaObject); + if (RuntimeContext::GetInstance()->IsProcessSystemApiAdapterValid()) { + propertiesPtr->SetIntProp(KvDBProperties::SECURITY_LABEL, param.option.secOption.securityLabel); + propertiesPtr->SetIntProp(KvDBProperties::SECURITY_FLAG, param.option.secOption.securityFlag); + } + propertiesPtr->SetBoolProp(KvDBProperties::COMPRESS_ON_SYNC, param.option.isNeedCompressOnSync); + if (param.option.isNeedCompressOnSync) { + propertiesPtr->SetIntProp(KvDBProperties::COMPRESSION_RATE, + ParamCheckUtils::GetValidCompressionRate(param.option.compressionRate)); + } + propertiesPtr->SetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, param.option.syncDualTupleMode); + DBCommon::SetDatabaseIds(*propertiesPtr, param.appId, param.userId, param.storeId); + return E_OK; +} + +int AutoLaunch::GetAutoLaunchRelationProperties(const AutoLaunchParam ¶m, + const std::shared_ptr &propertiesPtr) +{ + if (!ParamCheckUtils::CheckStoreParameter(param.storeId, param.appId, param.userId)) { + LOGE("[AutoLaunch] CheckStoreParameter is invalid."); + return -E_INVALID_ARGS; + } + propertiesPtr->SetStringProp(RelationalDBProperties::DATA_DIR, param.path); + propertiesPtr->SetIdentifier(param.userId, param.appId, param.storeId); + return E_OK; +} + +int AutoLaunch::ExtAutoLaunchRequestCallBack(const std::string &identifier, AutoLaunchParam ¶m, DBType &openType) +{ + std::lock_guard lock(extLock_); + if (autoLaunchRequestCallbackMap_.empty()) { + LOGI("[AutoLaunch] autoLaunchRequestCallbackMap_ is empty"); + return -E_NOT_FOUND; // not E_OK is ok for communicator + } + + bool needOpen = false; + for (const auto &[type, callBack] : autoLaunchRequestCallbackMap_) { + needOpen = callBack(identifier, param); + if (needOpen) { + openType = type; + break; + } + } + + if (!needOpen) { + LOGI("[AutoLaunch] autoLaunchRequestCallback is not need open"); + return -E_NOT_FOUND; // not E_OK is ok for communicator + } + // inner error happened + if (openType >= DBType::DB_INVALID) { + LOGW("[AutoLaunch] Unknown DB Type, Ignore the open request"); + return -E_NOT_FOUND; // not E_OK is ok for communicator + } + return E_OK; +} + +int AutoLaunch::OpenKvConnection(AutoLaunchItem &autoLaunchItem) +{ + std::shared_ptr properties = + std::static_pointer_cast(autoLaunchItem.propertiesPtr); + int errCode = E_OK; + IKvDBConnection *conn = KvDBManager::GetDatabaseConnection(*properties, errCode, false); + if (errCode == -E_ALREADY_OPENED) { + LOGI("[AutoLaunch] GetOneConnection user already getkvstore by self"); + } else if (conn == nullptr) { + LOGE("[AutoLaunch] GetOneConnection GetDatabaseConnection failed errCode:%d", errCode); + } + autoLaunchItem.conn = conn; + return errCode; +} + +int AutoLaunch::OpenRelationalConnection(AutoLaunchItem &autoLaunchItem) +{ + std::shared_ptr properties = + std::static_pointer_cast(autoLaunchItem.propertiesPtr); + int errCode = E_OK; + auto conn = RelationalStoreInstance::GetDatabaseConnection(*properties, errCode); + if (errCode == -E_ALREADY_OPENED) { + LOGI("[AutoLaunch] GetOneConnection user already openstore by self"); + } else if (conn == nullptr) { + LOGE("[AutoLaunch] GetOneConnection GetDatabaseConnection failed errCode:%d", errCode); + } + autoLaunchItem.conn = conn; + return errCode; +} + +int AutoLaunch::RegisterLifeCycleCallback(AutoLaunchItem &autoLaunchItem, const std::string &identifier, + bool isExt) +{ + int errCode = E_OK; + DatabaseLifeCycleNotifier notifier; + if (isExt) { + notifier = std::bind( + &AutoLaunch::ExtConnectionLifeCycleCallback, this, std::placeholders::_1, std::placeholders::_2); + } else { + notifier = std::bind(&AutoLaunch::ConnectionLifeCycleCallback, + this, std::placeholders::_1, std::placeholders::_2); + } + switch (autoLaunchItem.type) { + case DBType::DB_KV: + errCode = static_cast(autoLaunchItem.conn)->RegisterLifeCycleCallback(notifier); + break; + case DBType::DB_RELATION: + errCode = + static_cast(autoLaunchItem.conn)->RegisterLifeCycleCallback(notifier); + break; + default: + LOGD("[AutoLaunch] Unknown Type[%d]", static_cast(autoLaunchItem.type)); + break; + } + return errCode; +} + +int AutoLaunch::PragmaAutoSync(AutoLaunchItem &autoLaunchItem) +{ + int errCode = E_OK; + if (autoLaunchItem.type != DBType::DB_KV) { + LOGD("[AutoLaunch] Current Type[%d] Not Support AutoSync Now", static_cast(autoLaunchItem.type)); + return errCode; + } + + bool enAutoSync = autoLaunchItem.isAutoSync; + errCode = static_cast(autoLaunchItem.conn)->Pragma(PRAGMA_AUTO_SYNC, + static_cast(&enAutoSync)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] PRAGMA_AUTO_SYNC failed, errCode:%d", errCode); + return errCode; + } + LOGI("[AutoLaunch] set PRAGMA_AUTO_SYNC ok, enAutoSync=%d", enAutoSync); + return errCode; +} + +void AutoLaunch::TryCloseKvConnection(AutoLaunchItem &autoLaunchItem) +{ + LOGI("[AutoLaunch] TryCloseKvConnection"); + if (autoLaunchItem.conn == nullptr) { + LOGI("[AutoLaunch] TryCloseKvConnection conn is nullptr, do nothing"); + return; + } + IKvDBConnection *kvConn = static_cast(autoLaunchItem.conn); + int errCode = kvConn->RegisterLifeCycleCallback(nullptr); + if (errCode != E_OK) { + LOGE("[AutoLaunch] TryCloseKvConnection RegisterLifeCycleCallback failed errCode:%d", errCode); + } + if (autoLaunchItem.observerHandle != nullptr) { + errCode = kvConn->UnRegisterObserver(autoLaunchItem.observerHandle); + if (errCode != E_OK) { + LOGE("[AutoLaunch] TryCloseKvConnection UnRegisterObserver failed errCode:%d", errCode); + } + autoLaunchItem.observerHandle = nullptr; + } + errCode = KvDBManager::ReleaseDatabaseConnection(kvConn); + if (errCode != E_OK) { + LOGE("[AutoLaunch] TryCloseKvConnection ReleaseDatabaseConnection failed errCode:%d", errCode); + } +} + +void AutoLaunch::TryCloseRelationConnection(AutoLaunchItem &autoLaunchItem) +{ + LOGI("[AutoLaunch] TryCloseRelationConnection"); + if (autoLaunchItem.conn == nullptr) { + LOGI("[AutoLaunch] TryCloseRelationConnection conn is nullptr, do nothing"); + return; + } + RelationalStoreConnection *rdbConn = static_cast(autoLaunchItem.conn); + int errCode = rdbConn->RegisterLifeCycleCallback(nullptr); + if (errCode != E_OK) { + LOGE("[AutoLaunch] TryCloseRelationConnection RegisterLifeCycleCallback failed errCode:%d", errCode); + } + errCode = rdbConn->Close(); + if (errCode != E_OK) { + LOGE("[AutoLaunch] TryCloseRelationConnection close connection failed errCode:%d", errCode); + } +} + +void AutoLaunch::EraseAutoLauchItem(const std::string &identifier, const std::string &userId) +{ + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier].erase(userId); + if (autoLaunchItemMap_[identifier].empty()) { + autoLaunchItemMap_.erase(identifier); + } +} + +void AutoLaunch::NotifyInvalidParam(const AutoLaunchItem &autoLaunchItem) +{ + if (!autoLaunchItem.notifier) { + return; + } + int retCode = RuntimeContext::GetInstance()->ScheduleTask([autoLaunchItem] { + std::string userId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::USER_ID, ""); + std::string appId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::APP_ID, ""); + std::string storeId = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::STORE_ID, ""); + autoLaunchItem.notifier(userId, appId, storeId, INVALID_PARAM); + }); + if (retCode != E_OK) { + LOGE("[AutoLaunch] AutoLaunchExt notifier ScheduleTask retCode:%d", retCode); + } +} + +int AutoLaunch::CheckAutoLaunchRealPath(const AutoLaunchItem &autoLaunchItem) +{ + std::string canonicalDir; + std::string dataDir = autoLaunchItem.propertiesPtr->GetStringProp(DBProperties::DATA_DIR, ""); + if (!ParamCheckUtils::CheckDataDir(dataDir, canonicalDir)) { + LOGE("[AutoLaunch] CheckDataDir is invalid Auto Launch failed."); + NotifyInvalidParam(autoLaunchItem); + return -E_INVALID_ARGS; + } + autoLaunchItem.propertiesPtr->SetStringProp(DBProperties::DATA_DIR, canonicalDir); + return E_OK; +} + +void AutoLaunch::Dump(int fd) +{ + std::lock_guard lock(dataLock_); + DBDumpHelper::Dump(fd, "\tenableAutoLaunch info [\n"); + for (const auto &[label, userItem] : autoLaunchItemMap_) { + DBDumpHelper::Dump(fd, "\t\tlabel = %s, userId = [\n", DBCommon::TransferStringToHex(label).c_str()); + for (const auto &entry : userItem) { + DBDumpHelper::Dump(fd, "\t\t\t%s\n", entry.first.c_str()); + } + DBDumpHelper::Dump(fd, "\t\t]\n"); + } + DBDumpHelper::Dump(fd, "\t]\n"); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/data_compression.cpp b/mock/distributeddb/common/src/data_compression.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c0b221f062821856669d18cafdd8e6878dd4a09a --- /dev/null +++ b/mock/distributeddb/common/src/data_compression.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "data_compression.h" +#include +#include "db_errno.h" + +namespace DistributedDB { +void DataCompression::GetCompressionAlgo(std::set &algorithmSet) +{ + algorithmSet.clear(); + for (const auto &item : GetCompressionAlgos()) { + algorithmSet.insert(item.first); + } +} + +int DataCompression::TransferCompressionAlgo(uint32_t compressAlgoType, CompressAlgorithm &algoType) +{ + auto iter = GetTransMap().find(compressAlgoType); + if (iter == GetTransMap().end()) { + return -E_INVALID_ARGS; + } + algoType = iter->second; + return E_OK; +} + +DataCompression *DataCompression::GetInstance(CompressAlgorithm algo) +{ + auto iter = GetCompressionAlgos().find(algo); + if (iter == GetCompressionAlgos().end()) { + return nullptr; + } + return iter->second; +} + +// All supported compression algorithm should call this function to register their instance. +void DataCompression::Register(CompressAlgorithm algo, DataCompression *compressionPtr) +{ + if (GetInstance(algo) != nullptr) { + return; + } + GetCompressionAlgos().insert({algo, compressionPtr}); + GetTransMap().insert({static_cast(algo), algo}); +} + +std::map &DataCompression::GetCompressionAlgos() +{ + static std::map compressionAlgos; + return compressionAlgos; +} + +std::map &DataCompression::GetTransMap() +{ + static std::map transferMap; + return transferMap; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/data_value.cpp b/mock/distributeddb/common/src/data_value.cpp new file mode 100644 index 0000000000000000000000000000000000000000..81929ae3405816518ed0d603f7cede662d08df98 --- /dev/null +++ b/mock/distributeddb/common/src/data_value.cpp @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "data_value.h" + +#include "db_errno.h" +#include "relational_schema_object.h" +#include "securec.h" + +namespace DistributedDB { +Blob::Blob() : ptr_(nullptr), size_(0) +{ +} + +Blob::~Blob() +{ + if (ptr_ != nullptr) { + delete[] ptr_; + ptr_ = nullptr; + } + size_ = 0; +} + +Blob::Blob(Blob &&blob) : ptr_(blob.ptr_), size_(blob.size_) +{ + blob.ptr_ = nullptr; + blob.size_ = 0; +} + +Blob &Blob::operator=(Blob &&blob) noexcept +{ + if (&blob != this) { + delete[] ptr_; + ptr_ = blob.ptr_; + size_ = blob.size_; + blob.ptr_ = nullptr; + blob.size_ = 0; + } + return *this; +} + +const uint8_t *Blob::GetData() const +{ + return ptr_; +} + +uint32_t Blob::GetSize() const +{ + return size_; +} + +int Blob::WriteBlob(const uint8_t *ptrArray, const uint32_t &size) +{ + if (ptrArray == nullptr || size == 0) { + return E_OK; + } + + delete[] ptr_; + ptr_ = nullptr; + + ptr_ = new (std::nothrow) uint8_t[size]; + if (ptr_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + errno_t errCode = memcpy_s(ptr_, size, ptrArray, size); + if (errCode != EOK) { + return -E_SECUREC_ERROR; + } + size_ = size; + return E_OK; +} + +DataValue::DataValue() : type_(StorageType::STORAGE_TYPE_NULL) +{ + value_.zeroMem = nullptr; +} + +DataValue::~DataValue() +{ + ResetValue(); +} + +DataValue::DataValue(const DataValue &dataValue) +{ + *this = dataValue; +} + +DataValue::DataValue(DataValue &&dataValue) noexcept +{ + *this = std::move(dataValue); +} + +DataValue &DataValue::operator=(const DataValue &dataValue) +{ + if (&dataValue == this) { + return *this; + } + ResetValue(); + switch (dataValue.type_) { + case StorageType::STORAGE_TYPE_INTEGER: + (void)dataValue.GetInt64(this->value_.iValue); + break; + case StorageType::STORAGE_TYPE_REAL: + (void)dataValue.GetDouble(this->value_.dValue); + break; + case StorageType::STORAGE_TYPE_BLOB: + case StorageType::STORAGE_TYPE_TEXT: + (void)dataValue.GetBlob(this->value_.blobPtr); + break; + default: + break; + } + type_ = dataValue.type_; + return *this; +} + +DataValue &DataValue::operator=(DataValue &&dataValue) noexcept +{ + if (&dataValue == this) { + return *this; + } + ResetValue(); + this->type_ = dataValue.type_; + this->value_ = dataValue.value_; + switch (type_) { + case StorageType::STORAGE_TYPE_BLOB: + case StorageType::STORAGE_TYPE_TEXT: + dataValue.value_.blobPtr = nullptr; + break; + default: + break; + } + return *this; +} + +DataValue &DataValue::operator=(int64_t intVal) +{ + ResetValue(); + type_ = StorageType::STORAGE_TYPE_INTEGER; + value_.iValue = intVal; + return *this; +} + +DataValue &DataValue::operator=(double doubleVal) +{ + ResetValue(); + type_ = StorageType::STORAGE_TYPE_REAL; + value_.dValue = doubleVal; + return *this; +} + +DataValue &DataValue::operator=(const Blob &blob) +{ + (void)SetBlob(blob); + return *this; +} + +int DataValue::Set(Blob *&blob) +{ + ResetValue(); + if (blob == nullptr || blob->GetSize() <= 0) { + LOGE("Transfer Blob to DataValue failed."); + return -E_INVALID_ARGS; + } + type_ = StorageType::STORAGE_TYPE_BLOB; + value_.blobPtr = blob; + blob = nullptr; + return E_OK; +} + +DataValue &DataValue::operator=(const std::string &string) +{ + (void)SetText(string); + return *this; +} + +bool DataValue::operator==(const DataValue &dataValue) const +{ + if (dataValue.type_ != type_) { + return false; + } + switch (type_) { + case StorageType::STORAGE_TYPE_INTEGER: + return dataValue.value_.iValue == value_.iValue; + case StorageType::STORAGE_TYPE_REAL: + return dataValue.value_.dValue == value_.dValue; + case StorageType::STORAGE_TYPE_BLOB: + case StorageType::STORAGE_TYPE_TEXT: + if (dataValue.value_.blobPtr->GetSize() != value_.blobPtr->GetSize()) { + return false; + } + for (uint32_t i = 0; i < dataValue.value_.blobPtr->GetSize(); ++i) { + if (dataValue.value_.blobPtr->GetData()[i] != value_.blobPtr->GetData()[i]) { + return false; + } + } + return true; + default: + return true; + } +} + +bool DataValue::operator!=(const DataValue &dataValue) const +{ + return !(*this == dataValue); +} + +int DataValue::GetDouble(double &outVal) const +{ + if (type_ != StorageType::STORAGE_TYPE_REAL) { + return -E_NOT_SUPPORT; + } + outVal = value_.dValue; + return E_OK; +} + +int DataValue::GetInt64(int64_t &outVal) const +{ + if (type_ != StorageType::STORAGE_TYPE_INTEGER) { + return -E_NOT_SUPPORT; + } + outVal = value_.iValue; + return E_OK; +} + +int DataValue::GetBlob(Blob *&outVal) const +{ + if (type_ != StorageType::STORAGE_TYPE_BLOB && type_ != StorageType::STORAGE_TYPE_TEXT) { + return -E_NOT_SUPPORT; + } + delete outVal; + outVal = nullptr; + outVal = new (std::nothrow) Blob(); + if (outVal == nullptr) { + return -E_OUT_OF_MEMORY; + } + return outVal->WriteBlob(value_.blobPtr->GetData(), value_.blobPtr->GetSize()); +} + +int DataValue::SetBlob(const Blob &val) +{ + ResetValue(); + if (val.GetSize() <= 0) { + return E_OK; + } + value_.blobPtr = new(std::nothrow) Blob(); + if (value_.blobPtr == nullptr) { + return -E_OUT_OF_MEMORY; + } + type_ = StorageType::STORAGE_TYPE_BLOB; + int errCode = E_OK; + if (val.GetSize() > 0) { + errCode = value_.blobPtr->WriteBlob(val.GetData(), val.GetSize()); + } + return errCode; +} + +int DataValue::GetBlob(Blob &outVal) const +{ + if (type_ != StorageType::STORAGE_TYPE_BLOB && type_ != StorageType::STORAGE_TYPE_TEXT) { + return -E_NOT_SUPPORT; + } + return outVal.WriteBlob(value_.blobPtr->GetData(), value_.blobPtr->GetSize()); +} + +int DataValue::SetText(const std::string &val) +{ + return SetText(reinterpret_cast(val.c_str()), val.length()); +} + +int DataValue::SetText(const uint8_t *val, uint32_t length) +{ + ResetValue(); + value_.blobPtr = new(std::nothrow) Blob(); + if (value_.blobPtr == nullptr) { + return -E_OUT_OF_MEMORY; + } + type_ = StorageType::STORAGE_TYPE_TEXT; + return value_.blobPtr->WriteBlob(val, length); +} + +int DataValue::GetText(std::string &outValue) const +{ + if (type_ != StorageType::STORAGE_TYPE_TEXT) { + return -E_NOT_SUPPORT; + } + const uint8_t *data = value_.blobPtr->GetData(); + uint32_t len = value_.blobPtr->GetSize(); + if (len == 0) { + outValue = ""; + return E_OK; + } + outValue.resize(len); + outValue.assign(data, data + len); + return E_OK; +} + +StorageType DataValue::GetType() const +{ + return type_; +} + +int DataValue::GetBlobLength(uint32_t &length) const +{ + if (type_ != StorageType::STORAGE_TYPE_BLOB && type_ != StorageType::STORAGE_TYPE_TEXT) { + return -E_NOT_SUPPORT; + } + length = value_.blobPtr->GetSize(); + return E_OK; +} + +void DataValue::ResetValue() +{ + switch (type_) { + case StorageType::STORAGE_TYPE_TEXT: + case StorageType::STORAGE_TYPE_BLOB: + delete value_.blobPtr; + value_.blobPtr = nullptr; + break; + case StorageType::STORAGE_TYPE_NULL: + case StorageType::STORAGE_TYPE_INTEGER: + case StorageType::STORAGE_TYPE_REAL: + default: + break; + } + type_ = StorageType::STORAGE_TYPE_NULL; + value_.zeroMem = nullptr; +} + +std::string DataValue::ToString() const +{ + std::string res; + switch (type_) { + case StorageType::STORAGE_TYPE_TEXT: + (void)GetText(res); + break; + case StorageType::STORAGE_TYPE_BLOB: + res = "NOT SUPPORT"; + break; + case StorageType::STORAGE_TYPE_NULL: + res = "null"; + break; + case StorageType::STORAGE_TYPE_INTEGER: + res = std::to_string(value_.iValue); + break; + case StorageType::STORAGE_TYPE_REAL: + res = std::to_string(value_.dValue); + break; + default: + res = "default"; + break; + } + return "[" + res + "]"; +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/common/src/db_common.cpp b/mock/distributeddb/common/src/db_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..96914ab2d50b075ed2910af6c0328cb72d843801 --- /dev/null +++ b/mock/distributeddb/common/src/db_common.cpp @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "db_common.h" + +#include +#include + +#include "db_errno.h" +#include "platform_specific.h" +#include "hash.h" +#include "value_hash_calc.h" + +namespace DistributedDB { +namespace { + void RemoveFiles(const std::list &fileList, OS::FileType type) + { + for (const auto &item : fileList) { + if (item.fileType != type) { + continue; + } + int errCode = OS::RemoveFile(item.fileName.c_str()); + if (errCode != E_OK) { + LOGE("Remove file failed:%d", errno); + } + } + } + + void RemoveDirectories(const std::list &fileList, OS::FileType type) + { + for (auto item = fileList.rbegin(); item != fileList.rend(); ++item) { + if (item->fileType != type) { + continue; + } + int errCode = OS::RemoveDBDirectory(item->fileName); + if (errCode != 0) { + LOGE("Remove directory failed:%d", errno); + } + } + } + const std::string HEX_CHAR_MAP = "0123456789abcdef"; + const std::string CAP_HEX_CHAR_MAP = "0123456789ABCDEF"; +} + +int DBCommon::CreateDirectory(const std::string &directory) +{ + bool isExisted = OS::CheckPathExistence(directory); + if (!isExisted) { + int errCode = OS::MakeDBDirectory(directory); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} + +void DBCommon::StringToVector(const std::string &src, std::vector &dst) +{ + dst.resize(src.size()); + dst.assign(src.begin(), src.end()); +} + +void DBCommon::VectorToString(const std::vector &src, std::string &dst) +{ + dst.clear(); + dst.assign(src.begin(), src.end()); +} + +std::string DBCommon::VectorToHexString(const std::vector &inVec, const std::string &separator) +{ + std::string outString; + for (auto &entry : inVec) { + outString.push_back(CAP_HEX_CHAR_MAP[entry >> 4]); // high 4 bits to one hex. + outString.push_back(CAP_HEX_CHAR_MAP[entry & 0x0F]); // low 4 bits to one hex. + outString += separator; + } + outString.erase(outString.size() - separator.size(), separator.size()); // remove needless separator at last + return outString; +} + +void DBCommon::PrintHexVector(const std::vector &data, int line, const std::string &tag) +{ + const size_t maxDataLength = 1024; + const int byteHexNum = 2; + size_t dataLength = data.size(); + + if (data.size() > maxDataLength) { + dataLength = maxDataLength; + } + + char *buff = new (std::nothrow) char[dataLength * byteHexNum + 1]; // dual and add one for the end; + if (buff == nullptr) { + return; + } + + for (std::vector::size_type i = 0; i < dataLength; ++i) { + buff[byteHexNum * i] = CAP_HEX_CHAR_MAP[data[i] >> 4]; // high 4 bits to one hex. + buff[byteHexNum * i + 1] = CAP_HEX_CHAR_MAP[data[i] & 0x0F]; // low 4 bits to one hex. + } + buff[dataLength * byteHexNum] = '\0'; + + if (line == 0) { + LOGD("[%s] size:%zu -- %s", tag.c_str(), data.size(), buff); + } else { + LOGD("[%s][%d] size:%zu -- %s", tag.c_str(), line, data.size(), buff); + } + + delete []buff; + return; +} + +std::string DBCommon::TransferHashString(const std::string &devName) +{ + if (devName.empty()) { + return ""; + } + std::vector devVect(devName.begin(), devName.end()); + std::vector hashVect; + int errCode = CalcValueHash(devVect, hashVect); + if (errCode != E_OK) { + return ""; + } + + return std::string(hashVect.begin(), hashVect.end()); +} + +std::string DBCommon::TransferStringToHex(const std::string &origStr) +{ + if (origStr.empty()) { + return ""; + } + + std::string tmp; + for (auto item : origStr) { + unsigned char currentByte = static_cast(item); + tmp.push_back(HEX_CHAR_MAP[currentByte >> 4]); // high 4 bits to one hex. + tmp.push_back(HEX_CHAR_MAP[currentByte & 0x0F]); // low 4 bits to one hex. + } + return tmp; +} + +int DBCommon::CalcValueHash(const std::vector &value, std::vector &hashValue) +{ + ValueHashCalc hashCalc; + int errCode = hashCalc.Initialize(); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + + errCode = hashCalc.Update(value); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + + errCode = hashCalc.GetResult(hashValue); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + + return E_OK; +} + +int DBCommon::CreateStoreDirectory(const std::string &directory, const std::string &identifierName, + const std::string &subDir, bool isCreate) +{ + std::string newDir = directory; + if (newDir.back() != '/') { + newDir += "/"; + } + + newDir += identifierName; + if (!isCreate) { + if (!OS::CheckPathExistence(newDir)) { + LOGE("Required path does not exist and won't create."); + return -E_INVALID_ARGS; + } + return E_OK; + } + + if (directory.empty()) { + return -E_INVALID_ARGS; + } + + int errCode = DBCommon::CreateDirectory(newDir); + if (errCode != E_OK) { + return errCode; + } + + newDir += ("/" + subDir); + return DBCommon::CreateDirectory(newDir); +} + +int DBCommon::CopyFile(const std::string &srcFile, const std::string &dstFile) +{ + const int copyBlockSize = 4096; + std::vector tmpBlock(copyBlockSize, 0); + int errCode; + FILE *fileIn = fopen(srcFile.c_str(), "rb"); + if (fileIn == nullptr) { + LOGE("[Common:CpFile] open the source file error:%d", errno); + return -E_INVALID_FILE; + } + FILE *fileOut = fopen(dstFile.c_str(), "wb"); + if (fileOut == nullptr) { + LOGE("[Common:CpFile] open the target file error:%d", errno); + errCode = -E_INVALID_FILE; + goto END; + } + for (;;) { + size_t readSize = fread(static_cast(tmpBlock.data()), 1, copyBlockSize, fileIn); + if (readSize < copyBlockSize) { + // not end and have error. + if (feof(fileIn) != 0 && ferror(fileIn) != 0) { + LOGE("Copy the file error:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + break; + } + } + + if (readSize != 0) { + size_t writeSize = fwrite(static_cast(tmpBlock.data()), 1, readSize, fileOut); + if (ferror(fileOut) != 0 || writeSize != readSize) { + LOGE("Write the data while copy:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + break; + } + } + + if (feof(fileIn) != 0) { + errCode = E_OK; + break; + } + } + +END: + if (fileIn != nullptr) { + (void)fclose(fileIn); + fileIn = nullptr; + } + if (fileOut != nullptr) { + (void)fclose(fileOut); + fileOut = nullptr; + } + return errCode; +} + +int DBCommon::RemoveAllFilesOfDirectory(const std::string &dir, bool isNeedRemoveDir) +{ + std::list fileList; + bool isExisted = OS::CheckPathExistence(dir); + if (!isExisted) { + return E_OK; + } + int errCode = OS::GetFileAttrFromPath(dir, fileList, true); + if (errCode != E_OK) { + return errCode; + } + + RemoveFiles(fileList, OS::FileType::FILE); + RemoveDirectories(fileList, OS::FileType::PATH); + if (isNeedRemoveDir) { + // Pay attention to the order of deleting the directory + if (OS::CheckPathExistence(dir) && OS::RemoveDBDirectory(dir.c_str()) != 0) { + LOGI("Remove the directory error:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + } + } + + return errCode; +} + +std::string DBCommon::GenerateIdentifierId(const std::string &storeId, + const std::string &appId, const std::string &userId) +{ + return userId + "-" + appId + "-" + storeId; +} + +std::string DBCommon::GenerateDualTupleIdentifierId(const std::string &storeId, const std::string &appId) +{ + return appId + "-" + storeId; +} + +void DBCommon::SetDatabaseIds(KvDBProperties &properties, const std::string &appId, const std::string &userId, + const std::string &storeId) +{ + properties.SetIdentifier(userId, appId, storeId); + std::string oriStoreDir; + std::string identifier = GenerateIdentifierId(storeId, appId, userId); + if (properties.GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false)) { + oriStoreDir = storeId; + } else { + oriStoreDir = identifier; + } + std::string hashIdentifier = TransferHashString(identifier); + std::string hashDir = TransferHashString(oriStoreDir); + std::string hexHashDir = TransferStringToHex(hashDir); + properties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, hexHashDir); +} + +std::string DBCommon::StringMasking(const std::string &oriStr, size_t remain) +{ + if (oriStr.size() > remain) { + return oriStr.substr(0, remain); + } + return oriStr; +} + +std::string DBCommon::GetDistributedTableName(const std::string &device, const std::string &tableName) +{ + std::string deviceHashHex = DBCommon::TransferStringToHex(DBCommon::TransferHashString(device)); + return DBConstant::RELATIONAL_PREFIX + tableName + "_" + deviceHashHex; +} + +void DBCommon::GetDeviceFromName(const std::string &deviceTableName, std::string &deviceHash, std::string &tableName) +{ + std::size_t found = deviceTableName.rfind('_'); + if (found != std::string::npos && found + 1 < deviceTableName.length() && + found > DBConstant::RELATIONAL_PREFIX.length()) { + deviceHash = deviceTableName.substr(found + 1); + tableName = deviceTableName.substr(DBConstant::RELATIONAL_PREFIX.length(), + found - DBConstant::RELATIONAL_PREFIX.length()); + } +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/db_constant.cpp b/mock/distributeddb/common/src/db_constant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..35f3cf65e32659c7c84cceb6c48636d642c81941 --- /dev/null +++ b/mock/distributeddb/common/src/db_constant.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "db_constant.h" + +namespace DistributedDB { +const std::string DBConstant::MULTI_SUB_DIR = "multi_ver"; +const std::string DBConstant::SINGLE_SUB_DIR = "single_ver"; +const std::string DBConstant::LOCAL_SUB_DIR = "local"; + +const std::string DBConstant::MAINDB_DIR = "main"; +const std::string DBConstant::METADB_DIR = "meta"; +const std::string DBConstant::CACHEDB_DIR = "cache"; + +const std::string DBConstant::LOCAL_DATABASE_NAME = "local"; +const std::string DBConstant::MULTI_VER_DATA_STORE = "multi_ver_data"; +const std::string DBConstant::MULTI_VER_COMMIT_STORE = "commit_logs"; +const std::string DBConstant::MULTI_VER_VALUE_STORE = "value_storage"; +const std::string DBConstant::MULTI_VER_META_STORE = "meta_storage"; +const std::string DBConstant::SINGLE_VER_DATA_STORE = "gen_natural_store"; +const std::string DBConstant::SINGLE_VER_META_STORE = "meta"; +const std::string DBConstant::SINGLE_VER_CACHE_STORE = "cache"; + +const std::string DBConstant::SQLITE_URL_PRE = "file:"; +const std::string DBConstant::SQLITE_DB_EXTENSION = ".db"; +const std::string DBConstant::SQLITE_MEMDB_IDENTIFY = "?mode=memory&cache=shared"; + +const std::string DBConstant::SCHEMA_KEY = "schemaKey"; + +const std::string DBConstant::PATH_POSTFIX_UNPACKED = "_unpacked"; +const std::string DBConstant::PATH_POSTFIX_IMPORT_BACKUP = "_import_bak"; +const std::string DBConstant::PATH_POSTFIX_IMPORT_ORIGIN = "_import_ori"; +const std::string DBConstant::PATH_POSTFIX_IMPORT_DUP = "_import_dup"; +const std::string DBConstant::PATH_POSTFIX_EXPORT_BACKUP = "_export_bak"; +const std::string DBConstant::PATH_POSTFIX_DB_INCOMPLETE = "_db_incomplete.lock"; + +const std::string DBConstant::REKEY_FILENAME_POSTFIX_PRE = "_ctrl_pre"; +const std::string DBConstant::REKEY_FILENAME_POSTFIX_OK = "_ctrl_ok"; +const std::string DBConstant::UPGRADE_POSTFIX = "_upgrade.lock"; +const std::string DBConstant::SET_SECOPT_POSTFIX = "_secopt.lock"; +const std::string DBConstant::PATH_BACKUP_POSTFIX = "_bak"; + +const std::string DBConstant::ID_CONNECTOR = "-"; + +const std::string DBConstant::DELETE_KVSTORE_REMOVING = "_removing"; +const std::string DBConstant::DB_LOCK_POSTFIX = ".lock"; + +const std::string DBConstant::SUBSCRIBE_QUERY_PREFIX = "subscribe_query_"; + +const std::string DBConstant::TRIGGER_REFERENCES_NEW = "NEW."; +const std::string DBConstant::TRIGGER_REFERENCES_OLD = "OLD."; + +const std::string DBConstant::UPDATE_META_FUNC = "update_meta_within_trigger"; + +const std::string DBConstant::SYSTEM_TABLE_PREFIX = "naturalbase_rdb_"; +const std::string DBConstant::RELATIONAL_PREFIX = "naturalbase_rdb_aux_"; +const std::string DBConstant::TIMESTAMP_ALIAS = "naturalbase_rdb_aux_timestamp"; +} \ No newline at end of file diff --git a/mock/distributeddb/common/src/db_dfx_adapter.cpp b/mock/distributeddb/common/src/db_dfx_adapter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ffcc0ddd74780e210dbc655538c291b4cb6ea2d --- /dev/null +++ b/mock/distributeddb/common/src/db_dfx_adapter.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "db_dfx_adapter.h" + +#include +#include +#include +#include + +#include "log_print.h" +#include "db_dump_helper.h" +#include "db_errno.h" +#include "kvdb_manager.h" +#include "relational_store_instance.h" +#include "runtime_context.h" +#include "sqlite_utils.h" +#ifdef USE_DFX_ABILITY +#include "hitrace_meter.h" +#include "hisysevent.h" +#endif + +namespace DistributedDB { +namespace { +#ifdef USE_DFX_ABILITY +constexpr uint64_t HITRACE_LABEL = HITRACE_TAG_DISTRIBUTEDDATA; +#endif +constexpr const char *DUMP_PARAM = "dump-distributeddb"; +} + +const std::string DBDfxAdapter::EVENT_CODE = "ERROR_CODE"; +const std::string DBDfxAdapter::APP_ID = "APP_ID"; +const std::string DBDfxAdapter::USER_ID = "USER_ID"; +const std::string DBDfxAdapter::STORE_ID = "STORE_ID"; +const std::string DBDfxAdapter::SQLITE_EXECUTE = "SQLITE_EXECUTE"; +const std::string DBDfxAdapter::SYNC_ACTION = "SYNC_ACTION"; +const std::string DBDfxAdapter::EVENT_OPEN_DATABASE_FAILED = "OPEN_DATABASE_FAILED"; + +void DBDfxAdapter::Dump(int fd, const std::vector &args) +{ + if (!args.empty()) { + const std::u16string u16DumpParam = + std::wstring_convert, char16_t> {}.from_bytes(DUMP_PARAM); + auto find = std::any_of(args.begin(), args.end(), [&u16DumpParam](const std::u16string &arg) { + return arg == u16DumpParam; + }); + if (!find) { + return; + } + } + DBDumpHelper::Dump(fd, "DistributedDB Dump Message Info:\n\n"); + DBDumpHelper::Dump(fd, "DistributedDB Database Basic Message Info:\n"); + KvDBManager::GetInstance()->Dump(fd); + RelationalStoreInstance::GetInstance()->Dump(fd); + DBDumpHelper::Dump(fd, "DistributedDB Common Message Info:\n"); + RuntimeContext::GetInstance()->DumpCommonInfo(fd); + DBDumpHelper::Dump(fd, "\tlast error msg = %s\n", SQLiteUtils::GetLastErrorMsg().c_str()); +} + +#ifdef USE_DFX_ABILITY +void DBDfxAdapter::ReportFault(const ReportTask &reportTask) +{ + RuntimeContext::GetInstance()->ScheduleTask([=]() { + // call hievent here + OHOS::HiviewDFX::HiSysEvent::Write(OHOS::HiviewDFX::HiSysEvent::Domain::DISTRIBUTED_DATAMGR, + reportTask.eventName, + OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, + APP_ID, reportTask.appId, + STORE_ID, reportTask.storeId, + EVENT_CODE, std::to_string(reportTask.errCode)); + }); +} + +void DBDfxAdapter::StartTrace(const std::string &action) +{ + ::StartTrace(HITRACE_LABEL, action); +} + +void DBDfxAdapter::FinishTrace() +{ + ::FinishTrace(HITRACE_LABEL); +} + +void DBDfxAdapter::StartTraceSQL() +{ +#ifdef TRACE_SQLITE_EXECUTE + ::StartTrace(HITRACE_LABEL, SQLITE_EXECUTE); +#endif +} + +void DBDfxAdapter::FinishTraceSQL() +{ +#ifdef TRACE_SQLITE_EXECUTE + ::FinishTrace(HITRACE_LABEL); +#endif +} + +void DBDfxAdapter::StartAsyncTrace(const std::string &action, int32_t taskId) +{ + // call hitrace here + // need include bytrace.h + ::StartAsyncTrace(HITRACE_LABEL, action, taskId); +} + +void DBDfxAdapter::FinishAsyncTrace(const std::string &action, int32_t taskId) +{ + // call hitrace here + ::FinishAsyncTrace(HITRACE_LABEL, action, taskId); +} + +#else +void DBDfxAdapter::ReportFault(const ReportTask &reportTask) +{ + (void) reportTask; +} + +void DBDfxAdapter::StartTrace(const std::string &action) +{ + (void) action; +} + +void DBDfxAdapter::FinishTrace() +{ +} + +void DBDfxAdapter::StartAsyncTrace(const std::string &action, int32_t taskId) +{ + (void) action; + (void) taskId; +} + +void DBDfxAdapter::FinishAsyncTrace(const std::string &action, int32_t taskId) +{ + (void) action; + (void) taskId; +} + +void DBDfxAdapter::StartTraceSQL() +{ +} + +void DBDfxAdapter::FinishTraceSQL() +{ +} +#endif +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/common/src/db_dump_helper.cpp b/mock/distributeddb/common/src/db_dump_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..262175429efa1d57728b654aed49e7a287252690 --- /dev/null +++ b/mock/distributeddb/common/src/db_dump_helper.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "db_dump_helper.h" + +#include +#include + +namespace DistributedDB { +void DBDumpHelper::Dump(int fd, const char *format, ...) +{ + va_list argList; + va_start(argList, format); +#if defined _WIN32 + (void) fd; +#else + vdprintf(fd, format, argList); +#endif + va_end(argList); +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/common/src/evloop/include/event_fd.h b/mock/distributeddb/common/src/evloop/include/event_fd.h new file mode 100644 index 0000000000000000000000000000000000000000..7700f867884357a70113b8d1e9b26cce84a56755 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/include/event_fd.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_FD_H +#define EVENT_FD_H + +#include "platform_specific.h" + +#if defined EVLOOP_TIMER_ONLY +using Handle = int; +static const int INVALID_HANDLE = -1; +#define IS_VALID_HANDLE(h) false +#define CLOSE_HANDLE(h) +#else +#include +using Handle = int; +static const int INVALID_HANDLE = -1; +#define IS_VALID_HANDLE(h) ((h) > 0) +#define CLOSE_HANDLE(h) do { close(h); } while (0) +#endif + +namespace DistributedDB { +class EventFd final { +public: + EventFd() : fd_(INVALID_HANDLE) {} + explicit EventFd(Handle handle) : fd_(handle) {} + + ~EventFd() + { + // we can't close it. + fd_ = INVALID_HANDLE; + } + + bool IsValid() const + { + return IS_VALID_HANDLE(fd_); + } + + operator Handle() const + { + return fd_; + } + + bool operator==(const EventFd &other) const + { + return other.fd_ == fd_; + } + + void Close() + { + if (IsValid()) { + CLOSE_HANDLE(fd_); + fd_ = INVALID_HANDLE; + } + } + +private: + Handle fd_; +}; +} + +#endif // EVENT_FD_H diff --git a/mock/distributeddb/common/src/evloop/include/ievent.h b/mock/distributeddb/common/src/evloop/include/ievent.h new file mode 100644 index 0000000000000000000000000000000000000000..ce66e008d2e3f738a8822252e62baf056fada165 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/include/ievent.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IEVENT_H +#define IEVENT_H + +#include "ref_object.h" +#include "macro_utils.h" +#include "event_fd.h" + +namespace DistributedDB { +using EventTime = int64_t; +using EventsMask = unsigned int; +using EventAction = std::function; +using EventFinalizer = std::function; + +class IEvent : public virtual RefObject { +public: + enum EventType { + ET_READ = 0x01, + ET_WRITE = 0x02, + ET_ERROR = 0x04, + ET_TIMEOUT = 0x08, + }; + + IEvent() = default; + DISABLE_COPY_ASSIGN_MOVE(IEvent); + + virtual int SetAction(const EventAction &action, const EventFinalizer &finalizer = nullptr) = 0; + virtual int AddEvents(EventsMask events) = 0; + virtual int RemoveEvents(EventsMask events) = 0; + virtual int SetTimeout(EventTime timeout) = 0; + virtual int Detach(bool wait) = 0; + virtual void IgnoreFinalizer() = 0; + + // The following 2 static methods is used to create real event objects, + // instead of an event object factory. + static IEvent *CreateEvent(EventTime timeout, int &errCode); + static IEvent *CreateEvent(EventFd fd, EventsMask events, EventTime timeout, int &errCode); + +protected: + virtual ~IEvent() {}; +}; +} + +#endif // IEVENT_H diff --git a/mock/distributeddb/common/src/evloop/include/ievent_loop.h b/mock/distributeddb/common/src/evloop/include/ievent_loop.h new file mode 100644 index 0000000000000000000000000000000000000000..b7451b8c4129afd4676709c8cbd89416a881d2f4 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/include/ievent_loop.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IEVENT_LOOP_H +#define IEVENT_LOOP_H + +#include "ref_object.h" +#include "macro_utils.h" + +namespace DistributedDB { +class IEvent; + +// Abstract of event loop. +class IEventLoop : public virtual RefObject { +public: + IEventLoop() = default; + + DISABLE_COPY_ASSIGN_MOVE(IEventLoop); + + // Add an event object to the loop. + virtual int Add(IEvent *event) = 0; + + // Remove an event object from the loop. + virtual int Remove(IEvent *event) = 0; + + // Run the loop. + virtual int Run() = 0; + + // Create a loop object. + static IEventLoop *CreateEventLoop(int &errCode); + +protected: + virtual ~IEventLoop() {}; +}; +} + +#endif // IEVENT_LOOP_H diff --git a/mock/distributeddb/common/src/evloop/src/event_impl.cpp b/mock/distributeddb/common/src/evloop/src/event_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4a70a3aeb61d910483ebcec502664dad5e118647 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/event_impl.cpp @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "event_impl.h" +#include "db_errno.h" +#include "log_print.h" +#include "event_loop_impl.h" + +namespace DistributedDB { +EventImpl::EventImpl(EventTime timeout) + : events_(ET_TIMEOUT), + revents_(0), + timeout_(timeout), + start_(0), + loop_(nullptr), + ignoreFinalizer_(false) +{ + if (timeout_ < 0) { + timeout_ = MAX_TIME_VALUE; + } + + OnKill([this]() { + UnlockObj(); + (void)Detach(false); + LockObj(); + }); + + OnLastRef([this]() { + if (finalizer_ && !ignoreFinalizer_) { + finalizer_(); + } + }); +} + +EventImpl::EventImpl(EventFd fd, EventsMask events, EventTime timeout) + : fd_(fd), + events_(events), + revents_(0), + timeout_(timeout), + start_(0), + loop_(nullptr), + ignoreFinalizer_(false) +{ + if (!(events & ET_TIMEOUT) || (timeout_ < 0)) { + timeout_ = MAX_TIME_VALUE; + } + if (!fd_.IsValid()) { + events_ &= ~(ET_READ | ET_WRITE | ET_ERROR); + } + + OnKill([this]() { + UnlockObj(); + (void)Detach(false); + LockObj(); + }); + + OnLastRef([this]() { + if (finalizer_ && !ignoreFinalizer_) { + finalizer_(); + } + }); +} + +EventImpl::~EventImpl() +{ + if (loop_ != nullptr) { + loop_->DecObjRef(loop_); + loop_ = nullptr; + } + if (fd_.IsValid()) { + fd_.Close(); + } +} + +int EventImpl::SetAction(const EventAction &action, const EventFinalizer &finalizer) +{ + if (!action || action_) { + return -E_INVALID_ARGS; + } + if (IsKilled()) { + return -E_OBJ_IS_KILLED; + } + + action_ = action; + finalizer_ = finalizer; + return E_OK; +} + +int EventImpl::AddEvents(EventsMask events) +{ + if (!IsValidArg(events)) { + return -E_INVALID_ARGS; + } + + EventsMask genericEvents = ET_READ | ET_WRITE | ET_ERROR; + if ((genericEvents & events) && !IsValidFd()) { + LOGE("ev add events failed, fd is invalid."); + return -E_INVALID_ARGS; + } + + EventLoopImpl *loop = nullptr; + { + RefObject::AutoLock lockGuard(this); + if (loop_ == nullptr) { + events_ |= events; + return E_OK; + } + loop = loop_; + loop->IncObjRef(loop); + } + + int errCode = loop->Modify(this, true, events); + loop->DecObjRef(loop); + if (errCode != E_OK) { + LOGE("ev add events failed, err: '%d'.", errCode); + } + return errCode; +} + +int EventImpl::RemoveEvents(EventsMask events) +{ + if (!IsValidArg(events)) { + return -E_INVALID_ARGS; + } + + EventsMask genericEvents = ET_READ | ET_WRITE | ET_ERROR; + if ((genericEvents & events) && !IsValidFd()) { + LOGE("ev remove events failed, fd is invalid."); + return -E_INVALID_ARGS; + } + + EventLoopImpl *loop = nullptr; + { + RefObject::AutoLock lockGuard(this); + if (loop_ == nullptr) { + events_ &= ~events; + return E_OK; + } + loop = loop_; + loop->IncObjRef(loop); + } + + int errCode = loop->Modify(this, false, events); + loop->DecObjRef(loop); + if (errCode != E_OK) { + LOGE("ev remove events failed, err: '%d'.", errCode); + } + return errCode; +} + +int EventImpl::SetTimeout(EventTime timeout) +{ + if (!IsValidArg(timeout)) { + return -E_INVALID_ARGS; + } + + EventLoopImpl *loop = nullptr; + { + RefObject::AutoLock lockGuard(this); + if (loop_ == nullptr) { + timeout_ = timeout; + return E_OK; + } + loop = loop_; + loop->IncObjRef(loop); + } + + int errCode = loop->Modify(this, timeout); + loop->DecObjRef(loop); + if (errCode != E_OK) { + LOGE("ev set timeout failed, err: '%d'.", errCode); + } + return errCode; +} + +int EventImpl::Detach(bool wait) +{ + EventLoopImpl *loop = nullptr; + { + RefObject::AutoLock lockGuard(this); + if (loop_ == nullptr) { + return E_OK; + } + loop = loop_; + loop->IncObjRef(loop); + } + + int errCode = loop->Remove(this); + if (errCode == -E_OBJ_IS_KILLED) { + errCode = E_OK; + } + + if ((errCode == E_OK) && wait) { + bool started = true; + if (!loop->IsInLoopThread(started)) { + Wait(); + } + loop->DecObjRef(loop); + return E_OK; + } + + loop->DecObjRef(loop); + return errCode; +} + +void EventImpl::IgnoreFinalizer() +{ + ignoreFinalizer_ = true; +} + +int EventImpl::CheckStatus() const +{ + RefObject::AutoLock lockGuard(this); + if (IsKilled()) { + return -E_OBJ_IS_KILLED; + } + if (!action_) { + return -E_INVALID_ARGS; + } + return E_OK; +} + +bool EventImpl::IsTimer() const +{ + return !IsValidFd(); +} + +bool EventImpl::IsValidFd() const +{ + return fd_.IsValid(); +} + +EventFd EventImpl::GetEventFd() const +{ + return fd_; +} + +EventsMask EventImpl::GetEvents() const +{ + return events_; +} + +bool EventImpl::SetLoop(EventLoopImpl *loop) +{ + RefObject::AutoLock lockGuard(this); + if (loop == nullptr) { + if (loop_ != nullptr) { + loop_->DecObjRef(loop_); + loop_ = nullptr; + } + detached_.notify_one(); + return true; + } + if (loop_ == nullptr) { + loop->IncObjRef(loop); + loop_ = loop; + return true; + } + return false; +} + +void EventImpl::Wait() +{ + RefObject::AutoLock lockGuard(this); + WaitLockedUntil(detached_, [this]()->bool { return loop_ == nullptr; }); +} + +bool EventImpl::Attached(const EventLoopImpl *loop, bool &isLoopConfused) const +{ + RefObject::AutoLock lockGuard(this); + if (loop_ != nullptr && loop != nullptr && loop_ != loop) { + // the event object is attached to another loop. + isLoopConfused = true; + } else { + isLoopConfused = false; + } + // returns true when both are nullptr. + return loop_ == loop; +} + +void EventImpl::SetEvents(bool isAdd, EventsMask events) +{ + if (isAdd) { + events_ |= events; + } else { + events_ &= ~events; + } +} + +void EventImpl::SetRevents(EventsMask events) +{ + EventsMask genericEvents = ET_READ | ET_WRITE | ET_ERROR; + EventsMask revents = events & genericEvents; + if (revents) { + revents_ = revents; + } else { + revents_ = events & ET_TIMEOUT; + } +} + +void EventImpl::SetTimeoutPeriod(EventTime timeout) +{ + if (timeout < 0) { + timeout_ = MAX_TIME_VALUE; + } else { + timeout_ = timeout; + } +} + +void EventImpl::SetStartTime(EventTime startTime) +{ + start_ = startTime; +} + +bool EventImpl::GetTimeoutPoint(EventTime &timePoint) const +{ + if (events_ & ET_TIMEOUT) { + timePoint = start_ + timeout_; + return true; + } + timePoint = MAX_TIME_VALUE; + return false; +} + +void EventImpl::UpdateElapsedTime(EventTime now) +{ + if (events_ & ET_TIMEOUT) { + EventTime timePoint = start_ + timeout_; + if ((now >= timePoint) || (now < start_)) { + start_ = now; + if (!revents_) { + revents_ = ET_TIMEOUT; + } + } + } +} + +int EventImpl::Dispatch() +{ + if (!action_) { + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + if (!IsKilled()) { + if (revents_) { + errCode = action_(revents_); + if (errCode != E_OK) { + LOGI("ev action() returns '%d'.", errCode); + } + } + } + return errCode; +} + +bool EventImpl::IsValidArg(EventsMask events) const +{ + EventsMask allEvents = ET_READ | ET_WRITE | ET_ERROR | ET_TIMEOUT; + return ((events != 0) && ((events & (~allEvents)) == 0)); +} + +bool EventImpl::IsValidArg(EventTime timeout) const +{ + return (timeout >= 0) && (timeout <= MAX_TIME_VALUE); +} + +DEFINE_OBJECT_TAG_FACILITIES(EventImpl) +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/evloop/src/event_impl.h b/mock/distributeddb/common/src/evloop/src/event_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..48964b5585161d970ec98bacbbd29ce326d2fe11 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/event_impl.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_IMPL_H +#define EVENT_IMPL_H + +#include +#include +#include "../include/ievent.h" + +namespace DistributedDB { +class EventLoopImpl; + +class EventImpl : public IEvent { +public: + explicit EventImpl(EventTime timeout); + EventImpl(EventFd fd, EventsMask events, EventTime timeout); + ~EventImpl() override; + + int SetAction(const EventAction &action, const EventFinalizer &finalizer) override; + int AddEvents(EventsMask events) override; + int RemoveEvents(EventsMask events) override; + int SetTimeout(EventTime timeout) override; + int Detach(bool wait) override; + void IgnoreFinalizer() override; + + int CheckStatus() const; + bool IsTimer() const; + bool IsValidFd() const; + EventFd GetEventFd() const; + EventsMask GetEvents() const; + bool SetLoop(EventLoopImpl *loop); + void Wait(); + bool Attached(const EventLoopImpl *loop, bool &isLoopConfused) const; + void SetEvents(bool isAdd, EventsMask events); + void SetRevents(EventsMask events); + void SetTimeoutPeriod(EventTime timeout); + void SetStartTime(EventTime startTime); + bool GetTimeoutPoint(EventTime &timePoint) const; + void UpdateElapsedTime(EventTime now); + int Dispatch(); + bool IsValidArg(EventsMask events) const; + bool IsValidArg(EventTime timeout) const; + DISABLE_COPY_ASSIGN_MOVE(EventImpl); + + static constexpr int MAX_TIME_VALUE = INT_MAX / 2; // half of the max + +private: + DECLARE_OBJECT_TAG(EventImpl); + + EventFd fd_; + EventsMask events_; + EventsMask revents_; + EventTime timeout_; // should not < 0 + EventTime start_; + EventLoopImpl *loop_; + EventAction action_; + EventFinalizer finalizer_; + bool ignoreFinalizer_; + std::condition_variable detached_; +}; +} + +#endif // EVENT_IMPL_H diff --git a/mock/distributeddb/common/src/evloop/src/event_loop_epoll.cpp b/mock/distributeddb/common/src/evloop/src/event_loop_epoll.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6519f3d5e4370ec2c0119b03074e10e2497f4e79 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/event_loop_epoll.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "event_loop_epoll.h" + +#ifdef EVENT_LOOP_USE_EPOLL +#include +#include "event_impl.h" +#include "log_print.h" +#include "db_errno.h" + +namespace DistributedDB { +EventLoopEpoll::EventLoopEpoll() + : pollFdCount_(0) +{ +} + +EventLoopEpoll::~EventLoopEpoll() +{ + if (wakeUpFd_.IsValid()) { + wakeUpFd_.Close(); + } + if (epollFd_.IsValid()) { + epollFd_.Close(); + } +} + +int EventLoopEpoll::Initialize() +{ + if (epollFd_.IsValid()) { + return -E_INVALID_ARGS; + } + + int errCode; + wakeUpFd_ = EventFd(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)); + if (!wakeUpFd_.IsValid()) { + errCode = -errno; + LOGE("Create event fd failed, err:'%d'", errCode); + return errCode; + } + + epollFd_ = EventFd(epoll_create(EPOLL_INIT_REVENTS)); + if (!epollFd_.IsValid()) { + errCode = -errno; + wakeUpFd_.Close(); + LOGE("Create epoll fd failed, err:'%d'", errCode); + return errCode; + } + + struct epoll_event event; + event.events = EPOLLIN; + event.data.ptr = this; + errCode = epoll_ctl(epollFd_, EPOLL_CTL_ADD, wakeUpFd_, &event); + if (errCode < 0) { + errCode = -errno; + epollFd_.Close(); + wakeUpFd_.Close(); + LOGE("Add wake up fd to epoll failed, err:'%d'", errCode); + return errCode; + } + + ++pollFdCount_; + return E_OK; +} + +int EventLoopEpoll::Prepare(const std::set &polling) +{ + if (pollFdCount_ > 0) { + revents_.resize(pollFdCount_); + return E_OK; + } + LOGE("Prepared epoll loop failed, fd count:'%d'", pollFdCount_); + return -E_INTERNAL_ERROR; +} + +int EventLoopEpoll::Poll(EventTime sleepTime) +{ + if (sleepTime > INT_MAX) { + LOGE("[EventLoopEpoll][Poll] sleepTime is too large!"); + return -E_INVALID_ARGS; + } + int nReady = epoll_wait(epollFd_, &revents_[0], revents_.size(), sleepTime); + if (nReady < 0) { + int errCode = -errno; + if (errCode != -EINTR) { + LOGE("Call epoll wait failed, err:'%d'", errCode); + return errCode; + } + nReady = 0; + } + + for (int index = 0; index < nReady; ++index) { + struct epoll_event *revent = &revents_[index]; + if (revent->data.ptr == this) { + EpollWokenUp(); + continue; + } + auto event = static_cast(revent->data.ptr); + EventsMask revents = CalEventsMask(revent->events); + event->SetRevents(revents); + } + return E_OK; +} + +int EventLoopEpoll::WakeUp() +{ + int64_t incValue = 1; + + while (true) { + int nWrite = write(wakeUpFd_, &incValue, sizeof(incValue)); + if (nWrite == sizeof(incValue)) { + break; + } + + int errCode = -errno; + if (errCode == -EINTR) { + continue; + } + if (errCode == -EAGAIN) { + // We have already signalled the loop. + break; + } + LOGE("Write loop wake up data failed, err:'%d'", errCode); + return errCode; + } + return E_OK; +} + +int EventLoopEpoll::Exit(const std::set &polling) +{ + if (revents_.capacity() > 0) { + std::vector revents; + revents.swap(revents_); + } + wakeUpFd_.Close(); + epollFd_.Close(); + pollFdCount_ = 0; + return E_OK; +} + +void EventLoopEpoll::EpollWokenUp() +{ + while (true) { + int64_t intValue; + int nRead = read(wakeUpFd_, &intValue, sizeof(intValue)); + if (nRead < 0) { + int errCode = -errno; + if (errCode == -EINTR) { + continue; + } + if (errCode != -EAGAIN) { + LOGE("Clear loop wake up data failed, err:'%d'", errCode); + } + } + break; + } +} + +uint32_t EventLoopEpoll::CalEpollEvents(EventsMask events) const +{ + uint32_t epollEvents = 0; + if (events & IEvent::ET_READ) { + epollEvents |= EPOLLIN; + } + if (events & IEvent::ET_WRITE) { + epollEvents |= EPOLLOUT; + } + if (events & IEvent::ET_ERROR) { + epollEvents |= EPOLLERR; + } + return epollEvents; +} + +EventsMask EventLoopEpoll::CalEventsMask(uint32_t epollEvents) +{ + EventsMask events = 0; + if (epollEvents & EPOLLIN) { + events |= IEvent::ET_READ; + } + if (epollEvents & EPOLLOUT) { + events |= IEvent::ET_WRITE; + } + if (epollEvents & EPOLLERR) { + events |= IEvent::ET_ERROR; + } + return events; +} + +int EventLoopEpoll::EpollCtl(int operation, EventImpl *event, EventsMask events) +{ + if (operation != EPOLL_CTL_ADD && + operation != EPOLL_CTL_MOD && + operation != EPOLL_CTL_DEL) { + return -E_INVALID_ARGS; + } + if (event == nullptr) { + return -E_INVALID_ARGS; + } + + EventFd fd = event->GetEventFd(); + if (fd.IsValid()) { + return -E_INVALID_ARGS; + } + + uint32_t epollEvents = CalEpollEvents(events); + struct epoll_event epollEvent; + epollEvent.events = epollEvents; + epollEvent.data.ptr = event; + + int errCode = epoll_ctl(epollFd_, operation, fd, &epollEvent); + if (errCode < 0) { + errCode = -errno; + return errCode; + } + return E_OK; +} + +int EventLoopEpoll::AddEvent(EventImpl *event) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + + EventsMask events = event->GetEvents(); + int errCode = EpollCtl(EPOLL_CTL_ADD, event, events); + if (errCode != E_OK) { + LOGE("Add fd to epoll set failed, err:'%d'", errCode); + return errCode; + } + + ++pollFdCount_; + return E_OK; +} + +int EventLoopEpoll::RemoveEvent(EventImpl *event) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + + EventsMask events = event->GetEvents(); + int errCode = EpollCtl(EPOLL_CTL_DEL, event, events); + if (errCode != E_OK) { + LOGE("Remove fd from epoll set failed, err:'%d'", errCode); + return errCode; + } + + --pollFdCount_; + return E_OK; +} + +int EventLoopEpoll::ModifyEvent(EventImpl *event, bool isAdd, EventsMask events) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + + EventsMask newEvents = event->GetEvents(); + if (isAdd) { + newEvents |= events; + } else { + newEvents &= ~events; + } + + int errCode = EpollCtl(EPOLL_CTL_MOD, event, newEvents); + if (errCode != E_OK) { + LOGE("Modify fd in epoll set failed, err:'%d'", errCode); + return errCode; + } + return E_OK; +} + +DEFINE_OBJECT_TAG_FACILITIES(EventLoopEpoll) +} +#endif // EVENT_LOOP_USE_EPOLL diff --git a/mock/distributeddb/common/src/evloop/src/event_loop_epoll.h b/mock/distributeddb/common/src/evloop/src/event_loop_epoll.h new file mode 100644 index 0000000000000000000000000000000000000000..d802afad49a3dea890fa483d10803a05bf247e68 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/event_loop_epoll.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_LOOP_EPOLL_H +#define EVENT_LOOP_EPOLL_H + +#include "event_loop_impl.h" + +#ifdef EVENT_LOOP_USE_EPOLL +#include +#include + +namespace DistributedDB { +class EventLoopEpoll : public EventLoopImpl { +public: + EventLoopEpoll(); + ~EventLoopEpoll() override; + + int Initialize() override; + +private: + int Prepare(const std::set &polling) override; + int Poll(EventTime sleepTime) override; + int WakeUp() override; + int Exit(const std::set &polling) override; + int AddEvent(EventImpl *event) override; + int RemoveEvent(EventImpl *event) override; + int ModifyEvent(EventImpl *event, bool isAdd, EventsMask events) override; + + void EpollWokenUp(); + uint32_t CalEpollEvents(EventsMask events) const; + EventsMask CalEventsMask(uint32_t epollEvents); + int EpollCtl(int operation, EventImpl *event, EventsMask events); + + DECLARE_OBJECT_TAG(EventLoopEpoll); + + static constexpr int EPOLL_INIT_REVENTS = 32; + EventFd wakeUpFd_; + EventFd epollFd_; + int pollFdCount_; + std::vector revents_; +}; +} +#endif // EVENT_LOOP_USE_EPOLL +#endif // EVENT_LOOP_EPOLL_H diff --git a/mock/distributeddb/common/src/evloop/src/event_loop_impl.cpp b/mock/distributeddb/common/src/evloop/src/event_loop_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc920a86464796defbdc12b7a31d327ccca6f4a7 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/event_loop_impl.cpp @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "event_loop_impl.h" + +#include + +#include "db_errno.h" +#include "log_print.h" +#include "event_impl.h" + +namespace DistributedDB { +class EventRequest { +public: + enum { + ADD_EVENT = 1, + REMOVE_EVENT, + SET_TIMEOUT, + MOD_EVENTS_ADD, + MOD_EVENTS_REMOVE, + }; + + EventRequest(int type, EventImpl *event, EventsMask events) + : type_(type), + event_(event), + events_(events), + timeout_(0) + { + if (event != nullptr) { + event->IncObjRef(event); + } + } + + EventRequest(int type, EventImpl *event, EventTime timeout) + : type_(type), + event_(event), + events_(0), + timeout_(timeout) + { + if (event != nullptr) { + event->IncObjRef(event); + } + } + + ~EventRequest() + { + if (event_ != nullptr) { + event_->DecObjRef(event_); + event_ = nullptr; + } + } + + static bool IsValidType(int type) + { + if (type < ADD_EVENT || type > MOD_EVENTS_REMOVE) { + return false; + } + return true; + } + + int GetType() const + { + return type_; + } + + void GetEvent(EventImpl *&event) const + { + event = event_; + } + + EventsMask GetEvents() const + { + return events_; + } + + EventTime GetTimeout() const + { + return timeout_; + } + +private: + int type_; + EventImpl *event_; + EventsMask events_; + EventTime timeout_; +}; + +EventLoopImpl::EventLoopImpl() + : pollingSetChanged_(false) +{ + OnKill([this](){ OnKillLoop(); }); +} + +EventLoopImpl::~EventLoopImpl() +{} + +int EventLoopImpl::Add(IEvent *event) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + + auto eventImpl = static_cast(event); + if (!eventImpl->SetLoop(this)) { + LOGE("Add ev to loop failed, already attached."); + return -E_INVALID_ARGS; + } + + EventTime timeout = 0; + int errCode = QueueRequest(EventRequest::ADD_EVENT, eventImpl, timeout); + if (errCode != E_OK) { + eventImpl->SetLoop(nullptr); + LOGE("Add ev to loop failed. err: '%d'.", errCode); + } + return errCode; +} + +int EventLoopImpl::Remove(IEvent *event) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + + auto eventImpl = static_cast(event); + bool isLoopConfused = false; + if (!eventImpl->Attached(this, isLoopConfused)) { + if (isLoopConfused) { + LOGE("Remove ev' from loop failed, loop confused."); + return -E_UNEXPECTED_DATA; + } + return E_OK; + } + + EventTime timeout = 0; + int errCode = QueueRequest(EventRequest::REMOVE_EVENT, eventImpl, timeout); + if (errCode != E_OK) { + LOGE("Remove ev from loop failed. err: '%d'.", errCode); + } + return errCode; +} + +int EventLoopImpl::Run() +{ + { + RefObject::AutoLock lockGuard(this); + if (IsKilled()) { + LOGE("Try to run a killed loop."); + return -E_OBJ_IS_KILLED; + } + if (loopThread_ != std::thread::id()) { + LOGE("Try to run a threaded loop."); + return -E_BUSY; + } + loopThread_ = std::this_thread::get_id(); + } + + int errCode; + IncObjRef(this); + + while (true) { + errCode = ProcessRequest(); + if (errCode != E_OK) { + break; + } + + errCode = Prepare(polling_); + if (errCode != E_OK) { + break; + } + + EventTime sleepTime = CalSleepTime(); + errCode = Poll(sleepTime); + if (errCode != E_OK) { + break; + } + + errCode = ProcessRequest(); + if (errCode != E_OK) { + break; + } + + errCode = DispatchAll(); + if (errCode != E_OK) { + break; + } + } + + CleanLoop(); + DecObjRef(this); + if (errCode == -E_OBJ_IS_KILLED) { + LOGD("Loop exited."); + } else { + LOGE("Loop exited, err:'%d'.", errCode); + } + return errCode; +} + +int EventLoopImpl::Modify(EventImpl *event, bool isAdd, EventsMask events) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + + int type = isAdd ? EventRequest::MOD_EVENTS_ADD : + EventRequest::MOD_EVENTS_REMOVE; + int errCode = QueueRequest(type, event, events); + if (errCode != E_OK) { + LOGE("Modify loop ev events failed. err: '%d'.", errCode); + } + return errCode; +} + +int EventLoopImpl::Modify(EventImpl *event, EventTime time) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + + int errCode = QueueRequest(EventRequest::SET_TIMEOUT, event, time); + if (errCode != E_OK) { + LOGE("Mod loop ev time failed. err: '%d'.", errCode); + } + return errCode; +} + +EventTime EventLoopImpl::GetTime() const +{ + uint64_t microsecond = 0; + OS::GetMonotonicRelativeTimeInMicrosecond(microsecond); // It is not very possible to fail, if so use 0 as default + return static_cast(microsecond / 1000); // 1000 is the multiple between microsecond and millisecond +} + +int EventLoopImpl::SendRequestToLoop(EventRequest *eventRequest) +{ + if (eventRequest == nullptr) { + return -E_INVALID_ARGS; + } + + RefObject::AutoLock lockGuard(this); + if (IsKilled()) { + return -E_OBJ_IS_KILLED; + } + requests_.push_back(eventRequest); + WakeUp(); + return E_OK; +} + +template +int EventLoopImpl::QueueRequest(int type, EventImpl *event, T argument) +{ + if (!EventRequest::IsValidType(type)) { + return -E_INVALID_ARGS; + } + if (event == nullptr || + !event->IsValidArg(argument)) { + return -E_INVALID_ARGS; + } + + if (IsKilled()) { // pre-check + return -E_OBJ_IS_KILLED; + } + + int errCode; + if (event != nullptr) { + errCode = event->CheckStatus(); + if (errCode != E_OK) { + if (errCode != -E_OBJ_IS_KILLED || + type != EventRequest::REMOVE_EVENT) { + return errCode; + } + } + } + + auto eventRequest = new (std::nothrow) EventRequest(type, event, argument); + if (eventRequest == nullptr) { + return -E_OUT_OF_MEMORY; + } + + errCode = SendRequestToLoop(eventRequest); + if (errCode != E_OK) { + delete eventRequest; + eventRequest = nullptr; + } + return errCode; +} + +bool EventLoopImpl::IsInLoopThread(bool &started) const +{ + if (loopThread_ == std::thread::id()) { + started = false; + } else { + started = true; + } + return std::this_thread::get_id() == loopThread_; +} + +bool EventLoopImpl::EventObjectExists(EventImpl *event) const +{ + return polling_.find(event) != polling_.end(); +} + +bool EventLoopImpl::EventFdExists(const EventImpl *event) const +{ + if (!event->IsValidFd()) { + return false; + } + for (auto ev : polling_) { + if (ev->GetEventFd() == event->GetEventFd()) { + return true; + } + } + return false; +} + +int EventLoopImpl::AddEventObject(EventImpl *event, EventTime now) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + if (EventObjectExists(event)) { + LOGE("Add event object failed. ev already exists."); + return -EEXIST; + } + if (EventFdExists(event)) { + LOGE("Add event object failed. ev fd already exists."); + return -EEXIST; + } + + int errCode = E_OK; + if (!event->IsTimer()) { + errCode = AddEvent(event); + } + + if (errCode == E_OK) { + polling_.insert(event); + event->SetStartTime(now); + event->SetRevents(0); + event->IncObjRef(event); + pollingSetChanged_ = true; + } else { + LOGE("Add event failed. err: '%d'.", errCode); + } + return errCode; +} + +int EventLoopImpl::RemoveEventObject(EventImpl *event) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + if (!EventObjectExists(event)) { + return -E_NO_SUCH_ENTRY; + } + + int errCode = E_OK; + if (!event->IsTimer()) { + errCode = RemoveEvent(event); + } + + if (errCode == E_OK) { + polling_.erase(event); + event->SetLoop(nullptr); + event->DecObjRef(event); + pollingSetChanged_ = true; + } else { + LOGE("Remove event failed. err: '%d'.", errCode); + } + return errCode; +} + +int EventLoopImpl::ModifyEventObject(EventImpl *event, bool isAdd, EventsMask events) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + if (!EventObjectExists(event)) { + return -EEXIST; + } + + int errCode = E_OK; + if (!event->IsTimer()) { + EventsMask genericEvents = events & (~IEvent::ET_TIMEOUT); + if (genericEvents) { + errCode = ModifyEvent(event, isAdd, genericEvents); + } + } + + if (errCode == E_OK) { + event->SetEvents(isAdd, events); + } else { + LOGE("Modify event' failed. err: '%d'.", errCode); + } + return errCode; +} + +int EventLoopImpl::ModifyEventObject(EventImpl *event, EventTime timeout) +{ + if (event == nullptr) { + return -E_INVALID_ARGS; + } + if (!EventObjectExists(event)) { + return -E_NO_SUCH_ENTRY; + } + event->SetTimeoutPeriod(timeout); + return E_OK; +} + +void EventLoopImpl::ProcessRequest(std::list &requests) +{ + EventTime now = GetTime(); + while (true) { + if (requests.empty()) { + break; + } + + EventRequest *request = requests.front(); + requests.pop_front(); + if (request == nullptr) { + continue; + } + + if (!IsKilled()) { + EventImpl *event = nullptr; + request->GetEvent(event); + EventsMask events = request->GetEvents(); + EventTime timeout = request->GetTimeout(); + + switch (request->GetType()) { + case EventRequest::ADD_EVENT: + (void)(AddEventObject(event, now)); + break; + + case EventRequest::REMOVE_EVENT: + (void)(RemoveEventObject(event)); + break; + + case EventRequest::MOD_EVENTS_ADD: + (void)(ModifyEventObject(event, true, events)); + break; + + case EventRequest::MOD_EVENTS_REMOVE: + (void)(ModifyEventObject(event, false, events)); + break; + + case EventRequest::SET_TIMEOUT: + (void)(ModifyEventObject(event, timeout)); + break; + + default: + break; + } + } + + delete request; + request = nullptr; + } +} + +int EventLoopImpl::ProcessRequest() +{ + int errCode = E_OK; + std::list requests; + { + RefObject::AutoLock lockGuard(this); + if (IsKilled()) { + errCode = -E_OBJ_IS_KILLED; + } + if (requests_.empty()) { + return errCode; + } + std::swap(requests, requests_); + } + + ProcessRequest(requests); + return errCode; +} + +EventTime EventLoopImpl::CalSleepTime() const +{ + EventTime now = GetTime(); + EventTime minInterval = EventImpl::MAX_TIME_VALUE; + + for (auto event : polling_) { + if (event == nullptr) { + continue; + } + + EventTime t; + bool valid = event->GetTimeoutPoint(t); + if (!valid) { + continue; + } + + if (t <= now) { + return 0; + } + + EventTime interval = t - now; + if (interval < minInterval) { + minInterval = interval; + } + } + + return minInterval; +} + +int EventLoopImpl::DispatchAll() +{ + do { + EventTime now = GetTime(); + pollingSetChanged_ = false; + + for (auto event : polling_) { + if (IsKilled()) { + return -E_OBJ_IS_KILLED; + } + if (event == nullptr) { + continue; + } + + event->IncObjRef(event); + event->UpdateElapsedTime(now); + int errCode = event->Dispatch(); + if (errCode != E_OK) { + RemoveEventObject(event); + } else { + event->SetRevents(0); + } + event->DecObjRef(event); + + if (pollingSetChanged_) { + break; + } + } + } while (pollingSetChanged_); + return E_OK; +} + +void EventLoopImpl::CleanLoop() +{ + if (!IsKilled()) { + return; + } + + ProcessRequest(); + std::set polling = std::move(polling_); + int errCode = Exit(polling); + if (errCode != E_OK) { + LOGE("Exit loop failed when cleanup, err:'%d'.", errCode); + } + + for (auto event : polling) { + if (event != nullptr) { + event->KillAndDecObjRef(event); + } + } +} + +void EventLoopImpl::OnKillLoop() +{ + bool started = true; + if (IsInLoopThread(started)) { + // Loop object is set to state: killed, + // everything will be done in loop.Run() + return; + } + + if (started) { + // Ditto + WakeUp(); + } else { + // Drop the lock. + UnlockObj(); + CleanLoop(); + LockObj(); + } +} +} diff --git a/mock/distributeddb/common/src/evloop/src/event_loop_impl.h b/mock/distributeddb/common/src/evloop/src/event_loop_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..91c270ba396cabd2a73a0638536d77d317f95bba --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/event_loop_impl.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_LOOP_IMPL_H +#define EVENT_LOOP_IMPL_H + +#include +#include +#include +#include "platform_specific.h" +#include "../include/ievent_loop.h" +#include "../include/ievent.h" + +#if defined EVLOOP_TIMER_ONLY +#define EVENT_LOOP_USE_SELECT +#else +#define EVENT_LOOP_USE_EPOLL +#endif + +namespace DistributedDB { +class EventImpl; +class EventRequest; + +class EventLoopImpl : public IEventLoop { +public: + EventLoopImpl(); + ~EventLoopImpl() override; + DISABLE_COPY_ASSIGN_MOVE(EventLoopImpl); + + int Add(IEvent *event) override; + int Remove(IEvent *event) override; + int Run() override; + int Modify(EventImpl *event, bool isAdd, EventsMask events); + int Modify(EventImpl *event, EventTime time); + + // Initialize the loop, code removed from the constructor. + virtual int Initialize() = 0; + bool IsInLoopThread(bool &started) const; + +private: + virtual int Prepare(const std::set &polling) = 0; + virtual int Poll(EventTime sleepTime) = 0; + virtual int WakeUp() = 0; + virtual int Exit(const std::set &polling) = 0; + virtual int AddEvent(EventImpl *event) = 0; + virtual int RemoveEvent(EventImpl *event) = 0; + virtual int ModifyEvent(EventImpl *event, bool isAdd, EventsMask events) = 0; + virtual EventTime GetTime() const; + + template + int QueueRequest(int type, EventImpl *event, T argument); + int SendRequestToLoop(EventRequest *eventRequest); + bool EventObjectExists(EventImpl *event) const; + bool EventFdExists(const EventImpl *event) const; + int AddEventObject(EventImpl *event, EventTime now); + int RemoveEventObject(EventImpl *event); + int ModifyEventObject(EventImpl *event, bool isAdd, EventsMask events); + int ModifyEventObject(EventImpl *event, EventTime timeout); + void ProcessRequest(std::list &requests); + int ProcessRequest(); + EventTime CalSleepTime() const; + int DispatchAll(); + void CleanLoop(); + void OnKillLoop(); + + std::list requests_; + std::set polling_; + bool pollingSetChanged_; + std::thread::id loopThread_; +}; +} + +#endif // EVENT_LOOP_IMPL_H diff --git a/mock/distributeddb/common/src/evloop/src/event_loop_select.cpp b/mock/distributeddb/common/src/evloop/src/event_loop_select.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b26b382ff63fba84bd1e563d7bf5dc69791808cc --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/event_loop_select.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "event_loop_select.h" + +#ifdef EVENT_LOOP_USE_SELECT +#include +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +EventLoopSelect::EventLoopSelect() +{ +} + +EventLoopSelect::~EventLoopSelect() +{ +} + +int EventLoopSelect::Initialize() +{ + return E_OK; +} + +int EventLoopSelect::Prepare(const std::set &polling) +{ + (void)polling; + return E_OK; +} + +int EventLoopSelect::Poll(EventTime sleepTime) +{ + std::unique_lock lockGuard(wakeUpMutex_); + auto now = std::chrono::system_clock::now(); + auto to = now + std::chrono::milliseconds(sleepTime); + wakeUpCondition_.wait_until(lockGuard, to); + return E_OK; +} + +int EventLoopSelect::WakeUp() +{ + wakeUpCondition_.notify_one(); + return E_OK; +} + +int EventLoopSelect::Exit(const std::set &polling) +{ + (void)polling; + return E_OK; +} + +int EventLoopSelect::AddEvent(EventImpl *event) +{ + (void)event; + return E_OK; +} + +int EventLoopSelect::RemoveEvent(EventImpl *event) +{ + (void)event; + return E_OK; +} + +int EventLoopSelect::ModifyEvent(EventImpl *event, bool isAdd, EventsMask events) +{ + (void)event; + (void)isAdd; + (void)events; + return E_OK; +} + +DEFINE_OBJECT_TAG_FACILITIES(EventLoopSelect) +} // namespace DistributedDB + +#endif // EVENT_LOOP_USE_SELECT diff --git a/mock/distributeddb/common/src/evloop/src/event_loop_select.h b/mock/distributeddb/common/src/evloop/src/event_loop_select.h new file mode 100644 index 0000000000000000000000000000000000000000..a4d77d15f47b2b64e412fb6b84a5f14898031b0c --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/event_loop_select.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_LOOP_SELECT_H +#define EVENT_LOOP_SELECT_H + +#include "event_loop_impl.h" + +#ifdef EVENT_LOOP_USE_SELECT +#include +#include + +namespace DistributedDB { +class EventLoopSelect : public EventLoopImpl { +public: + EventLoopSelect(); + ~EventLoopSelect(); + + int Initialize() override; + +private: + int Prepare(const std::set &polling) override; + int Poll(EventTime sleepTime) override; + int WakeUp() override; + int Exit(const std::set &polling) override; + int AddEvent(EventImpl *event) override; + int RemoveEvent(EventImpl *event) override; + int ModifyEvent(EventImpl *event, bool isAdd, EventsMask events) override; + + DECLARE_OBJECT_TAG(EventLoopSelect); + + std::mutex wakeUpMutex_; + std::condition_variable wakeUpCondition_; +}; +} // namespace DistributedDB +#endif // EVENT_LOOP_USE_SELECT +#endif // EVENT_LOOP_SELECT_H diff --git a/mock/distributeddb/common/src/evloop/src/ievent.cpp b/mock/distributeddb/common/src/evloop/src/ievent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..361edfe06a508b0f66c45ec6e0d9d2f06b72e2a9 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/ievent.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../include/ievent.h" +#include "db_errno.h" +#include "event_impl.h" + +namespace DistributedDB { +IEvent *IEvent::CreateEvent(EventTime timeout, int &errCode) +{ + if (timeout < 0) { + errCode = -E_INVALID_ARGS; + return nullptr; + } + + IEvent *event = new (std::nothrow) EventImpl(timeout); + if (event == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + errCode = E_OK; + return event; +} + +IEvent *IEvent::CreateEvent(EventFd fd, EventsMask events, + EventTime timeout, int &errCode) +{ + errCode = -E_INVALID_ARGS; + if (!events) { + return nullptr; + } + if ((events & ET_TIMEOUT) && (timeout < 0)) { + return nullptr; + } + if (!(events & ET_TIMEOUT) && !fd.IsValid()) { + return nullptr; + } + + IEvent *event = new (std::nothrow) EventImpl(fd, events, timeout); + if (event == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + errCode = E_OK; + return event; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/evloop/src/ievent_loop.cpp b/mock/distributeddb/common/src/evloop/src/ievent_loop.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce30ab3608aa68abe651659f3bc9e4b8bc6f0807 --- /dev/null +++ b/mock/distributeddb/common/src/evloop/src/ievent_loop.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "event_loop_impl.h" +#include "db_errno.h" + +#ifdef EVENT_LOOP_USE_EPOLL +#include "event_loop_epoll.h" +using EventLoop = DistributedDB::EventLoopEpoll; +#else +#include "event_loop_select.h" +using EventLoop = DistributedDB::EventLoopSelect; +#endif + +namespace DistributedDB { +IEventLoop *IEventLoop::CreateEventLoop(int &errCode) +{ + EventLoopImpl *loop = new (std::nothrow) EventLoop; + if (loop == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + + errCode = loop->Initialize(); + if (errCode != E_OK) { + delete loop; + loop = nullptr; + } + return loop; +} +} diff --git a/mock/distributeddb/common/src/flatbuffer_schema.cpp b/mock/distributeddb/common/src/flatbuffer_schema.cpp new file mode 100644 index 0000000000000000000000000000000000000000..021b5ec74002ce034825320a1e621d398e42d5de --- /dev/null +++ b/mock/distributeddb/common/src/flatbuffer_schema.cpp @@ -0,0 +1,1005 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "schema_object.h" +#include +#include +#include "schema_constant.h" +#include "schema_utils.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +namespace { +#ifndef OMIT_FLATBUFFER +constexpr double EPSILON = 0.000001; // 0.000001 for tolerance +inline bool IsDoubleNearlyEqual(double left, double right) +{ + if (std::fabs(left - right) < EPSILON) { + return true; + } + double absBigger = std::max(std::fabs(left), std::fabs(right)); + double relativeDiff = ((absBigger == 0.0) ? 0.0 : (std::fabs(left - right) / absBigger)); // 0.0 for double 0 + return relativeDiff < EPSILON; +} +#endif // OMIT_FLATBUFFER +} + +void SchemaObject::FlatBufferSchema::CopyFrom(const FlatBufferSchema &other) +{ + // The SchemaObject guarantee not CopyFrom "Self"; owner_ can only be set at construction. + description_ = other.description_; +} + +std::string SchemaObject::FlatBufferSchema::GetDescription() const +{ + return description_; +} + +#ifndef OMIT_FLATBUFFER +bool SchemaObject::FlatBufferSchema::IsFlatBufferSchema(const std::string &inOriginal, std::string &outDecoded) +{ + if (inOriginal.empty()) { + LOGE("[FBSchema][Is] OriSchema empty."); + return false; + } + if (inOriginal.size() >= SchemaConstant::SCHEMA_STRING_SIZE_LIMIT * 2) { // 2 :Maximum base64 encode size multiple + // Base64 encode will not exceed 2 times original binary + LOGE("[FBSchema][Is] OriSchemaSize=%zu too large even after base64 encode.", inOriginal.size()); + return false; + } + auto oriSchemaBuf = reinterpret_cast(inOriginal.c_str()); + flatbuffers::Verifier oriVerifier(oriSchemaBuf, inOriginal.size()); + if (reflection::VerifySizePrefixedSchemaBuffer(oriVerifier)) { + outDecoded = inOriginal; // The original one is the decoded one + return true; + } + outDecoded.clear(); + return false; +} + +// A macro check pointer get from flatbuffer that won't be nullptr(required field) in fact after verified by flatbuffer +#define CHECK_NULL_UNLIKELY_RETURN_ERROR(pointer) \ + if ((pointer) == nullptr) { \ + return -E_INTERNAL_ERROR; \ + } + +namespace { +constexpr uint32_t ROOT_DEFINE_DEPTH = 0; +constexpr uint32_t SIZE_PREFIX_SIZE = sizeof(flatbuffers::uoffset_t); +constexpr int32_t INDEX_OF_NOT_ENUM = -1; + +inline bool AttributeExistAndHasValue(const reflection::KeyValue *inAttr) +{ + return (inAttr != nullptr) && (inAttr->value() != nullptr) && (inAttr->value()->size() > 0); +} + +inline bool IsIntegerType(reflection::BaseType inType) +{ + return (inType >= reflection::BaseType::Bool) && (inType <= reflection::BaseType::ULong); +} + +inline bool IsRealType(reflection::BaseType inType) +{ + return (inType >= reflection::BaseType::Float) && (inType <= reflection::BaseType::Double); +} + +inline bool IsScalarType(reflection::BaseType inType) +{ + return IsIntegerType(inType) || IsRealType(inType); +} + +inline bool IsStringType(reflection::BaseType inType) +{ + return inType == reflection::BaseType::String; +} + +inline bool IsIndexableType(reflection::BaseType inType) +{ + return IsScalarType(inType) || IsStringType(inType); +} + +inline bool IsVectorType(reflection::BaseType inType) +{ + return inType == reflection::BaseType::Vector; +} + +inline bool IsStringOrVectorType(reflection::BaseType inType) +{ + return IsStringType(inType) || IsVectorType(inType); +} + +inline bool IsObjectType(reflection::BaseType inType) +{ + return inType == reflection::BaseType::Obj; +} + +inline bool IsSupportTypeAtRoot(reflection::BaseType inType) +{ + return IsIndexableType(inType) || IsVectorType(inType) || IsObjectType(inType); +} + +inline bool IsRequiredSupportType(reflection::BaseType inType) +{ + return IsStringOrVectorType(inType) || IsObjectType(inType); +} + +inline bool IsConflict(bool deprecated, bool required) +{ + return deprecated && required; +} +} + +int SchemaObject::FlatBufferSchema::ParseFlatBufferSchema(const std::string &inDecoded) +{ + description_.clear(); // For recovering from a fail parse + // The upper logic had guaranteed that the inDecoded be verified OK + auto schema = reflection::GetSizePrefixedSchema(inDecoded.c_str()); + CHECK_NULL_UNLIKELY_RETURN_ERROR(schema); + auto rootTable = schema->root_table(); + if (rootTable == nullptr || rootTable->is_struct()) { + LOGE("[FBSchema][Parse] Root table nullptr or is struct."); + return -E_SCHEMA_PARSE_FAIL; + } + + auto rootTableName = rootTable->name(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(rootTableName); + description_ += ("RootTableName=" + SchemaUtils::StripNameSpace(rootTableName->str()) + ";"); + + int errCode = ParseCheckRootTableAttribute(*rootTable); + if (errCode != E_OK) { + return errCode; + } + + RawIndexInfos indexCollect; + errCode = ParseCheckRootTableDefine(*schema, *rootTable, indexCollect); + if (errCode != E_OK) { + return errCode; + } + + errCode = ParseCheckIndexes(indexCollect); + if (errCode != E_OK) { + return errCode; + } + return E_OK; +} + +int SchemaObject::FlatBufferSchema::CompareFlatBufferDefine(const FlatBufferSchema &other) const +{ + // Schema had been parsed and constraint checked before this function is called, so as we assumed. + // Here in the compare procedure, we only check null-point, do not check or suspect of constraint any more. + auto selfSchema = GetSchema(); + auto otherSchema = other.GetSchema(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfSchema); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherSchema); + auto selfRootTable = selfSchema->root_table(); + auto otherRootTable = otherSchema->root_table(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfRootTable); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherRootTable); + auto selfRootTableName = selfRootTable->name(); + auto otherRootTableName = otherRootTable->name(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfRootTableName); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherRootTableName); + + std::string selfRootName = SchemaUtils::StripNameSpace(selfRootTableName->str()); + std::string otherRootName = SchemaUtils::StripNameSpace(otherRootTableName->str()); + if (selfRootName != otherRootName) { + LOGE("[FBSchema][Compare] RootName differ, self=%s, other=%s.", selfRootName.c_str(), otherRootName.c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + // We don't have to compare rootTableAttribute or index here, they are done by SchemaObject + std::set comparedTypeNameSet; + return CompareTableOrStructDefine({selfSchema, otherSchema}, {selfRootTable, otherRootTable}, true, + comparedTypeNameSet); +} + +namespace { +int CheckSizePrefixRawValue(const RawValue &inValue) +{ + if (inValue.first == nullptr) { // Unlikely + return -E_INVALID_ARGS; + } + if (inValue.second <= SIZE_PREFIX_SIZE) { + LOGE("[FBSchema][CheckSizePreValue] ValueSize=%u too short.", inValue.second); + return -E_INVALID_ARGS; + } + auto realSize = flatbuffers::ReadScalar(inValue.first); + if (realSize != inValue.second - SIZE_PREFIX_SIZE) { + LOGE("[FBSchema][CheckSizePreValue] RealSize=%u mismatch valueSize=(%u-4).", realSize, inValue.second); + return -E_INVALID_ARGS; + } + return E_OK; +} +} + +int SchemaObject::FlatBufferSchema::VerifyFlatBufferValue(const RawValue &inValue, bool tryNoSizePrefix) const +{ + (void)tryNoSizePrefix; // Use it in the future, currently we demand value is sizePrefixed + int errCode = CheckSizePrefixRawValue(inValue); + if (errCode != E_OK) { + return errCode; + } + auto schema = GetSchema(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(schema); + auto rootTable = schema->root_table(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(rootTable); + if (!flatbuffers::Verify(*schema, *rootTable, inValue.first + SIZE_PREFIX_SIZE, + inValue.second - SIZE_PREFIX_SIZE)) { + return -E_FLATBUFFER_VERIFY_FAIL; + } + return E_OK; +} + +namespace { +FieldType MapFieldType(reflection::BaseType inType) +{ + static std::map fieldTypeMap{ + {reflection::BaseType::Bool, FieldType::LEAF_FIELD_BOOL}, + {reflection::BaseType::Byte, FieldType::LEAF_FIELD_INTEGER}, + {reflection::BaseType::UByte, FieldType::LEAF_FIELD_INTEGER}, + {reflection::BaseType::Short, FieldType::LEAF_FIELD_INTEGER}, + {reflection::BaseType::UShort, FieldType::LEAF_FIELD_INTEGER}, + {reflection::BaseType::Int, FieldType::LEAF_FIELD_INTEGER}, + {reflection::BaseType::UInt, FieldType::LEAF_FIELD_LONG}, + {reflection::BaseType::Long, FieldType::LEAF_FIELD_LONG}, + {reflection::BaseType::ULong, FieldType::LEAF_FIELD_DOUBLE}, + {reflection::BaseType::Float, FieldType::LEAF_FIELD_DOUBLE}, + {reflection::BaseType::Double, FieldType::LEAF_FIELD_DOUBLE}, + {reflection::BaseType::String, FieldType::LEAF_FIELD_STRING}, + {reflection::BaseType::Vector, FieldType::LEAF_FIELD_ARRAY}, + {reflection::BaseType::Obj, FieldType::INTERNAL_FIELD_OBJECT}, + }; + if (fieldTypeMap.count(inType) == 0) { + return FieldType::LEAF_FIELD_NULL; + } + return fieldTypeMap[inType]; +} + +RawString CheckDollarDotAndSkipIt(RawString inPath) +{ + if (inPath == nullptr) { + return nullptr; + } + auto pathStr = inPath; + if (*pathStr++ != '$') { + return nullptr; + } + if (*pathStr++ != '.') { + return nullptr; + } + if (*pathStr == 0) { + return nullptr; + } + return pathStr; +} + +const reflection::Field *GetFieldInfoFromSchemaByPath(const reflection::Schema &schema, RawString pathStr) +{ + auto rootTable = schema.root_table(); + if (rootTable == nullptr) { // Unlikely + return nullptr; + } + auto rootFields = rootTable->fields(); + if (rootFields == nullptr) { // Unlikely + return nullptr; + } + // Unlikely to return nullptr, except internal-error happened + return rootFields->LookupByKey(pathStr); +} + +int DoVerifyBeforeExtract(const flatbuffers::Table &rootValue, const reflection::Field &fieldInfo, + const flatbuffers::Verifier &verifier) +{ + auto type = fieldInfo.type(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(type); + bool verifyResult = true; + switch (type->base_type()) { + case reflection::Bool: + case reflection::Byte: + case reflection::UByte: + verifyResult = rootValue.VerifyField(verifier, fieldInfo.offset()); + break; + case reflection::Short: + case reflection::UShort: + verifyResult = rootValue.VerifyField(verifier, fieldInfo.offset()); + break; + case reflection::Int: + case reflection::UInt: + verifyResult = rootValue.VerifyField(verifier, fieldInfo.offset()); + break; + case reflection::Long: + case reflection::ULong: + verifyResult = rootValue.VerifyField(verifier, fieldInfo.offset()); + break; + case reflection::Float: + verifyResult = rootValue.VerifyField(verifier, fieldInfo.offset()); + break; + case reflection::Double: + verifyResult = rootValue.VerifyField(verifier, fieldInfo.offset()); + break; + case reflection::String: + verifyResult = rootValue.VerifyField(verifier, fieldInfo.offset()) && + verifier.VerifyString(flatbuffers::GetFieldS(rootValue, fieldInfo)); // VerifyString can accept null + break; + default: + return -E_NOT_SUPPORT; + } + return (verifyResult ? E_OK : -E_FLATBUFFER_VERIFY_FAIL); +} + +inline std::string DoExtractString(const flatbuffers::Table &rootValue, const reflection::Field &fieldInfo) +{ + auto strVal = flatbuffers::GetFieldS(rootValue, fieldInfo); + if (strVal == nullptr) { + return ""; + } + return strVal->str(); +} + +int DoExtractValue(const flatbuffers::Table &rootValue, const reflection::Field &fieldInfo, TypeValue &outExtract) +{ + auto type = fieldInfo.type(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(type); + switch (type->base_type()) { + case reflection::Bool: + outExtract.second.boolValue = (flatbuffers::GetFieldI(rootValue, fieldInfo) != 0); + break; + case reflection::Byte: + outExtract.second.integerValue = flatbuffers::GetFieldI(rootValue, fieldInfo); + break; + case reflection::UByte: + outExtract.second.integerValue = flatbuffers::GetFieldI(rootValue, fieldInfo); + break; + case reflection::Short: + outExtract.second.integerValue = flatbuffers::GetFieldI(rootValue, fieldInfo); + break; + case reflection::UShort: + outExtract.second.integerValue = flatbuffers::GetFieldI(rootValue, fieldInfo); + break; + case reflection::Int: + outExtract.second.integerValue = flatbuffers::GetFieldI(rootValue, fieldInfo); + break; + case reflection::UInt: + outExtract.second.longValue = flatbuffers::GetFieldI(rootValue, fieldInfo); + break; + case reflection::Long: + outExtract.second.longValue = flatbuffers::GetFieldI(rootValue, fieldInfo); + break; + case reflection::ULong: + outExtract.second.doubleValue = flatbuffers::GetFieldI(rootValue, fieldInfo); + break; + case reflection::Float: + outExtract.second.doubleValue = flatbuffers::GetFieldF(rootValue, fieldInfo); + break; + case reflection::Double: + outExtract.second.doubleValue = flatbuffers::GetFieldF(rootValue, fieldInfo); + break; + case reflection::String: + outExtract.second.stringValue = DoExtractString(rootValue, fieldInfo); + break; + default: + return -E_NOT_SUPPORT; + } + return E_OK; +} + +int ExtractFlatBufferValueFinal(const flatbuffers::Table &rootValue, const reflection::Field &fieldInfo, + const flatbuffers::Verifier &verifier, TypeValue &outExtract) +{ + auto type = fieldInfo.type(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(type); + auto baseType = type->base_type(); + if (!IsIndexableType(baseType)) { + LOGE("[ExtractFinal] BaseType=%s not indexable.", reflection::EnumNameBaseType(baseType)); + return -E_NOT_SUPPORT; + } + outExtract.first = MapFieldType(type->base_type()); + int errCode = DoVerifyBeforeExtract(rootValue, fieldInfo, verifier); + if (errCode != E_OK) { + LOGE("[ExtractFinal] DoVerify fail, errCode=%d.", errCode); + return errCode; + } + errCode = DoExtractValue(rootValue, fieldInfo, outExtract); + if (errCode != E_OK) { + LOGE("[ExtractFinal] DoExtract fail, errCode=%d.", errCode); + return errCode; + } + return E_OK; +} +} + +int SchemaObject::FlatBufferSchema::ExtractFlatBufferValue(RawString inPath, const RawValue &inValue, + TypeValue &outExtract, bool tryNoSizePrefix) const +{ + // NOTE!!! This function is performance sensitive !!! Carefully not to allocate memory often!!! + (void)tryNoSizePrefix; // Use it in the future, currently we demand value is sizePrefixed + int errCode = CheckSizePrefixRawValue(inValue); + if (errCode != E_OK) { + return errCode; + } + auto pathStr = CheckDollarDotAndSkipIt(inPath); + if (pathStr == nullptr) { // Unlikely + LOGE("[FBSchema][Extract] inPath not begin with $. or nothing after it."); + return -E_INVALID_ARGS; + } + auto schema = GetSchema(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(schema); + // Currently we don't support nest-path + auto fieldInfo = GetFieldInfoFromSchemaByPath(*schema, pathStr); + if (fieldInfo == nullptr) { + LOGE("[FBSchema][Extract] FieldInfo of path=%s not found.", pathStr); + return -E_INTERNAL_ERROR; + } + // Begin extract, we have to minimal verify if we don't trust value from database + auto valueRealBegin = inValue.first + SIZE_PREFIX_SIZE; + auto valueRealSize = inValue.second - SIZE_PREFIX_SIZE; + flatbuffers::Verifier verifier(valueRealBegin, valueRealSize); + auto offset = verifier.VerifyOffset(0); // Attention: Verify root offset before we call GetAnyRoot + if (offset == 0) { + LOGE("[FBSchema][Extract] Verity root offset failed."); + return -E_FLATBUFFER_VERIFY_FAIL; + } + auto rootValue = flatbuffers::GetAnyRoot(valueRealBegin); + if (rootValue == nullptr) { + LOGE("[FBSchema][Extract] Get rootTable from value fail."); + return -E_INVALID_DATA; + } + // Verity vTable of rootTable before we extract anything from rootValue by reflection + bool vTableOk = rootValue->VerifyTableStart(verifier); + if (!vTableOk) { + LOGE("[FBSchema][Extract] Verify vTable of rootTable of value fail."); + return -E_FLATBUFFER_VERIFY_FAIL; + } + errCode = ExtractFlatBufferValueFinal(*rootValue, *fieldInfo, verifier, outExtract); + if (errCode != E_OK) { + return errCode; + } + verifier.EndTable(); + return E_OK; +} + +const reflection::Schema *SchemaObject::FlatBufferSchema::GetSchema() const +{ + // This function is called after schemaString_ had been verified by flatbuffer + return reflection::GetSizePrefixedSchema(owner_.schemaString_.c_str()); +} + +int SchemaObject::FlatBufferSchema::ParseCheckRootTableAttribute(const reflection::Object &rootTable) +{ + auto rootTableAttr = rootTable.attributes(); + if (rootTableAttr == nullptr) { + LOGE("[FBSchema][ParseRootAttr] Root table no attribute."); + return -E_SCHEMA_PARSE_FAIL; + } + + auto versionAttr = rootTableAttr->LookupByKey(SchemaConstant::KEYWORD_SCHEMA_VERSION.c_str()); + if (!AttributeExistAndHasValue(versionAttr)) { + LOGE("[FBSchema][ParseRootAttr] No SCHEMA_VERSION attribute or no value."); + return -E_SCHEMA_PARSE_FAIL; + } + if (SchemaUtils::Strip(versionAttr->value()->str()) != SchemaConstant::SCHEMA_SUPPORT_VERSION) { + LOGE("[FBSchema][ParseRootAttr] Unexpect SCHEMA_VERSION=%s.", versionAttr->value()->c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + owner_.schemaVersion_ = SchemaConstant::SCHEMA_SUPPORT_VERSION; + description_ += (SchemaConstant::KEYWORD_SCHEMA_VERSION + "=" + SchemaConstant::SCHEMA_SUPPORT_VERSION + ";"); + + auto skipsizeAttr = rootTableAttr->LookupByKey(SchemaConstant::KEYWORD_SCHEMA_SKIPSIZE.c_str()); + if (!AttributeExistAndHasValue(skipsizeAttr)) { + LOGI("[FBSchema][ParseRootAttr] No SCHEMA_SKIPSIZE attribute or no value."); + owner_.schemaSkipSize_ = 0; // Default skipsize value + return E_OK; + } + std::string skipsizeStr = SchemaUtils::Strip(skipsizeAttr->value()->str()); + int skipsizeInt = strtol(skipsizeStr.c_str(), nullptr, 10); // 10: decimal + if (std::to_string(skipsizeInt) != skipsizeStr || skipsizeInt < 0 || + static_cast(skipsizeInt) > SchemaConstant::SCHEMA_SKIPSIZE_MAX) { + LOGE("[FBSchema][ParseRootAttr] Unexpect SCHEMA_SKIPSIZE value=%s.", skipsizeAttr->value()->c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + owner_.schemaSkipSize_ = static_cast(skipsizeInt); + description_ += (SchemaConstant::KEYWORD_SCHEMA_SKIPSIZE + "=" + skipsizeStr + ";"); + return E_OK; +} + +int SchemaObject::FlatBufferSchema::ParseCheckRootTableDefine(const reflection::Schema &schema, + const reflection::Object &rootTable, RawIndexInfos &indexCollect) +{ + // Clear schemaDefine_ to recover from a fail parse + owner_.schemaDefine_.clear(); + auto fields = rootTable.fields(); + if (fields == nullptr || fields->size() == 0) { + LOGE("[FBSchema][ParseRootDefine] Empty define."); + return -E_SCHEMA_PARSE_FAIL; + } + for (uint32_t i = 0; i < fields->size(); i++) { + auto eachField = (*fields)[i]; + CHECK_NULL_UNLIKELY_RETURN_ERROR(eachField); + + auto name = eachField->name(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(name); + int errCode = SchemaUtils::CheckFieldName(name->str()); + if (errCode != E_OK) { + LOGE("[FBSchema][ParseRootDefine] Invalid fieldName=%s, errCode=%d.", name->c_str(), errCode); + return -E_SCHEMA_PARSE_FAIL; + } + FieldPath path{name->str()}; + if (owner_.schemaDefine_[ROOT_DEFINE_DEPTH].count(path) != 0) { // Unlikely + LOGE("[FBSchema][ParseRootDefine] FieldPath=%s already exist at root.", name->c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + + errCode = ParseCheckFieldInfo(schema, *eachField, path, indexCollect); + if (errCode != E_OK) { + LOGE("[FBSchema][ParseRootDefine] ParseFieldInfo errCode=%d, FieldPath=%s.", errCode, + SchemaUtils::FieldPathString(path).c_str()); + return errCode; + } + } + uint32_t fieldPathCount = 0; + for (uint32_t depth = ROOT_DEFINE_DEPTH; depth < SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX; depth++) { + if (owner_.schemaDefine_.count(depth) != 0) { + fieldPathCount += owner_.schemaDefine_[depth].size(); + } + } + if (fieldPathCount > SchemaConstant::SCHEMA_FEILD_NAME_COUNT_MAX) { + LOGE("[FBSchema][ParseRootDefine] FieldPath count=%u exceed the limitation.", fieldPathCount); + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; +} + +namespace { +bool CheckFieldTypeSupport(const reflection::Type &inType, bool isRootField) +{ + auto baseType = inType.base_type(); + if (isRootField) { + if (!IsSupportTypeAtRoot(baseType)) { + LOGE("[FBSchema][DecideType] BaseType=%s not support at root.", reflection::EnumNameBaseType(baseType)); + return false; + } + if (IsIntegerType(baseType) && (inType.index() != INDEX_OF_NOT_ENUM)) { + LOGE("[FBSchema][DecideType] BaseType=%s is enum, not support.", reflection::EnumNameBaseType(baseType)); + return false; + } + if (IsVectorType(baseType)) { + auto elementType = inType.element(); + if (!IsIndexableType(elementType)) { + LOGE("[FBSchema][DecideType] ElementType=%s not support for vector.", + reflection::EnumNameBaseType(elementType)); + return false; + } + } + } else { + // Currently only support nest in Struct, support only scalar and nest-struct + if (!IsScalarType(baseType) && !IsObjectType(baseType)) { + LOGE("[FBSchema][DecideType] BaseType=%s not support for struct.", reflection::EnumNameBaseType(baseType)); + return false; + } + } + return true; +} +} + +int SchemaObject::FlatBufferSchema::ParseCheckFieldInfo(const reflection::Schema &schema, + const reflection::Field &field, const FieldPath &path, RawIndexInfos &indexCollect) +{ + if (path.empty() || path.size() > SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX) { + LOGE("[FBSchema][ParseField] FieldPath size=%zu invalid.", path.size()); + return -E_SCHEMA_PARSE_FAIL; + } + uint32_t depth = path.size() - 1; // Depth count from zero + bool isRootField = (depth == ROOT_DEFINE_DEPTH); + SchemaAttribute &fieldInfo = owner_.schemaDefine_[depth][path]; // Create new entry in schemaDefine_ + + auto type = field.type(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(type); + if (!CheckFieldTypeSupport(*type, isRootField)) { + return -E_SCHEMA_PARSE_FAIL; + } + auto baseType = type->base_type(); + // Only type and isIndexable of SchemaAttribute is necessary + fieldInfo.type = MapFieldType(baseType); + fieldInfo.isIndexable = (IsIndexableType(baseType) && isRootField); + description_ += (SchemaUtils::FieldPathString(path) + "=" + reflection::EnumNameBaseType(baseType) + ";"); + + if (IsRequiredSupportType(baseType)) { + if (IsConflict(field.deprecated(), field.required())) { + LOGE("[FBSchema][ParseField] Deprecated conflict with required."); + return -E_SCHEMA_PARSE_FAIL; + } + } + if (fieldInfo.isIndexable) { + CollectRawIndexInfos(field, indexCollect); + } + if (IsObjectType(baseType)) { + int errCode = ParseCheckStructDefine(schema, field, path); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} + +void SchemaObject::FlatBufferSchema::CollectRawIndexInfos(const reflection::Field &field, + RawIndexInfos &indexCollect) const +{ + auto name = field.name(); + if (name == nullptr) { // Not possible + return; + } + auto fieldAttr = field.attributes(); + if (fieldAttr == nullptr) { + return; + } + auto indexAttr = fieldAttr->LookupByKey(SchemaConstant::KEYWORD_INDEX.c_str()); + if (indexAttr == nullptr) { + return; + } + if (indexAttr->value() == nullptr) { + indexCollect[name->str()] = ""; // Must be SingleField-Index + return; + } + indexCollect[name->str()] = indexAttr->value()->str(); // May still be empty string +} + +int SchemaObject::FlatBufferSchema::ParseCheckStructDefine(const reflection::Schema &schema, + const reflection::Field &field, const FieldPath &path) +{ + if (path.size() >= SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX) { + LOGE("[FBSchema][ParseStruct] Struct define at depth limitation."); + return -E_SCHEMA_PARSE_FAIL; + } + auto objects = schema.objects(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(objects); + auto type = field.type(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(type); + auto objIndex = type->index(); + if (objIndex < 0 || static_cast(objIndex) >= objects->size()) { // Unlikely + return -E_INTERNAL_ERROR; + } + auto structObj = (*objects)[objIndex]; + CHECK_NULL_UNLIKELY_RETURN_ERROR(structObj); + auto structName = structObj->name(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(structName); + if (!structObj->is_struct()) { + LOGE("[FBSchema][ParseStruct] Nest table=%s not support.", structName->c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + description_ += ("StructName=" + SchemaUtils::StripNameSpace(structName->str()) + ";"); + + // Parse fields + auto structFields = structObj->fields(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(structFields); + // Flatbuffer guarantee that struct will not be empty size, even if it is empty, we just ignore it + for (uint32_t i = 0; i < structFields->size(); i++) { + auto eachField = (*structFields)[i]; + CHECK_NULL_UNLIKELY_RETURN_ERROR(eachField); + auto eachName = eachField->name(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(eachName); + int errCode = SchemaUtils::CheckFieldName(eachName->str()); + if (errCode != E_OK) { + LOGE("[FBSchema][ParseStruct] Invalid fieldName=%s, errCode=%d.", eachName->c_str(), errCode); + return -E_SCHEMA_PARSE_FAIL; + } + FieldPath eachPath = path; + eachPath.push_back(eachName->str()); + RawIndexInfos notUsed; + errCode = ParseCheckFieldInfo(schema, *eachField, eachPath, notUsed); + if (errCode != E_OK) { + LOGE("[FBSchema][ParseStruct] ParseFieldInfo errCode=%d, FieldPath=%s.", errCode, + SchemaUtils::FieldPathString(eachPath).c_str()); + return errCode; + } + } + return E_OK; +} + +namespace { +inline bool IsNotCompositeIndex(const std::string &indexStr) +{ + // In fact, test found that attrValue will be "0" if not exist + return indexStr.empty() || indexStr == std::string("0"); +} +} + +int SchemaObject::FlatBufferSchema::ParseCheckIndexes(const RawIndexInfos &indexCollect) +{ + for (const auto &entry : indexCollect) { + std::vector indexStrArray{entry.first}; // Entry.first is fieldName at root that was checked valid + const std::string &rawIndexStr = entry.second; + if (IsNotCompositeIndex(rawIndexStr)) { + int errCode = owner_.ParseCheckEachIndexFromStringArray(indexStrArray); + if (errCode != E_OK) { + LOGE("[FBSchema][ParseIndex] Create single-index=%s fail, errCode=%d.", entry.first.c_str(), errCode); + return errCode; + } + description_ += ("INDEX=" + entry.first + ";"); + continue; + } + // Parse other indexField + for (uint32_t curPos = 0; curPos < rawIndexStr.size();) { + uint32_t nextCommaPos = rawIndexStr.find_first_of(',', curPos); + std::string eachIndexField = rawIndexStr.substr(curPos, nextCommaPos - curPos); + eachIndexField = SchemaUtils::Strip(eachIndexField); + if (!eachIndexField.empty()) { // Continuous ',' just ignore + indexStrArray.push_back(eachIndexField); + } + if (nextCommaPos >= rawIndexStr.size()) { // No ',' anymore + break; + } + curPos = nextCommaPos + 1; + } + int errCode = owner_.ParseCheckEachIndexFromStringArray(indexStrArray); + if (errCode != E_OK) { + LOGE("[FBSchema][ParseIndex] Create composite-index=%s, rawStr=%s fail, errCode=%d.", entry.first.c_str(), + rawIndexStr.c_str(), errCode); + return errCode; + } + description_ += ("INDEX=" + entry.first + ";"); + } + if (owner_.schemaIndexes_.size() > SchemaConstant::SCHEMA_INDEX_COUNT_MAX) { + LOGE("[FBSchema][ParseIndex] Index count=%zu exceed limitation.", owner_.schemaIndexes_.size()); + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; +} + +namespace { +inline bool IsNotEqualNotCompatible(int errCode) +{ + return (errCode != -E_SCHEMA_EQUAL_EXACTLY) && (errCode != -E_SCHEMA_UNEQUAL_COMPATIBLE) && + (errCode != -E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE); +} + +int CompareFieldCount(bool isRoot, uint32_t selfCount, uint32_t otherCount) +{ + if (isRoot) { + if (otherCount < selfCount) { + LOGE("[FBSchema][CompareRoot] RootFieldSize: other=%u less than self=%u.", otherCount, selfCount); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } else { + if (selfCount != otherCount) { + LOGE("[FBSchema][CompareRoot] StructFieldSize: self=%u differ with other=%u.", selfCount, otherCount); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } + return (selfCount == otherCount) ? -E_SCHEMA_EQUAL_EXACTLY : -E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE; +} + +int CompareFieldInfoBesideType(const reflection::Field &selfField, const reflection::Field &otherField, + reflection::BaseType theType) +{ + // Compare offset + if (selfField.offset() != otherField.offset()) { + LOGE("[FBSchema][CompareField] Offset differ: self=%u, other=%u.", selfField.offset(), otherField.offset()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + // Compare default value + if (selfField.default_integer() != otherField.default_integer()) { + LOGE("[FBSchema][CompareField] DefaultInteger differ: self=%lld, other=%lld.", + static_cast(selfField.default_integer()), static_cast(otherField.default_integer())); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + // QUEER: for the same default_real value in fbs, flatbuffer will generate different value in binary ??? + if (!IsDoubleNearlyEqual(selfField.default_real(), otherField.default_real())) { + LOGE("[FBSchema][CompareField] DefaultReal differ: self=%f, other=%f.", selfField.default_real(), + otherField.default_real()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + // Ignore deprecated, Compare required + if (IsRequiredSupportType(theType)) { + if (selfField.required() != otherField.required()) { + LOGE("[FBSchema][CompareField] Require differ: self=%d, other=%d.", selfField.required(), + otherField.required()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } + return -E_SCHEMA_EQUAL_EXACTLY; +} + +int CompareFieldInfo(const reflection::Field &selfField, const reflection::Field &otherField, bool &isStruct) +{ + auto selfType = selfField.type(); + auto otherType = otherField.type(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfType); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherType); + // Compare type + auto selfBaseType = selfType->base_type(); + auto otherBaseType = otherType->base_type(); + if (selfBaseType != otherBaseType) { + LOGE("[FBSchema][CompareField] BaseType differ: self=%s, other=%s.", reflection::EnumNameBaseType(selfBaseType), + reflection::EnumNameBaseType(otherBaseType)); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + if (IsVectorType(selfBaseType)) { + auto selfElementType = selfType->element(); + auto otherElementType = otherType->element(); + if (selfElementType != otherElementType) { + LOGE("[FBSchema][CompareField] ElementType differ: self=%u, other=%u.", selfElementType, otherElementType); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } + if (IsObjectType(selfBaseType)) { + isStruct = true; + } + return CompareFieldInfoBesideType(selfField, otherField, selfBaseType); +} + +// Split from original functions which would be longer than 50 line +int CompareExtraField(const PairConstPointer &bothObject) +{ + // This is private function, the caller guarantee that inputParameter not nullptr + auto selfFields = bothObject.first->fields(); + auto otherFields = bothObject.second->fields(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfFields); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherFields); + // Each field in other not in self, should not be required + for (uint32_t i = 0; i < otherFields->size(); i++) { + auto eachOtherField = (*otherFields)[i]; + CHECK_NULL_UNLIKELY_RETURN_ERROR(eachOtherField); + auto otherName = eachOtherField->name(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherName); + auto correspondSelfField = selfFields->LookupByKey(otherName->c_str()); + if (correspondSelfField != nullptr) { + continue; + } + if (eachOtherField->required()) { + LOGE("[FBSchema][CompareDefine] Extra field=%s should not be required.", otherName->c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } + return -E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE; +} +} + +int SchemaObject::FlatBufferSchema::CompareTableOrStructDefine(const PairConstPointer &bothSchema, + const PairConstPointer &bothObject, bool isRoot, std::set &compared) const +{ + // This is private function, the caller guarantee that inputParameter not nullptr + auto selfFields = bothObject.first->fields(); + auto otherFields = bothObject.second->fields(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfFields); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherFields); + int errCode = CompareFieldCount(isRoot, selfFields->size(), otherFields->size()); + if (errCode == -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + return errCode; + } + // Each field in self should be in other, and they should be same + for (uint32_t i = 0; i < selfFields->size(); i++) { + auto eachSelfField = (*selfFields)[i]; + CHECK_NULL_UNLIKELY_RETURN_ERROR(eachSelfField); + auto selfName = eachSelfField->name(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfName); + auto correspondOtherField = otherFields->LookupByKey(selfName->c_str()); + if (correspondOtherField == nullptr) { + LOGE("[FBSchema][CompareDefine] SelfField=%s not found in other.", selfName->c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + bool isStruct = false; + errCode = CompareFieldInfo(*eachSelfField, *correspondOtherField, isStruct); + if (IsNotEqualNotCompatible(errCode)) { + LOGE("[FBSchema][CompareDefine] Compare info of field=%s fail, errCode=%d.", selfName->c_str(), errCode); + return errCode; + } + if (isStruct) { + // Previous parse guarantee that recursion will not be unlimited, don't be afraid. + errCode = CompareStruct(bothSchema, {eachSelfField, correspondOtherField}, compared); + if (IsNotEqualNotCompatible(errCode)) { + return errCode; + } + } + } + if (selfFields->size() == otherFields->size()) { + return -E_SCHEMA_EQUAL_EXACTLY; + } + return CompareExtraField(bothObject); +} + +int SchemaObject::FlatBufferSchema::CompareStruct(const PairConstPointer &bothSchema, + const PairConstPointer &bothField, std::set &compared) const +{ + // This is private function, the caller guarantee that inputParameter not nullptr + auto selfObjects = bothSchema.first->objects(); + auto otherObjects = bothSchema.second->objects(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfObjects); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherObjects); + auto selfType = bothField.first->type(); + auto otherType = bothField.second->type(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfType); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherType); + auto selfObjIndex = selfType->index(); + auto otherObjIndex = otherType->index(); + if (selfObjIndex < 0 || static_cast(selfObjIndex) >= selfObjects->size()) { // Unlikely + return -E_INTERNAL_ERROR; + } + if (otherObjIndex < 0 || static_cast(otherObjIndex) >= otherObjects->size()) { // Unlikely + return -E_INTERNAL_ERROR; + } + auto selfStructObj = (*selfObjects)[selfObjIndex]; + auto otherStructObj = (*otherObjects)[otherObjIndex]; + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfStructObj); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherStructObj); + // Previous parse can guarantee that they are both struct, no need to check again + auto selfStructName = selfStructObj->name(); + auto otherStructName = otherStructObj->name(); + CHECK_NULL_UNLIKELY_RETURN_ERROR(selfStructName); + CHECK_NULL_UNLIKELY_RETURN_ERROR(otherStructName); + std::string selfName = SchemaUtils::StripNameSpace(selfStructName->str()); + std::string otherName = SchemaUtils::StripNameSpace(otherStructName->str()); + if (selfName != otherName) { + LOGE("[FBSchema][CompareStruct] The field is not of same struct type, self=%s, other=%s.", + selfName.c_str(), otherName.c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + if (compared.count(selfName) != 0) { // This struct-type had already been compared, no need to do recurse again + return -E_SCHEMA_EQUAL_EXACTLY; + } + compared.insert(selfName); + // Compare struct detail + if (selfStructObj->minalign() != otherStructObj->minalign()) { + LOGE("[FBSchema][CompareStruct] The struct minalign differ, self=%d, other=%d.", + selfStructObj->minalign(), otherStructObj->minalign()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + if (selfStructObj->bytesize() != otherStructObj->bytesize()) { + LOGE("[FBSchema][CompareStruct] The struct bytesize differ, self=%d, other=%d.", + selfStructObj->bytesize(), otherStructObj->bytesize()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + // Previous parse guarantee that recursion will not be unlimited, don't be afraid. + return CompareTableOrStructDefine(bothSchema, {selfStructObj, otherStructObj}, false, compared); +} +#else // OMIT_FLATBUFFER +bool SchemaObject::FlatBufferSchema::IsFlatBufferSchema(const std::string &inOriginal, std::string &outDecoded) +{ + (void)inOriginal; + (void)outDecoded; + LOGW("FlatBuffer Omit From Compile."); + return false; +} + +int SchemaObject::FlatBufferSchema::ParseFlatBufferSchema(const std::string &inDecoded) +{ + (void)inDecoded; + owner_.schemaType_ = SchemaType::FLATBUFFER; // For fix compile warning + return -E_NOT_PERMIT; +} + +int SchemaObject::FlatBufferSchema::CompareFlatBufferDefine(const FlatBufferSchema &other) const +{ + (void)other; + return -E_NOT_PERMIT; +} + +int SchemaObject::FlatBufferSchema::VerifyFlatBufferValue(const RawValue &inValue, bool tryNoSizePrefix) const +{ + (void)inValue; + (void)tryNoSizePrefix; + return -E_NOT_PERMIT; +} + +int SchemaObject::FlatBufferSchema::ExtractFlatBufferValue(RawString inPath, const RawValue &inValue, + TypeValue &outExtract, bool tryNoSizePrefix) const +{ + (void)inPath; + (void)inValue; + (void)outExtract; + (void)tryNoSizePrefix; + return -E_NOT_PERMIT; +} +#endif // OMIT_FLATBUFFER +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/hash.cpp b/mock/distributeddb/common/src/hash.cpp new file mode 100644 index 0000000000000000000000000000000000000000..94c9f1b213d2410d30706f0d75ae479ca00ee532 --- /dev/null +++ b/mock/distributeddb/common/src/hash.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hash.h" + +namespace DistributedDB { +uint64_t Hash::HashFunc(const std::string &input) +{ + uint64_t hash = 0; + size_t idx = 0; + + for (idx = 0; idx < input.size(); idx++) { + hash = (hash * PRIME_SEED) + input.at(idx); + } + + return hash; +} + +uint32_t Hash::Hash32Func(const std::string &input) +{ + uint32_t hash = 0; + size_t idx = 0; + + for (idx = 0; idx < input.size(); idx++) { + hash = (hash << 4) + input.at(idx); // left shift the lowest 4 bits for 4 bits. + uint32_t x = (hash & 0xf0000000); + if (x != 0) { + hash ^= (x >> 24); // right shift the high byte for 24 bits. + } + hash &= ~x; + } + return (hash & 0x7fffffff); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/json_object.cpp b/mock/distributeddb/common/src/json_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eeaf99652214944485fa2f0ef854b0ebedcc2d55 --- /dev/null +++ b/mock/distributeddb/common/src/json_object.cpp @@ -0,0 +1,932 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "json_object.h" +#include +#include +#include +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +#ifndef OMIT_JSON +namespace { + const uint32_t MAX_NEST_DEPTH = 100; +#ifdef JSONCPP_USE_BUILDER + const int JSON_VALUE_PRECISION = 16; + const std::string JSON_CONFIG_INDENTATION = "indentation"; + const std::string JSON_CONFIG_COLLECT_COMMENTS = "collectComments"; + const std::string JSON_CONFIG_PRECISION = "precision"; +#endif +} +uint32_t JsonObject::maxNestDepth_ = MAX_NEST_DEPTH; + +uint32_t JsonObject::SetMaxNestDepth(uint32_t nestDepth) +{ + uint32_t preValue = maxNestDepth_; + // No need to check the reasonability, only test code will use this method + maxNestDepth_ = nestDepth; + return preValue; +} + +uint32_t JsonObject::CalculateNestDepth(const std::string &inString, int &errCode) +{ + auto begin = reinterpret_cast(inString.c_str()); + auto end = begin + inString.size(); + return CalculateNestDepth(begin, end, errCode); +} + +uint32_t JsonObject::CalculateNestDepth(const uint8_t *dataBegin, const uint8_t *dataEnd, int &errCode) +{ + if (dataBegin == nullptr || dataEnd == nullptr || dataBegin >= dataEnd) { + errCode = -E_INVALID_ARGS; + return maxNestDepth_ + 1; // return a invalid depth + } + bool isInString = false; + uint32_t maxDepth = 0; + uint32_t objectDepth = 0; + uint32_t arrayDepth = 0; + uint32_t numOfEscape = 0; + + for (auto ptr = dataBegin; ptr < dataEnd; ptr++) { + if (*ptr == '"' && numOfEscape % 2 == 0) { // 2 used to detect parity + isInString = !isInString; + continue; + } + if (!isInString) { + if (*ptr == '{') { + objectDepth++; + maxDepth = std::max(maxDepth, objectDepth + arrayDepth); + } + if (*ptr == '}') { + objectDepth = ((objectDepth > 0) ? (objectDepth - 1) : 0); + } + if (*ptr == '[') { + arrayDepth++; + maxDepth = std::max(maxDepth, objectDepth + arrayDepth); + } + if (*ptr == ']') { + arrayDepth = ((arrayDepth > 0) ? (arrayDepth - 1) : 0); + } + } + numOfEscape = ((*ptr == '\\') ? (numOfEscape + 1) : 0); + } + return maxDepth; +} + +JsonObject::JsonObject(const JsonObject &other) +{ + isValid_ = other.isValid_; + value_ = other.value_; +} + +JsonObject& JsonObject::operator=(const JsonObject &other) +{ + if (&other != this) { + isValid_ = other.isValid_; + value_ = other.value_; + } + return *this; +} + +JsonObject::JsonObject(const Json::Value &value) : isValid_(true), value_(value) +{ +} + +int JsonObject::Parse(const std::string &inString) +{ + // The jsoncpp lib parser in strict mode will still regard root type jsonarray as valid, but we require jsonobject + if (isValid_) { + LOGE("[Json][Parse] Already Valid."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + uint32_t nestDepth = CalculateNestDepth(inString, errCode); + if (errCode != E_OK || nestDepth > maxNestDepth_) { + LOGE("[Json][Parse] Json calculate nest depth failed %d, depth=%u exceed max allowed=%u.", errCode, nestDepth, + maxNestDepth_); + return -E_JSON_PARSE_FAIL; + } +#ifdef JSONCPP_USE_BUILDER + JSONCPP_STRING errs; + Json::CharReaderBuilder builder; + Json::CharReaderBuilder::strictMode(&builder.settings_); + builder[JSON_CONFIG_COLLECT_COMMENTS] = false; + std::unique_ptr const jsonReader(builder.newCharReader()); + + auto begin = reinterpret_cast(inString.c_str()); + auto end = reinterpret_cast(inString.c_str() + inString.length()); + if (!jsonReader->parse(begin, end, &value_, &errs)) { + value_ = Json::Value(); + LOGE("[Json][Parse] Parse string to JsonValue fail, reason=%s.", errs.c_str()); + return -E_JSON_PARSE_FAIL; + } +#else + Json::Reader reader(Json::Features::strictMode()); + if (!reader.parse(inString, value_, false)) { + value_ = Json::Value(); + LOGE("[Json][Parse] Parse string to JsonValue fail, reason=%s.", reader.getFormattedErrorMessages().c_str()); + return -E_JSON_PARSE_FAIL; + } +#endif + // The jsoncpp lib parser in strict mode will still regard root type jsonarray as valid, but we require jsonobject + if (value_.type() != Json::ValueType::objectValue) { + value_ = Json::Value(); + LOGE("[Json][Parse] Not an object at root."); + return -E_JSON_PARSE_FAIL; + } + isValid_ = true; + return E_OK; +} + +int JsonObject::Parse(const std::vector &inData) +{ + if (inData.empty()) { + return -E_INVALID_ARGS; + } + return Parse(inData.data(), inData.data() + inData.size()); +} + +int JsonObject::Parse(const uint8_t *dataBegin, const uint8_t *dataEnd) +{ + if (isValid_) { + LOGE("[Json][Parse] Already Valid."); + return -E_NOT_PERMIT; + } + if (dataBegin == nullptr || dataEnd == nullptr || dataBegin >= dataEnd) { + return -E_INVALID_ARGS; + } + int errCode = E_OK; + uint32_t nestDepth = CalculateNestDepth(dataBegin, dataEnd, errCode); + if (errCode != E_OK || nestDepth > maxNestDepth_) { + LOGE("[Json][Parse] Json calculate nest depth failed %d, depth=%u exceed max allowed=%u.", errCode, nestDepth, + maxNestDepth_); + return -E_JSON_PARSE_FAIL; + } +#ifdef JSONCPP_USE_BUILDER + auto begin = reinterpret_cast(dataBegin); + auto end = reinterpret_cast(dataEnd); + + JSONCPP_STRING errs; + Json::CharReaderBuilder builder; + Json::CharReaderBuilder::strictMode(&builder.settings_); + builder[JSON_CONFIG_COLLECT_COMMENTS] = false; + std::unique_ptr const jsonReader(builder.newCharReader()); + // The endDoc parameter of reader::parse refer to the byte after the string itself + if (!jsonReader->parse(begin, end, &value_, &errs)) { + value_ = Json::Value(); + LOGE("[Json][Parse] Parse dataRange to JsonValue fail, reason=%s.", errs.c_str()); + return -E_JSON_PARSE_FAIL; + } +#else + Json::Reader reader(Json::Features::strictMode()); + auto begin = reinterpret_cast(dataBegin); + auto end = reinterpret_cast(dataEnd); + // The endDoc parameter of reader::parse refer to the byte after the string itself + if (!reader.parse(begin, end, value_, false)) { + value_ = Json::Value(); + LOGE("[Json][Parse] Parse dataRange to JsonValue fail, reason=%s.", reader.getFormattedErrorMessages().c_str()); + return -E_JSON_PARSE_FAIL; + } +#endif + // The jsoncpp lib parser in strict mode will still regard root type jsonarray as valid, but we require jsonobject + if (value_.type() != Json::ValueType::objectValue) { + value_ = Json::Value(); + LOGE("[Json][Parse] Not an object at root."); + return -E_JSON_PARSE_FAIL; + } + isValid_ = true; + return E_OK; +} + +bool JsonObject::IsValid() const +{ + return isValid_; +} + +std::string JsonObject::ToString() const +{ + if (!isValid_) { + LOGE("[Json][ToString] Not Valid Yet."); + return std::string(); + } +#ifdef JSONCPP_USE_BUILDER + Json::StreamWriterBuilder writerBuilder; + writerBuilder[JSON_CONFIG_INDENTATION] = ""; + writerBuilder[JSON_CONFIG_PRECISION] = JSON_VALUE_PRECISION; + std::unique_ptr const jsonWriter(writerBuilder.newStreamWriter()); + std::stringstream ss; + jsonWriter->write(value_, &ss); + // The endingLineFeedSymbol is left empty by default. + return ss.str(); +#else + Json::FastWriter fastWriter; + // Call omitEndingLineFeed to let JsonCpp not append an \n at the end of string. If not doing so, when passing a + // minified jsonString, the result of this function will be one byte longer then the original, which may cause the + // result checked as length invalid by upper logic when the original length is just at the limitation boundary. + fastWriter.omitEndingLineFeed(); + return fastWriter.write(value_); +#endif +} + +bool JsonObject::IsFieldPathExist(const FieldPath &inPath) const +{ + if (!isValid_) { + LOGE("[Json][isExisted] Not Valid Yet."); + return false; + } + int errCode = E_OK; + (void)GetJsonValueByFieldPath(inPath, errCode); // Ignore return const reference + return (errCode == E_OK); +} + +int JsonObject::GetFieldTypeByFieldPath(const FieldPath &inPath, FieldType &outType) const +{ + if (!isValid_) { + LOGE("[Json][GetType] Not Valid Yet."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + const Json::Value &valueNode = GetJsonValueByFieldPath(inPath, errCode); + if (errCode != E_OK) { + return errCode; + } + return GetFieldTypeByJsonValue(valueNode, outType); +} + +int JsonObject::GetFieldValueByFieldPath(const FieldPath &inPath, FieldValue &outValue) const +{ + if (!isValid_) { + LOGE("[Json][GetValue] Not Valid Yet."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + const Json::Value &valueNode = GetJsonValueByFieldPath(inPath, errCode); + if (errCode != E_OK) { + return errCode; + } + FieldType valueType; + errCode = GetFieldTypeByJsonValue(valueNode, valueType); + if (errCode != E_OK) { + return errCode; + } + switch (valueType) { + case FieldType::LEAF_FIELD_BOOL: + outValue.boolValue = valueNode.asBool(); + break; + case FieldType::LEAF_FIELD_INTEGER: + outValue.integerValue = valueNode.asInt(); + break; + case FieldType::LEAF_FIELD_LONG: + outValue.longValue = valueNode.asInt64(); + break; + case FieldType::LEAF_FIELD_DOUBLE: + outValue.doubleValue = valueNode.asDouble(); + break; + case FieldType::LEAF_FIELD_STRING: + outValue.stringValue = valueNode.asString(); + break; + default: + return -E_NOT_SUPPORT; + } + return E_OK; +} + +int JsonObject::GetSubFieldPath(const FieldPath &inPath, std::set &outSubPath) const +{ + if (!isValid_) { + LOGE("[Json][GetSubPath] Not Valid Yet."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + const Json::Value &valueNode = GetJsonValueByFieldPath(inPath, errCode); + if (errCode != E_OK) { + return errCode; + } + if (valueNode.type() != Json::ValueType::objectValue) { + return -E_NOT_SUPPORT; + } + // Note: the subFields JsonCpp returnout will be different from each other + std::vector subFields = valueNode.getMemberNames(); + for (const auto &eachSubField : subFields) { + FieldPath eachSubPath = inPath; + eachSubPath.push_back(eachSubField); + outSubPath.insert(eachSubPath); + } + return E_OK; +} + +int JsonObject::GetSubFieldPath(const std::set &inPath, std::set &outSubPath) const +{ + for (const auto &eachPath : inPath) { + int errCode = GetSubFieldPath(eachPath, outSubPath); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} + +int JsonObject::GetSubFieldPathAndType(const FieldPath &inPath, std::map &outSubPathType) const +{ + if (!isValid_) { + LOGE("[Json][GetSubPathType] Not Valid Yet."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + const Json::Value &valueNode = GetJsonValueByFieldPath(inPath, errCode); + if (errCode != E_OK) { + return errCode; + } + if (valueNode.type() != Json::ValueType::objectValue) { + return -E_NOT_SUPPORT; + } + // Note: the subFields JsonCpp returnout will be different from each other + std::vector subFields = valueNode.getMemberNames(); + for (const auto &eachSubField : subFields) { + FieldPath eachSubPath = inPath; + eachSubPath.push_back(eachSubField); + FieldType eachSubType; + errCode = GetFieldTypeByJsonValue(valueNode[eachSubField], eachSubType); + if (errCode != E_OK) { + return errCode; + } + outSubPathType[eachSubPath] = eachSubType; + } + return E_OK; +} + +int JsonObject::GetSubFieldPathAndType(const std::set &inPath, + std::map &outSubPathType) const +{ + for (const auto &eachPath : inPath) { + int errCode = GetSubFieldPathAndType(eachPath, outSubPathType); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} + +int JsonObject::GetArraySize(const FieldPath &inPath, uint32_t &outSize) const +{ + if (!isValid_) { + LOGE("[Json][GetArraySize] Not Valid Yet."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + const Json::Value &valueNode = GetJsonValueByFieldPath(inPath, errCode); + if (errCode != E_OK) { + return errCode; + } + if (valueNode.type() != Json::ValueType::arrayValue) { + return -E_NOT_SUPPORT; + } + outSize = valueNode.size(); + return E_OK; +} + +int JsonObject::GetArrayContentOfStringOrStringArray(const FieldPath &inPath, + std::vector> &outContent) const +{ + if (!isValid_) { + LOGE("[Json][GetArrayContent] Not Valid Yet."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + const Json::Value &valueNode = GetJsonValueByFieldPath(inPath, errCode); + if (errCode != E_OK) { + LOGW("[Json][GetArrayContent] Get JsonValue Fail=%d.", errCode); + return errCode; + } + if (valueNode.type() != Json::ValueType::arrayValue) { + LOGE("[Json][GetArrayContent] Not an array."); + return -E_NOT_SUPPORT; + } + for (uint32_t index = 0; index < valueNode.size(); index++) { + const Json::Value &eachArrayItem = valueNode[index]; + if (eachArrayItem.isString()) { + outContent.emplace_back(std::vector({eachArrayItem.asString()})); + continue; + } + if (eachArrayItem.isArray()) { + if (eachArrayItem.empty()) { + continue; // Ignore empty array-type member + } + outContent.emplace_back(std::vector()); + errCode = GetStringArrayContentByJsonValue(eachArrayItem, outContent.back()); + if (errCode == E_OK) { + continue; // Everything ok + } + } + // If reach here, then something is not ok + outContent.clear(); + LOGE("[Json][GetArrayContent] Not string or array or GetStringArray fail=%d at index=%u.", errCode, index); + return -E_NOT_SUPPORT; + } + return E_OK; +} + +namespace { +bool InsertFieldCheckParameter(const FieldPath &inPath, FieldType inType, const FieldValue &inValue, + uint32_t maxNestDepth) +{ + if (inPath.empty() || inPath.size() > maxNestDepth || inType == FieldType::LEAF_FIELD_ARRAY || + inType == FieldType::INTERNAL_FIELD_OBJECT) { + return false; + } + // Infinite double not support + if (inType == FieldType::LEAF_FIELD_DOUBLE && !std::isfinite(inValue.doubleValue)) { + return false; + } + return true; +} + +void LeafJsonNodeAppendValue(Json::Value &leafNode, FieldType inType, const FieldValue &inValue) +{ + if (inType == FieldType::LEAF_FIELD_STRING) { + leafNode.append(Json::Value(inValue.stringValue)); + } +} + +// Function design for InsertField call on an null-type Json::Value +void LeafJsonNodeAssignValue(Json::Value &leafNode, FieldType inType, const FieldValue &inValue) +{ + switch (inType) { + case FieldType::LEAF_FIELD_BOOL: + leafNode = Json::Value(inValue.boolValue); + break; + case FieldType::LEAF_FIELD_INTEGER: + // Cast to Json::Int to avoid "ambiguous call of overloaded function" + leafNode = Json::Value(static_cast(inValue.integerValue)); + break; + case FieldType::LEAF_FIELD_LONG: + // Cast to Json::Int64 to avoid "ambiguous call of overloaded function" + leafNode = Json::Value(static_cast(inValue.longValue)); + break; + case FieldType::LEAF_FIELD_DOUBLE: + leafNode = Json::Value(inValue.doubleValue); + break; + case FieldType::LEAF_FIELD_STRING: + leafNode = Json::Value(inValue.stringValue); + break; + case FieldType::LEAF_FIELD_OBJECT: + leafNode = Json::Value(Json::ValueType::objectValue); + break; + default: + // For LEAF_FIELD_NULL, Do nothing. + // For LEAF_FIELD_ARRAY and INTERNAL_FIELD_OBJECT, Not Support, had been excluded by InsertField + return; + } +} +} + +// move the nearest to the leaf of inPath, if not exist, will create it, else it should be an array object. +int JsonObject::MoveToPath(const FieldPath &inPath, Json::Value *&exact, Json::Value *&nearest) +{ + uint32_t nearDepth = 0; + int errCode = LocateJsonValueByFieldPath(inPath, exact, nearest, nearDepth); + if (errCode != -E_NOT_FOUND) { // Path already exist and it's not an array object + return -E_JSON_INSERT_PATH_EXIST; + } + // nearDepth 0 represent for root value. nearDepth equal to inPath.size indicate an exact path match + if (nearest == nullptr || nearDepth >= inPath.size()) { // Impossible + return -E_INTERNAL_ERROR; + } + if (nearest->type() != Json::ValueType::objectValue) { // path ends with type not object + return -E_JSON_INSERT_PATH_CONFLICT; + } + // Use nearDepth as startIndex pointing to the first field that lacked + for (uint32_t lackFieldIndex = nearDepth; lackFieldIndex < inPath.size(); lackFieldIndex++) { + // The new JsonValue is null-type, we can safely add members to an null-type JsonValue which will turn into + // object-type after member adding. Then move "nearest" to point to the new JsonValue. + nearest = &((*nearest)[inPath[lackFieldIndex]]); + } + return E_OK; +} + +int JsonObject::InsertField(const FieldPath &inPath, const JsonObject &inValue, bool isAppend) +{ + if (inPath.empty() || inPath.size() > maxNestDepth_ || !inValue.IsValid()) { + return -E_INVALID_ARGS; + } + if (!isValid_) { + value_ = Json::Value(Json::ValueType::objectValue); + isValid_ = true; + } + Json::Value *exact = nullptr; + Json::Value *nearest = nullptr; + int errCode = MoveToPath(inPath, exact, nearest); + if (errCode != E_OK) { + return errCode; + } + LOGD("nearest type is %d", nearest->type()); + if (isAppend || nearest->type() == Json::ValueType::arrayValue) { + nearest->append(inValue.value_); + } else { + *nearest = inValue.value_; + } + return E_OK; +} + +int JsonObject::InsertField(const FieldPath &inPath, FieldType inType, const FieldValue &inValue, bool isAppend) +{ + if (!InsertFieldCheckParameter(inPath, inType, inValue, maxNestDepth_)) { + return -E_INVALID_ARGS; + } + if (!isValid_) { + // Insert on invalid object never fail after parameter check ok, so here no need concern rollback. + value_ = Json::Value(Json::ValueType::objectValue); + isValid_ = true; + } + Json::Value *exact = nullptr; + Json::Value *nearest = nullptr; + int errCode = MoveToPath(inPath, exact, nearest); + if (errCode != E_OK) { + return errCode; + } + // Here "nearest" points to the JsonValue(null-type now) corresponding to the last field + if (isAppend || nearest->type() == Json::ValueType::arrayValue) { + LeafJsonNodeAppendValue(*nearest, inType, inValue); + } else { + LeafJsonNodeAssignValue(*nearest, inType, inValue); + } + return E_OK; +} + +int JsonObject::DeleteField(const FieldPath &inPath) +{ + if (!isValid_) { + LOGE("[Json][DeleteField] Not Valid Yet."); + return -E_NOT_PERMIT; + } + if (inPath.empty()) { + return -E_INVALID_ARGS; + } + Json::Value *exact = nullptr; + Json::Value *nearest = nullptr; + uint32_t nearDepth = 0; + int errCode = LocateJsonValueByFieldPath(inPath, exact, nearest, nearDepth); + if (errCode != E_OK) { // Path not exist + return -E_JSON_DELETE_PATH_NOT_FOUND; + } + // nearDepth should be equal to inPath.size() - 1, because nearest is at the parent path of inPath + if (nearest == nullptr || nearest->type() != Json::ValueType::objectValue || nearDepth != inPath.size() - 1) { + return -E_INTERNAL_ERROR; // Impossible + } + // Remove member from nearest, ignore returned removed Value, use nearDepth as index pointing to last field of path. + (void)nearest->removeMember(inPath[nearDepth]); + return E_OK; +} + +int JsonObject::GetStringArrayContentByJsonValue(const Json::Value &value, + std::vector &outStringArray) const +{ + if (value.type() != Json::ValueType::arrayValue) { + LOGE("[Json][GetStringArrayByValue] Not an array."); + return -E_NOT_SUPPORT; + } + for (uint32_t index = 0; index < value.size(); index++) { + const Json::Value &eachArrayItem = value[index]; + if (!eachArrayItem.isString()) { + LOGE("[Json][GetStringArrayByValue] Index=%u in Array is not string.", index); + outStringArray.clear(); + return -E_NOT_SUPPORT; + } + outStringArray.push_back(eachArrayItem.asString()); + } + return E_OK; +} + +int JsonObject::GetFieldTypeByJsonValue(const Json::Value &value, FieldType &outType) const +{ + Json::ValueType valueType = value.type(); + switch (valueType) { + case Json::ValueType::nullValue: + outType = FieldType::LEAF_FIELD_NULL; + break; + case Json::ValueType::booleanValue: + outType = FieldType::LEAF_FIELD_BOOL; + break; + // The case intValue and uintValue cover from INT64_MIN to UINT64_MAX. Inside this range, isInt() take range + // from INT32_MIN to INT32_MAX, which should be regard as LEAF_FIELD_INTEGER; isInt64() take range from + // INT64_MIN to INT64_MAX, which should be regard as LEAF_FIELD_LONG if it is not LEAF_FIELD_INTEGER; + // INT64_MAX + 1 to UINT64_MAX will be regard as LEAF_FIELD_DOUBLE, therefore lose its precision when read out + // as double value. + case Json::ValueType::intValue: + case Json::ValueType::uintValue: + if (value.isInt()) { + outType = FieldType::LEAF_FIELD_INTEGER; + } else if (value.isInt64()) { + outType = FieldType::LEAF_FIELD_LONG; + } else { + outType = FieldType::LEAF_FIELD_DOUBLE; // The isDouble() judge is always true in this case. + } + break; + // Integral value beyond range INT64_MIN to UINT64_MAX will be recognized as realValue and lose its precision. + // Value in scientific notation or has decimal point will be recognized as realValue without exception, + // no matter whether the value is large or small, no matter with or without non-zero decimal part. + // In a word, when regard as DOUBLE type, a value can not guarantee its presision + case Json::ValueType::realValue: + // The isDouble() judge is always true in this case. A value exceed double range is not support. + outType = FieldType::LEAF_FIELD_DOUBLE; + if (!std::isfinite(value.asDouble())) { + LOGE("[Json][GetTypeByJson] Infinite double not support."); + return -E_NOT_SUPPORT; + } + break; + case Json::ValueType::stringValue: + outType = FieldType::LEAF_FIELD_STRING; + break; + case Json::ValueType::arrayValue: + outType = FieldType::LEAF_FIELD_ARRAY; + break; + case Json::ValueType::objectValue: + if (value.getMemberNames().empty()) { + outType = FieldType::LEAF_FIELD_OBJECT; + break; + } + outType = FieldType::INTERNAL_FIELD_OBJECT; + break; + default: + LOGE("[Json][GetTypeByJson] no such type."); + return -E_NOT_SUPPORT; + } + return E_OK; +} + +const Json::Value &JsonObject::GetJsonValueByFieldPath(const FieldPath &inPath, int &errCode) const +{ + // Root path always exist + if (inPath.empty()) { + errCode = E_OK; + return value_; + } + const Json::Value *valueNode = &value_; + for (const auto &eachPathSegment : inPath) { + if ((valueNode->type() != Json::ValueType::objectValue) || (!valueNode->isMember(eachPathSegment))) { + // Current JsonValue is not an object, or no such member field + errCode = -E_INVALID_PATH; + return value_; + } + valueNode = &((*valueNode)[eachPathSegment]); + } + errCode = E_OK; + return *valueNode; +} + +int JsonObject::LocateJsonValueByFieldPath(const FieldPath &inPath, Json::Value *&exact, + Json::Value *&nearest, uint32_t &nearDepth) +{ + if (!isValid_) { + return -E_NOT_PERMIT; + } + exact = &value_; + nearest = &value_; + nearDepth = 0; + if (inPath.empty()) { + return E_OK; + } + for (const auto &eachPathSegment : inPath) { + nearest = exact; // Let "nearest" trace "exact" before "exact" go deeper + if (nearest != &value_) { + nearDepth++; // For each "nearest" trace up "exact", increase nearDepth to indicate where it is. + } + if ((exact->type() != Json::ValueType::objectValue) || (!exact->isMember(eachPathSegment))) { + // "exact" is not an object, or no such member field + exact = nullptr; // Set "exact" to nullptr indicate exact path not exist + return -E_NOT_FOUND; + } + exact = &((*exact)[eachPathSegment]); // "exact" go deeper + } + if (exact->type() == Json::ValueType::arrayValue) { + return -E_NOT_FOUND; // could append value if path is an array field. + } + // Here, JsonValue exist at exact path, "nearest" is "exact" parent. + return E_OK; +} + +int JsonObject::GetObjectArrayByFieldPath(const FieldPath &inPath, std::vector &outArray) const +{ + if (!isValid_) { + LOGE("[Json][GetValue] Not Valid Yet."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + const Json::Value &valueNode = GetJsonValueByFieldPath(inPath, errCode); + if (errCode != E_OK) { + LOGE("[Json][GetValue] Get json value failed. %d", errCode); + return errCode; + } + + if (!valueNode.isArray()) { + LOGE("[Json][GetValue] Not Array type."); + return -E_NOT_PERMIT; + } + for (Json::ArrayIndex i = 0; i < valueNode.size(); ++i) { + outArray.emplace_back(JsonObject(valueNode[i])); + } + return E_OK; +} + +int JsonObject::GetObjectByFieldPath(const FieldPath &inPath, JsonObject &outObj) const +{ + if (!isValid_) { + LOGE("[Json][GetValue] Not Valid Yet."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + const Json::Value &valueNode = GetJsonValueByFieldPath(inPath, errCode); + if (errCode != E_OK) { + LOGE("[Json][GetValue] Get json value failed. %d", errCode); + return errCode; + } + + if (!valueNode.isObject()) { + LOGE("[Json][GetValue] Not Object type."); + return -E_NOT_PERMIT; + } + outObj = JsonObject(valueNode); + return E_OK; +} + +int JsonObject::GetStringArrayByFieldPath(const FieldPath &inPath, std::vector &outArray) const +{ + if (!isValid_) { + LOGE("[Json][GetValue] Not Valid Yet."); + return -E_NOT_PERMIT; + } + int errCode = E_OK; + const Json::Value &valueNode = GetJsonValueByFieldPath(inPath, errCode); + if (errCode != E_OK) { + LOGE("[Json][GetValue] Get json value failed. %d", errCode); + return errCode; + } + + return GetStringArrayContentByJsonValue(valueNode, outArray); +} + +#else // OMIT_JSON +uint32_t JsonObject::SetMaxNestDepth(uint32_t nestDepth) +{ + (void)nestDepth; + return 0; +} + +uint32_t JsonObject::CalculateNestDepth(const std::string &inString, int &errCode) +{ + (void)inString; + (void)errCode; + return 0; +} + +uint32_t JsonObject::CalculateNestDepth(const uint8_t *dataBegin, const uint8_t *dataEnd, int &errCode) +{ + (void)dataBegin; + (void)dataEnd; + (void)errCode; + return 0; +} + +JsonObject::JsonObject(const JsonObject &other) = default; + +JsonObject& JsonObject::operator=(const JsonObject &other) = default; + +int JsonObject::Parse(const std::string &inString) +{ + (void)inString; + LOGW("[Json][Parse] Json Omit From Compile."); + return -E_NOT_PERMIT; +} + +int JsonObject::Parse(const std::vector &inData) +{ + (void)inData; + LOGW("[Json][Parse] Json Omit From Compile."); + return -E_NOT_PERMIT; +} + +int JsonObject::Parse(const uint8_t *dataBegin, const uint8_t *dataEnd) +{ + (void)dataBegin; + (void)dataEnd; + LOGW("[Json][Parse] Json Omit From Compile."); + return -E_NOT_PERMIT; +} + +bool JsonObject::IsValid() const +{ + return false; +} + +std::string JsonObject::ToString() const +{ + return std::string(); +} + +bool JsonObject::IsFieldPathExist(const FieldPath &inPath) const +{ + (void)inPath; + return false; +} + +int JsonObject::GetFieldTypeByFieldPath(const FieldPath &inPath, FieldType &outType) const +{ + (void)inPath; + (void)outType; + return -E_NOT_PERMIT; +} + +int JsonObject::GetFieldValueByFieldPath(const FieldPath &inPath, FieldValue &outValue) const +{ + (void)inPath; + (void)outValue; + return -E_NOT_PERMIT; +} + +int JsonObject::GetSubFieldPath(const FieldPath &inPath, std::set &outSubPath) const +{ + (void)inPath; + (void)outSubPath; + return -E_NOT_PERMIT; +} + +int JsonObject::GetSubFieldPath(const std::set &inPath, std::set &outSubPath) const +{ + (void)inPath; + (void)outSubPath; + return -E_NOT_PERMIT; +} + +int JsonObject::GetSubFieldPathAndType(const FieldPath &inPath, std::map &outSubPathType) const +{ + (void)inPath; + (void)outSubPathType; + return -E_NOT_PERMIT; +} + +int JsonObject::GetSubFieldPathAndType(const std::set &inPath, + std::map &outSubPathType) const +{ + (void)inPath; + (void)outSubPathType; + return -E_NOT_PERMIT; +} + +int JsonObject::GetArraySize(const FieldPath &inPath, uint32_t &outSize) const +{ + (void)inPath; + (void)outSize; + return -E_NOT_PERMIT; +} + +int JsonObject::GetArrayContentOfStringOrStringArray(const FieldPath &inPath, + std::vector> &outContent) const +{ + (void)inPath; + (void)outContent; + return -E_NOT_PERMIT; +} + +int JsonObject::InsertField(const FieldPath &inPath, FieldType inType, const FieldValue &inValue) +{ + (void)inPath; + (void)inType; + (void)inValue; + return -E_NOT_PERMIT; +} + +int JsonObject::InsertField(const FieldPath &inPath, const JsonObject &inValue, bool isAppend = false) +{ + (void)inPath; + (void)inValue; + (void)isAppend; + return -E_NOT_PERMIT; +} + +int JsonObject::DeleteField(const FieldPath &inPath) +{ + (void)inPath; + return -E_NOT_PERMIT; +} + +int JsonObject::GetArrayValueByFieldPath(const FieldPath &inPath, JsonObject &outArray) const +{ + (void)inPath; + (void)outArray; + return -E_NOT_PERMIT; +} +#endif // OMIT_JSON +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/lock_status_observer.cpp b/mock/distributeddb/common/src/lock_status_observer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c26da197bca7ba77a2b48b3ad8e2a0d42a41d75c --- /dev/null +++ b/mock/distributeddb/common/src/lock_status_observer.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "lock_status_observer.h" + +#include "log_print.h" + +namespace DistributedDB { +LockStatusObserver::LockStatusObserver() + : lockStatusChangedNotifier_(nullptr), + isStarted_(false) +{} + +LockStatusObserver::~LockStatusObserver() +{ + Stop(); +} + +int LockStatusObserver::Start() +{ + if (isStarted_) { + return E_OK; + } + + int errCode = PrepareNotifierChain(); + if (errCode != E_OK) { + LOGE("PrepareNotifierChain failed, errorCode = %d", errCode); + return errCode; + } + isStarted_ = true; + return E_OK; +} + +bool LockStatusObserver::IsStarted() const +{ + return isStarted_; +} + +void LockStatusObserver::Stop() +{ + if (!isStarted_) { + return; + } + + lockStatusChangedNotifier_->UnRegisterEventType(LOCK_STATUS_CHANGE_EVENT); + RefObject::KillAndDecObjRef(lockStatusChangedNotifier_); + lockStatusChangedNotifier_ = nullptr; + isStarted_ = false; +} + +int LockStatusObserver::PrepareNotifierChain() +{ + if (lockStatusChangedNotifier_ != nullptr) { + return E_OK; + } + + lockStatusChangedNotifier_ = new (std::nothrow) NotificationChain(); + if (lockStatusChangedNotifier_ == nullptr) { + LOGE("lockStatusChangedNotifier_ is nullptr"); + return -E_OUT_OF_MEMORY; + } + + int errCode = lockStatusChangedNotifier_->RegisterEventType(LOCK_STATUS_CHANGE_EVENT); + if (errCode != E_OK) { + LOGE("RegisterEventType failed, errCode = %d", errCode); + RefObject::KillAndDecObjRef(lockStatusChangedNotifier_); + lockStatusChangedNotifier_ = nullptr; + } + return errCode; +} + +NotificationChain::Listener *LockStatusObserver::RegisterLockStatusChangedLister(const LockStatusNotifier &action, + int &errCode) const +{ + if (lockStatusChangedNotifier_ == nullptr) { + LOGE("lockStatusChangedNotifier_ is nullptr"); + errCode = -E_NOT_INIT; + return nullptr; + } + + if (!action) { + LOGE("action is nullptr"); + errCode = -E_INVALID_ARGS; + return nullptr; + } + return lockStatusChangedNotifier_->RegisterListener(LOCK_STATUS_CHANGE_EVENT, action, nullptr, errCode); +} + +void LockStatusObserver::OnStatusChange(bool isLocked) const +{ + if (lockStatusChangedNotifier_ == nullptr) { + LOGE("lockStatusChangedNotifier_ is nullptr"); + return; + } + lockStatusChangedNotifier_->NotifyEvent(LOCK_STATUS_CHANGE_EVENT, &isLocked); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/lock_status_observer.h b/mock/distributeddb/common/src/lock_status_observer.h new file mode 100644 index 0000000000000000000000000000000000000000..ae221b5faa6893574caaee70ef3a920152c67014 --- /dev/null +++ b/mock/distributeddb/common/src/lock_status_observer.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOCK_STATUS_OBSERVER_H +#define LOCK_STATUS_OBSERVER_H + +#include "notification_chain.h" +#include "runtime_context.h" + +namespace DistributedDB { +class LockStatusObserver final { +public: + LockStatusObserver(); + ~LockStatusObserver(); + DISABLE_COPY_ASSIGN_MOVE(LockStatusObserver); + int Start(); + void Stop(); + void OnStatusChange(bool isLocked) const; + bool IsStarted() const; + NotificationChain::Listener *RegisterLockStatusChangedLister(const LockStatusNotifier &action, int &errCode) const; + +private: + static const EventType LOCK_STATUS_CHANGE_EVENT = 2; + int PrepareNotifierChain(); + NotificationChain *lockStatusChangedNotifier_; + bool isStarted_; +}; +} + +#endif diff --git a/mock/distributeddb/common/src/log_print.cpp b/mock/distributeddb/common/src/log_print.cpp new file mode 100644 index 0000000000000000000000000000000000000000..43132b5d90bd9ef4a54cdb3c1d4cc39b951d0099 --- /dev/null +++ b/mock/distributeddb/common/src/log_print.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "log_print.h" + +#include +#include +#include +#include + +#include "securec.h" +#include "platform_specific.h" +#include "hilog/log.h" + +namespace DistributedDB { +Logger *Logger::logHandler = nullptr; +const std::string Logger::PRIVATE_TAG = "s{private}"; + +class HiLogger : public Logger { +public: + void Print(Level level, const std::string &tag, const std::string &msg) override + { + if (msg.empty()) { + return; + } + const std::string format = "%{public}s"; + OHOS::HiviewDFX::HiLogLabel label = { LOG_CORE, 0xD001630, tag.c_str() }; // log module id. + switch (level) { + case Level::LEVEL_DEBUG: + (void)OHOS::HiviewDFX::HiLog::Debug(label, format.c_str(), msg.c_str()); + break; + case Level::LEVEL_INFO: + (void)OHOS::HiviewDFX::HiLog::Info(label, format.c_str(), msg.c_str()); + break; + case Level::LEVEL_WARN: + (void)OHOS::HiviewDFX::HiLog::Warn(label, format.c_str(), msg.c_str()); + break; + case Level::LEVEL_ERROR: + (void)OHOS::HiviewDFX::HiLog::Error(label, format.c_str(), msg.c_str()); + break; + case Level::LEVEL_FATAL: + (void)OHOS::HiviewDFX::HiLog::Fatal(label, format.c_str(), msg.c_str()); + break; + default: + break; + } + } +}; + +Logger *Logger::GetInstance() +{ + static std::mutex logInstanceLock; + static std::atomic logInstance = nullptr; + // For Double-Checked Locking, we need check logInstance twice + if (logInstance == nullptr) { + std::lock_guard lock(logInstanceLock); + if (logInstance == nullptr) { + // Here, we new logInstance to print log, if new failed, we can do nothing. + logInstance = new (std::nothrow) HiLogger; + } + } + return logInstance; +} + +void Logger::RegisterLogger(Logger *logger) +{ + static std::mutex logHandlerLock; + if (logger == nullptr) { + return; + } + if (logHandler == nullptr) { + std::lock_guard lock(logHandlerLock); + if (logHandler == nullptr) { + logHandler = logger; + } + } +} + +void Logger::Log(Level level, const std::string &tag, const char *func, int line, const char *format, ...) +{ + (void)func; + (void)line; + if (format == nullptr) { + return; + } + + static const int maxLogLength = 1024; + va_list argList; + va_start(argList, format); + char logBuff[maxLogLength]; + std::string msg; + std::string formatTemp; + PreparePrivateLog(format, formatTemp); + int bytes = vsnprintf_s(logBuff, maxLogLength, maxLogLength - 1, formatTemp.c_str(), argList); + if (bytes < 0) { + msg = "log buffer overflow!"; + } else { + msg = logBuff; + } + va_end(argList); + if (logHandler != nullptr) { + logHandler->Print(level, tag, msg); + return; + } + + Logger::RegisterLogger(Logger::GetInstance()); + if (logHandler != nullptr) { + logHandler->Print(level, tag, msg); + } +} + +void Logger::PreparePrivateLog(const char *format, std::string &outStrFormat) +{ + outStrFormat = format; + std::string::size_type pos = outStrFormat.find(PRIVATE_TAG); + if (pos != std::string::npos) { + outStrFormat.replace(pos, PRIVATE_TAG.size(), ".3s"); + } +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/notification_chain.cpp b/mock/distributeddb/common/src/notification_chain.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b7daea5f425dff783deaf42f27804722629680d --- /dev/null +++ b/mock/distributeddb/common/src/notification_chain.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "notification_chain.h" + +#include +#include + +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +NotificationChain::Listener *NotificationChain::RegisterListener( + EventType type, const Listener::OnEvent &onEvent, const Listener::OnFinalize &onFinalize, int &errCode) +{ + errCode = E_OK; + if (!onEvent) { + LOGE("[NotificationChain] Register listener failed, 'onEvent()' is null!"); + errCode = -E_INVALID_ARGS; + return nullptr; + } + + NotificationChain::ListenerChain *listenerChain = FindAndGetListenerChainLocked(type); + if (listenerChain == nullptr) { + LOGE("[NotificationChain] Register listener failed, no event type %u found!", type); + errCode = -E_NOT_REGISTER; + return nullptr; + } + + NotificationChain::Listener *listener = new (std::nothrow) + NotificationChain::Listener(onEvent, onFinalize); + if (listener == nullptr) { + listenerChain->DecObjRef(listenerChain); + listenerChain = nullptr; + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + + errCode = listenerChain->RegisterListener(listener); + if (errCode != E_OK) { + LOGE("[NotificationChain] Register listener failed, event type %u has been unregistered!", type); + listener->DecObjRef(listener); + listener = nullptr; + listenerChain->DecObjRef(listenerChain); + listenerChain = nullptr; + return nullptr; + } + + listenerChain->DecObjRef(listenerChain); + listenerChain = nullptr; + return listener; +} + +int NotificationChain::RegisterEventType(EventType type) +{ + AutoLock lockGuard(this); + if (IsKilled()) { + LOGI("Register event failed, the notification chain has been killed!"); + return -E_STALE; + } + + ListenerChain *listenerChain = FindListenerChain(type); + if (listenerChain != nullptr) { + LOGE("[NotificationChain] Register event failed, event type %u has been registered!", type); + return -E_ALREADY_REGISTER; + } + + listenerChain = new (std::nothrow) ListenerChain(); + if (listenerChain == nullptr) { + LOGE("[NotificationChain] Register event failed, OOM!"); + return -E_OUT_OF_MEMORY; + } + + listenerChain->OnKill([listenerChain] { + listenerChain->ClearListeners(); + }); + eventChains_.insert(std::pair(type, listenerChain)); + IncObjRef(this); + return E_OK; +} + +int NotificationChain::UnRegisterEventType(EventType type) +{ + NotificationChain::ListenerChain *listenerChain = nullptr; + { + AutoLock lockGuard(this); + listenerChain = FindListenerChain(type); + if (listenerChain == nullptr) { + LOGE("[NotificationChain] UnRegister event failed, event %u is not registered!", type); + return -E_NOT_FOUND; + } + eventChains_.erase(type); + } + + listenerChain->KillAndDecObjRef(listenerChain); + listenerChain = nullptr; + DecObjRef(this); + return E_OK; +} + +void NotificationChain::NotifyEvent(EventType type, void *arg) +{ + NotificationChain::ListenerChain *listenerChain = FindAndGetListenerChainLocked(type); + if (listenerChain == nullptr) { + return; + } + listenerChain->NotifyListeners(arg); + listenerChain->DecObjRef(listenerChain); + listenerChain = nullptr; +} + +NotificationChain::ListenerChain::ListenerChain() {} + +NotificationChain::ListenerChain::~ListenerChain() {} + +NotificationChain::ListenerChain *NotificationChain::FindAndGetListenerChainLocked(EventType type) +{ + AutoLock lockGuard(this); + ListenerChain *listenerChain = FindListenerChain(type); + if (listenerChain == nullptr) { + return nullptr; + } + listenerChain->IncObjRef(listenerChain); + return listenerChain; +} + +NotificationChain::ListenerChain *NotificationChain::FindListenerChain(EventType type) const +{ + auto iter = eventChains_.find(type); + if (iter != eventChains_.end()) { + return iter->second; + } + return nullptr; +} + +int NotificationChain::ListenerChain::RegisterListener(Listener *listener) +{ + AutoLock lockGuard(this); + if (IsKilled()) { + return -E_STALE; + } + if (listenerSet_.find(listener) != listenerSet_.end()) { + return -E_ALREADY_REGISTER; + } + listenerSet_.insert(listener); + listener->SetOwner(this); + return E_OK; +} + +int NotificationChain::ListenerChain::UnRegisterListener(Listener *listener, bool wait) +{ + if (listener == nullptr) { + return -E_INVALID_ARGS; + } + + { + AutoLock lockGuard(this); + auto result = listenerSet_.find(listener); + if (result != listenerSet_.end()) { + if (wait) { + listener->OnKill([listener]() { + listener->KillWait(); + }); + } + listenerSet_.erase(result); + } + } + + listener->KillAndDecObjRef(listener); + listener = nullptr; + return E_OK; +} + +void NotificationChain::ListenerChain::BackupListenerSet(std::set &backupSet) const +{ + for (auto listener : listenerSet_) { + listener->IncObjRef(listener); + backupSet.insert(listener); + } +} + +void NotificationChain::ListenerChain::NotifyListeners(void *arg) +{ + std::set tmpSet; + { + AutoLock lockGuard(this); + if (IsKilled()) { + return; + } + BackupListenerSet(tmpSet); + } + + for (auto listener : tmpSet) { + if (listener != nullptr) { + listener->NotifyListener(arg); + listener->DecObjRef(listener); + listener = nullptr; + } + } +} + +void NotificationChain::ListenerChain::ClearListeners() +{ + std::set tmpSet; + BackupListenerSet(tmpSet); + listenerSet_.clear(); + // Enter this function with lock held(OnKill() is invoked with object lock held), so drop it. + UnlockObj(); + + for (auto listener : tmpSet) { + // Drop the ref 1 which increased in 'BackupListenerSet()', + // the origal 1 will be dropped when user call listener->Drop(); + listener->KillAndDecObjRef(listener); + listener = nullptr; + } + + // Lock it again before leaving. + LockObj(); +} + +void NotificationChain::Listener::NotifyListener(void *arg) +{ + if (onEvent_ && !IsKilled()) { + if (EnterEventAction()) { + onEvent_(arg); + LeaveEventAction(); + } + } +} + +void NotificationChain::Listener::Finalize() const +{ + if (onFinalize_) { + onFinalize_(); + } +} + +bool NotificationChain::Listener::EnterEventAction() +{ + AutoLock lockGuard(this); + if (IsKilled()) { + return false; + } + // We never call onEvent() of the same listener in parallel with 2 or more threads. + eventRunningThread_ = std::this_thread::get_id(); + return true; +} + +void NotificationChain::Listener::LeaveEventAction() +{ + AutoLock lockGuard(this); + eventRunningThread_ = std::thread::id(); + safeKill_.notify_one(); +} + +void NotificationChain::Listener::KillWait() +{ + // We entered with object lock held. + if ((eventRunningThread_ == std::thread::id()) || + (eventRunningThread_ == std::this_thread::get_id())) { + return; + } + + LOGW("[NotificationChain] Try to kill an active event listener, now wait."); + bool noDeadLock = WaitLockedUntil(safeKill_, [this]() { + if (eventRunningThread_ == std::thread::id()) { + return true; + } + return false; + }, KILL_WAIT_SECONDS); + if (!noDeadLock) { + LOGE("[NotificationChain] Dead lock maybe happen, we stop waiting the listener."); + } else { + LOGW("[NotificationChain] Wait the active event listener ok."); + } +} + +void NotificationChain::Listener::SetOwner(ListenerChain *listenerChain) +{ + if (listenerChain_ != nullptr) { + listenerChain_->DecObjRef(listenerChain_); + } + listenerChain_ = listenerChain; + if (listenerChain_ != nullptr) { + listenerChain_->IncObjRef(listenerChain_); + } +} + +int NotificationChain::Listener::Drop(bool wait) +{ + if (listenerChain_ == nullptr) { + LOGE("[NotificationChain] Drop listener failed, lost the chain!"); + return -E_INTERNAL_ERROR; + } + return listenerChain_->UnRegisterListener(this, wait); +} + +NotificationChain::Listener::Listener(const OnEvent &onEvent, const OnFinalize &onFinalize) + : onEvent_(onEvent), + onFinalize_(onFinalize), + listenerChain_(nullptr) +{ + OnLastRef([this]() { + this->Finalize(); + }); +} + +NotificationChain::Listener::~Listener() +{ + SetOwner(nullptr); +} + +NotificationChain::~NotificationChain() +{ + for (auto &iter : eventChains_) { + iter.second->KillAndDecObjRef(iter.second); + iter.second = nullptr; + } + eventChains_.clear(); +} + +DEFINE_OBJECT_TAG_FACILITIES(NotificationChain) +DEFINE_OBJECT_TAG_FACILITIES(NotificationChain::Listener) +DEFINE_OBJECT_TAG_FACILITIES(NotificationChain::ListenerChain) +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/common/src/param_check_utils.cpp b/mock/distributeddb/common/src/param_check_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e7a94aac223bf15250f33f7f8bf8a51acf1d9e19 --- /dev/null +++ b/mock/distributeddb/common/src/param_check_utils.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "param_check_utils.h" + +#include "db_errno.h" +#include "platform_specific.h" +#include "log_print.h" + +namespace DistributedDB { +bool ParamCheckUtils::CheckDataDir(const std::string &dataDir, std::string &canonicalDir) +{ + if (dataDir.empty() || (dataDir.length() > DBConstant::MAX_DATA_DIR_LENGTH)) { + LOGE("Invalid data directory[%zu]", dataDir.length()); + return false; + } + + if (OS::GetRealPath(dataDir, canonicalDir) != E_OK) { + return false; + } + // After normalizing the path, determine whether the path is a legal path considered by the program. + // There has been guaranteed by the upper layer, So there is no need trustlist set here. + return true; +} + +bool ParamCheckUtils::IsStoreIdSafe(const std::string &storeId) +{ + if (storeId.empty() || (storeId.length() > DBConstant::MAX_STORE_ID_LENGTH)) { + LOGE("Invalid store id[%zu]", storeId.length()); + return false; + } + + auto iter = std::find_if_not(storeId.begin(), storeId.end(), + [](char value) { return (std::isalnum(value) || value == '_'); }); + if (iter != storeId.end()) { + LOGE("Invalid store id format"); + return false; + } + return true; +} + +bool ParamCheckUtils::CheckStoreParameter(const std::string &storeId, const std::string &appId, + const std::string &userId, bool isIgnoreUserIdCheck) +{ + if (!IsStoreIdSafe(storeId)) { + return false; + } + if (!isIgnoreUserIdCheck) { + if (userId.empty() || userId.length() > DBConstant::MAX_USER_ID_LENGTH) { + LOGE("Invalid user info[%zu][%zu]", userId.length(), appId.length()); + return false; + } + if (userId.find(DBConstant::ID_CONNECTOR) != std::string::npos) { + LOGE("Invalid userId character in the store para info."); + return false; + } + } + if (appId.empty() || appId.length() > DBConstant::MAX_APP_ID_LENGTH) { + LOGE("Invalid app info[%zu][%zu]", userId.length(), appId.length()); + return false; + } + + if ((appId.find(DBConstant::ID_CONNECTOR) != std::string::npos) || + (storeId.find(DBConstant::ID_CONNECTOR) != std::string::npos)) { + LOGE("Invalid character in the store para info."); + return false; + } + return true; +} + +bool ParamCheckUtils::CheckEncryptedParameter(CipherType cipher, const CipherPassword &passwd) +{ + if (cipher != CipherType::DEFAULT && cipher != CipherType::AES_256_GCM) { + LOGE("Invalid cipher type!"); + return false; + } + + return (passwd.GetSize() != 0); +} + +bool ParamCheckUtils::CheckConflictNotifierType(int conflictType) +{ + if (conflictType <= 0) { + return false; + } + // Divide the type into different types. + if (conflictType >= CONFLICT_NATIVE_ALL) { + conflictType -= CONFLICT_NATIVE_ALL; + } + if (conflictType >= CONFLICT_FOREIGN_KEY_ORIG) { + conflictType -= CONFLICT_FOREIGN_KEY_ORIG; + } + if (conflictType >= CONFLICT_FOREIGN_KEY_ONLY) { + conflictType -= CONFLICT_FOREIGN_KEY_ONLY; + } + return (conflictType == 0); +} + +bool ParamCheckUtils::CheckSecOption(const SecurityOption &secOption) +{ + if (secOption.securityLabel > S4 || secOption.securityLabel < NOT_SET) { + LOGE("[DBCommon] SecurityLabel is invalid, label is [%d].", secOption.securityLabel); + return false; + } + if (secOption.securityFlag != 0) { + if ((secOption.securityLabel != S3 && secOption.securityLabel != S4) || secOption.securityFlag != SECE) { + LOGE("[DBCommon] SecurityFlag is invalid."); + return false; + } + } + return true; +} + +bool ParamCheckUtils::CheckObserver(const Key &key, unsigned int mode) +{ + if (key.size() > DBConstant::MAX_KEY_SIZE) { + return false; + } + + if (mode > OBSERVER_CHANGES_LOCAL_ONLY || mode < OBSERVER_CHANGES_NATIVE) { + return false; + } + return true; +} + +bool ParamCheckUtils::IsS3SECEOpt(const SecurityOption &secOpt) +{ + SecurityOption S3SeceOpt = {SecurityLabel::S3, SecurityFlag::SECE}; + return (secOpt == S3SeceOpt); +} + +int ParamCheckUtils::CheckAndTransferAutoLaunchParam(const AutoLaunchParam ¶m, bool checkDir, + SchemaObject &schemaObject, std::string &canonicalDir) +{ + if ((param.option.notifier && !ParamCheckUtils::CheckConflictNotifierType(param.option.conflictType)) || + (!param.option.notifier && param.option.conflictType != 0)) { + LOGE("[AutoLaunch] CheckConflictNotifierType is invalid."); + return -E_INVALID_ARGS; + } + if (!ParamCheckUtils::CheckStoreParameter(param.storeId, param.appId, param.userId)) { + LOGE("[AutoLaunch] CheckStoreParameter is invalid."); + return -E_INVALID_ARGS; + } + + const AutoLaunchOption &option = param.option; + if (!ParamCheckUtils::CheckSecOption(option.secOption)) { + LOGE("[AutoLaunch] CheckSecOption is invalid."); + return -E_INVALID_ARGS; + } + + if (option.isEncryptedDb) { + if (!ParamCheckUtils::CheckEncryptedParameter(option.cipher, option.passwd)) { + LOGE("[AutoLaunch] CheckEncryptedParameter is invalid."); + return -E_INVALID_ARGS; + } + } + + if (!param.option.schema.empty()) { + schemaObject.ParseFromSchemaString(param.option.schema); + if (!schemaObject.IsSchemaValid()) { + LOGE("[AutoLaunch] ParseFromSchemaString is invalid."); + return -E_INVALID_SCHEMA; + } + } + + if (!checkDir) { + canonicalDir = param.option.dataDir; + return E_OK; + } + + if (!ParamCheckUtils::CheckDataDir(param.option.dataDir, canonicalDir)) { + LOGE("[AutoLaunch] CheckDataDir is invalid."); + return -E_INVALID_ARGS; + } + return E_OK; +} + +uint8_t ParamCheckUtils::GetValidCompressionRate(uint8_t compressionRate) +{ + // Valid when between 1 and 100. When compressionRate is invalid, change it to default rate. + if (compressionRate < 1 || compressionRate > DBConstant::DEFAULT_COMPTRESS_RATE) { + LOGD("Invalid compression rate:%u.", compressionRate); + compressionRate = DBConstant::DEFAULT_COMPTRESS_RATE; + } + return compressionRate; +} + +bool ParamCheckUtils::CheckRelationalTableName(const std::string &tableName) +{ + auto iter = std::find_if_not(tableName.begin(), tableName.end(), [](char c) { + return (std::isalnum(c) || c == '_'); + }); + if (iter != tableName.end()) { + return false; + } + return tableName.compare(0, DBConstant::SYSTEM_TABLE_PREFIX.size(), DBConstant::SYSTEM_TABLE_PREFIX) != 0; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/common/src/parcel.cpp b/mock/distributeddb/common/src/parcel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..372a9bfc66ffdf914b708338762688ce4fbd0084 --- /dev/null +++ b/mock/distributeddb/common/src/parcel.cpp @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "parcel.h" + +#include + +#include "endian_convert.h" +#include "securec.h" +#include "macro_utils.h" +#include "log_print.h" +#include "db_errno.h" +#include "db_constant.h" + +namespace DistributedDB { +Parcel::Parcel(uint8_t *inBuf, uint32_t len) + : buf_(inBuf), + bufPtr_(inBuf), + totalLen_(len) +{ + if (inBuf == nullptr || len == 0) { + isError_ = true; + } +} + +Parcel::~Parcel() +{ + buf_ = nullptr; + bufPtr_ = nullptr; +} + +bool Parcel::IsError() const +{ + return isError_; +} + +int Parcel::WriteBool(bool data) +{ + uint8_t value = data ? 1 : 0; + return WriteUInt8(value); +} + +uint32_t Parcel::ReadBool(bool &val) +{ + uint8_t intVal = 0; + uint32_t len = ReadUInt8(intVal); + val = intVal == 1 ? true : false; + return len; +} + +int Parcel::WriteInt(int32_t data) +{ + return WriteInteger(data); +} + +uint32_t Parcel::ReadInt(int32_t &val) +{ + return ReadInteger(val); +} + +int Parcel::WriteUInt8(uint8_t data) +{ + return WriteInteger(data); +} + +uint32_t Parcel::ReadUInt8(uint8_t &val) +{ + return ReadInteger(val); +} + +int Parcel::WriteDouble(double data) +{ + double inData = HostToNet(data); + if (isError_ || parcelLen_ + sizeof(double) > totalLen_) { + isError_ = true; + return -E_PARSE_FAIL; + } + errno_t errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_, &inData, sizeof(double)); + if (errCode != EOK) { + isError_ = true; + return -E_SECUREC_ERROR; + } + bufPtr_ += sizeof(double); + parcelLen_ += sizeof(double); + return E_OK; +} + +uint32_t Parcel::ReadDouble(double &val) +{ + if (isError_ || bufPtr_ == nullptr || parcelLen_ + sizeof(double) > totalLen_) { + isError_ = true; + return 0; + } + val = *(reinterpret_cast(bufPtr_)); + bufPtr_ += sizeof(double); + parcelLen_ += sizeof(double); + val = NetToHost(val); + return sizeof(double); +} + +int Parcel::WriteInt64(int64_t data) +{ + return WriteInteger(data); +} + +uint32_t Parcel::ReadInt64(int64_t &val) +{ + return ReadInteger(val); +} + +int Parcel::WriteUInt32(uint32_t data) +{ + return WriteInteger(data); +} + +uint32_t Parcel::ReadUInt32(uint32_t &val) +{ + return ReadInteger(val); +} + +int Parcel::WriteUInt64(uint64_t data) +{ + return WriteInteger(data); +} + +uint32_t Parcel::ReadUInt64(uint64_t &val) +{ + return ReadInteger(val); +} + +int Parcel::WriteVectorChar(const std::vector& data) +{ + return WriteVector(data); +} + +uint32_t Parcel::ReadVectorChar(std::vector& val) +{ + return ReadVector(val); +} + +int Parcel::WriteString(const std::string &inVal) +{ + if (inVal.size() > INT32_MAX) { + LOGE("[WriteString] Invalid string, size:%zu.", inVal.size()); + isError_ = true; + return -E_PARSE_FAIL; + } + if (IsError()) { + return -E_PARSE_FAIL; + } + uint32_t len = inVal.size(); + uint64_t stepLen = sizeof(uint32_t) + static_cast(inVal.size()); + len = HostToNet(len); + if (stepLen > INT32_MAX || parcelLen_ + BYTE_8_ALIGN(stepLen) > totalLen_) { + LOGE("[WriteString] stepLen:%" PRIu64 ", totalLen:%" PRIu64 ", parcelLen:%" PRIu64, stepLen, totalLen_, + parcelLen_); + isError_ = true; + return -E_PARSE_FAIL; + } + errno_t errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_, &len, sizeof(uint32_t)); + if (errCode != EOK) { + LOGE("[WriteString] bufPtr:%d, totalLen:%" PRIu64 ", parcelLen:%" PRIu64, bufPtr_ != nullptr, totalLen_, + parcelLen_); + isError_ = true; + return -E_SECUREC_ERROR; + } + bufPtr_ += sizeof(uint32_t); + if (inVal.size() == 0) { + bufPtr_ += BYTE_8_ALIGN(stepLen) - stepLen; + parcelLen_ += BYTE_8_ALIGN(stepLen); + return errCode; + } + errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_ - sizeof(uint32_t), inVal.c_str(), inVal.size()); + if (errCode != EOK) { + LOGE("[WriteString] totalLen:%" PRIu64 ", parcelLen:%" PRIu64 ", inVal.size:%zu.", + totalLen_, parcelLen_, inVal.size()); + isError_ = true; + return -E_SECUREC_ERROR; + } + bufPtr_ += inVal.size(); + bufPtr_ += BYTE_8_ALIGN(stepLen) - stepLen; + parcelLen_ += BYTE_8_ALIGN(stepLen); + return E_OK; +} + +uint32_t Parcel::ReadString(std::string &outVal) +{ + if (IsError()) { + return 0; + } + if (bufPtr_ == nullptr || parcelLen_ + sizeof(uint32_t) > totalLen_) { + LOGE("[ReadString] bufPtr:%d, totalLen:%" PRIu64 ", parcelLen:%" PRIu64, bufPtr_ != nullptr, totalLen_, + parcelLen_); + isError_ = true; + return 0; + } + uint32_t len = *(reinterpret_cast(bufPtr_)); + len = NetToHost(len); + uint64_t stepLen = static_cast(len) + sizeof(uint32_t); + if (stepLen > INT32_MAX || parcelLen_ + BYTE_8_ALIGN(stepLen) > totalLen_) { + LOGE("[ReadString] stepLen:%" PRIu64 ", totalLen:%" PRIu64 ", parcelLen:%" PRIu64, stepLen, totalLen_, + parcelLen_); + isError_ = true; + return 0; + } + outVal.resize(len); + outVal.assign(bufPtr_ + sizeof(uint32_t), bufPtr_ + stepLen); + bufPtr_ += BYTE_8_ALIGN(stepLen); + parcelLen_ += BYTE_8_ALIGN(stepLen); + stepLen = BYTE_8_ALIGN(stepLen); + return static_cast(stepLen); +} + +bool Parcel::IsContinueRead() +{ + return (parcelLen_ < totalLen_); +} + +#ifndef OMIT_MULTI_VER +int Parcel::WriteMultiVerCommit(const MultiVerCommitNode &commit) +{ + int errCode = WriteVectorChar(commit.commitId); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write commitId err!"); + isError_ = true; + return errCode; + } + errCode = WriteVectorChar(commit.leftParent); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write leftParent err!"); + return errCode; + } + errCode = WriteVectorChar(commit.rightParent); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write rightParent err!"); + return errCode; + } + errCode = WriteUInt64(commit.timestamp); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write timestamp err!"); + return errCode; + } + errCode = WriteUInt64(commit.version); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write version err!"); + return errCode; + } + errCode = WriteUInt64(commit.isLocal); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write isLocal err!"); + return errCode; + } + errCode = WriteString(commit.deviceInfo); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write deviceInfo err!"); + } + return errCode; +} + +uint32_t Parcel::ReadMultiVerCommit(MultiVerCommitNode &commit) +{ + if (isError_) { + return 0; + } + uint64_t len = ReadVectorChar(commit.commitId); + len += ReadVectorChar(commit.leftParent); + len += ReadVectorChar(commit.rightParent); + len += ReadUInt64(commit.timestamp); + len += ReadUInt64(commit.version); + len += ReadUInt64(commit.isLocal); + len += ReadString(commit.deviceInfo); + if (isError_ || len > INT32_MAX) { + isError_ = true; + return 0; + } + return static_cast(len); +} +int Parcel::WriteMultiVerCommits(const std::vector &commits) +{ + uint64_t len = commits.size(); + int errCode = WriteUInt64(len); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write len err!"); + isError_ = true; + return errCode; + } + for (auto &iter : commits) { + errCode = WriteVectorChar(iter.commitId); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write commitId err!"); + return errCode; + } + errCode = WriteVectorChar(iter.leftParent); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write leftParent err!"); + return errCode; + } + errCode = WriteVectorChar(iter.rightParent); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write rightParent err!"); + return errCode; + } + errCode = WriteUInt64(iter.timestamp); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write timestamp err!"); + return errCode; + } + errCode = WriteUInt64(iter.version); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write version err!"); + return errCode; + } + errCode = WriteUInt64(iter.isLocal); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write isLocal err!"); + return errCode; + } + errCode = WriteString(iter.deviceInfo); + if (errCode != E_OK) { + LOGE("Parcel::WriteMultiVerCommit write deviceInfo err!"); + return errCode; + } + EightByteAlign(); + } + EightByteAlign(); + return errCode; +} + +uint32_t Parcel::ReadMultiVerCommits(std::vector &commits) +{ + uint64_t len = 0; + uint64_t size = 0; + len += ReadUInt64(size); + if (isError_) { + return 0; + } + if (size > DBConstant::MAX_COMMIT_SIZE) { + isError_ = true; + LOGE("Parcel::ReadMultiVerCommits commits size too large: %" PRIu64, size); + return 0; + } + for (uint64_t i = 0; i < size; i++) { + MultiVerCommitNode commit; + len += ReadVectorChar(commit.commitId); + len += ReadVectorChar(commit.leftParent); + len += ReadVectorChar(commit.rightParent); + len += ReadUInt64(commit.timestamp); + len += ReadUInt64(commit.version); + len += ReadUInt64(commit.isLocal); + len += ReadString(commit.deviceInfo); + commits.push_back(commit); + EightByteAlign(); + len = BYTE_8_ALIGN(len); + if (isError_ || len > INT32_MAX) { + isError_ = true; + return 0; + } + } + len = BYTE_8_ALIGN(len); + + return static_cast(len); +} +#endif + +int Parcel::WriteBlob(const char *buffer, uint32_t bufLen) +{ + if (buffer == nullptr) { + LOGE("[WriteBlob] Invalid buffer."); + isError_ = true; + return -E_INVALID_ARGS; + } + if (IsError()) { + return -E_PARSE_FAIL; + } + if (parcelLen_ + bufLen > totalLen_) { + LOGE("[WriteBlob] bufLen:%" PRIu32 ", totalLen:%" PRIu64 ", parcelLen:%" PRIu64, bufLen, totalLen_, parcelLen_); + isError_ = true; + return -E_PARSE_FAIL; + } + uint32_t leftLen = static_cast(totalLen_ - parcelLen_); + int errCode = memcpy_s(bufPtr_, leftLen, buffer, bufLen); + if (errCode != EOK) { + LOGE("[WriteBlob] leftLen:%u, bufLen:%u", leftLen, bufLen); + isError_ = true; + return -E_SECUREC_ERROR; + } + uint32_t length = (BYTE_8_ALIGN(bufLen) < leftLen) ? BYTE_8_ALIGN(bufLen) : leftLen; + bufPtr_ += length; + parcelLen_ += length; + return E_OK; +} +uint32_t Parcel::ReadBlob(char *buffer, uint32_t bufLen) +{ + if (buffer == nullptr) { + LOGE("[ReadBlob] Invalid buffer."); + isError_ = true; + return 0; + } + if (IsError()) { + return 0; + } + uint32_t leftLen = static_cast(totalLen_ - parcelLen_); + if (parcelLen_ + bufLen > totalLen_) { + LOGE("[ReadBlob] bufLen:%" PRIu32 ", totalLen:%" PRIu64 ", parcelLen:%" PRIu64, bufLen, totalLen_, parcelLen_); + isError_ = true; + return 0; + } + int errCode = memcpy_s(buffer, bufLen, bufPtr_, bufLen); + if (errCode != EOK) { + LOGE("[ReadBlob] bufLen:%u", bufLen); + isError_ = true; + return 0; + } + uint32_t length = (BYTE_8_ALIGN(bufLen) < leftLen) ? BYTE_8_ALIGN(bufLen) : leftLen; + bufPtr_ += length; + parcelLen_ += length; + return length; +} + +uint32_t Parcel::GetBoolLen() +{ + return GetUInt8Len(); +} + +uint32_t Parcel::GetUInt8Len() +{ + return sizeof(uint8_t); +} + +uint32_t Parcel::GetIntLen() +{ + return sizeof(int32_t); +} + +uint32_t Parcel::GetUInt32Len() +{ + return sizeof(uint32_t); +} + +uint32_t Parcel::GetUInt64Len() +{ + return sizeof(uint64_t); +} + +uint32_t Parcel::GetInt64Len() +{ + return sizeof(int64_t); +} + +uint32_t Parcel::GetDoubleLen() +{ + return sizeof(double); +} + +uint32_t Parcel::GetVectorCharLen(const std::vector &data) +{ + return GetVectorLen(data); +} + +uint32_t Parcel::GetStringLen(const std::string &data) +{ + if (data.size() > INT32_MAX) { + return 0; + } + uint64_t len = sizeof(uint32_t) + static_cast(data.size()); + len = BYTE_8_ALIGN(len); + if (len > INT32_MAX) { + return 0; + } + return static_cast(len); +} + +#ifndef OMIT_MULTI_VER +uint32_t Parcel::GetMultiVerCommitLen(const MultiVerCommitNode &commit) +{ + uint64_t len = GetVectorCharLen(commit.commitId); + len += GetVectorCharLen(commit.leftParent); + len += GetVectorCharLen(commit.rightParent); + len += GetUInt64Len(); + len += GetUInt64Len(); + len += GetUInt64Len(); + len += GetStringLen(commit.deviceInfo); + if (len > INT32_MAX) { + return 0; + } + return static_cast(len); +} + +uint32_t Parcel::GetMultiVerCommitsLen(const std::vector &commits) +{ + uint64_t len = GetUInt64Len(); + for (auto &iter : commits) { + len += GetVectorCharLen(iter.commitId); + len += GetVectorCharLen(iter.leftParent); + len += GetVectorCharLen(iter.rightParent); + len += GetUInt64Len(); + len += GetUInt64Len(); + len += GetUInt64Len(); + len += GetStringLen(iter.deviceInfo); + len = BYTE_8_ALIGN(len); + if (len > INT32_MAX) { + return 0; + } + } + len = BYTE_8_ALIGN(len); + if (len > INT32_MAX) { + return 0; + } + return static_cast(len); +} +#endif + +void Parcel::EightByteAlign() +{ + bufPtr_ += BYTE_8_ALIGN(parcelLen_) - parcelLen_; + parcelLen_ = BYTE_8_ALIGN(parcelLen_); +} + +uint32_t Parcel::GetEightByteAlign(uint32_t len) +{ + return BYTE_8_ALIGN(len); +} + +uint32_t Parcel::GetAppendedLen() +{ + // 8 is 8-byte-align max append len, there are 2 8-byte-align totally + return sizeof(uint32_t) + sizeof(uint32_t) + 8 * 2; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/performance_analysis.cpp b/mock/distributeddb/common/src/performance_analysis.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f0aa482b9d731b955e4ddd55afc7156141791ef9 --- /dev/null +++ b/mock/distributeddb/common/src/performance_analysis.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "performance_analysis.h" + +#include + +#include "db_errno.h" +#include "macro_utils.h" +#include "time_helper.h" +#include "log_print.h" +#include "platform_specific.h" + +namespace DistributedDB { +const std::string PerformanceAnalysis::STATISTICAL_DATA_FILE_NAME_HEADER = "/data/log/statistic"; +const std::string PerformanceAnalysis::CSV_FILE_EXTENSION = ".csv"; +const std::string PerformanceAnalysis::DEFAULT_FILE_NAME = "default00"; + +PerformanceAnalysis *PerformanceAnalysis::GetInstance(int stepNum) +{ + static PerformanceAnalysis inst(stepNum); + return &inst; +} + +PerformanceAnalysis::PerformanceAnalysis(uint32_t inStepNum) + : isOpen_(false) +{ + if (inStepNum == 0) { + stepNum_ = 0; + } + stepNum_ = inStepNum; + counts_.resize(stepNum_); + timeRecordData_.timeInfo.resize(stepNum_); + stepTimeRecordInfo_.resize(stepNum_); + for (auto &stepIter : stepTimeRecordInfo_) { + stepIter.max = 0; + stepIter.min = ULLONG_MAX; + stepIter.average = 0; + } + for (auto iter = counts_.begin(); iter != counts_.end(); ++iter) { + *iter = 0; + } + fileNumber_ = 0; + fileID_ = std::string(DEFAULT_FILE_NAME) + std::to_string(fileNumber_); +} + +PerformanceAnalysis::~PerformanceAnalysis() {}; + +bool PerformanceAnalysis::IsStepValid(uint32_t step) const +{ + return (stepNum_ < MAX_TIMERECORD_STEP_NUM && step < stepNum_); +} + +bool PerformanceAnalysis::IsOpen() const +{ + return isOpen_; +} + +void PerformanceAnalysis::OpenPerformanceAnalysis() +{ + isOpen_ = true; +} + +void PerformanceAnalysis::ClosePerformanceAnalysis() +{ + isOpen_ = false; +} + +bool PerformanceAnalysis::InsertTimeRecord(const TimePair &timePair, uint32_t step) +{ + if (!IsStepValid(step)) { + return false; + } + timeRecordData_.timeInfo[step] = timePair; + return true; +} + +bool PerformanceAnalysis::GetTimeRecord(uint32_t step, TimePair &timePair) const +{ + if (!IsStepValid(step)) { + return false; + } + timePair = timeRecordData_.timeInfo[step]; + return true; +} + +void PerformanceAnalysis::TimeRecordStart() +{ + if (!IsOpen()) { + return; + } + StepTimeRecordStart(0); +} + +void PerformanceAnalysis::TimeRecordEnd() +{ + if (!IsOpen()) { + return; + } + StepTimeRecordEnd(0); +} + +void PerformanceAnalysis::StepTimeRecordStart(uint32_t step) +{ + if (!IsOpen()) { + return; + } + if (!IsStepValid(step)) { + return; + } + TimePair timePair = {0, 0}; + uint64_t curTime = 0; + int errCode = OS::GetCurrentSysTimeInMicrosecond(curTime); + if (errCode != E_OK) { + LOGE("[performance_analysis] GetCurrentSysTimeInMicrosecond fail"); + } else { + timePair.startTime = curTime; + LOGD("[performance_analysis] StepTimeRecordStart step:%" PRIu32 ", curTime:%" PRIu64, step, curTime); + (void)InsertTimeRecord(timePair, step); + } +} + +void PerformanceAnalysis::StepTimeRecordEnd(uint32_t step) +{ + if (!IsOpen()) { + return; + } + if (!IsStepValid(step)) { + return; + } + TimePair timePair = {0, 0}; + bool errCode = GetTimeRecord(step, timePair); + if (!errCode) { + return; + } + (void)InsertTimeRecord({0, 0}, step); + + uint64_t curTime = 0; + (void)OS::GetCurrentSysTimeInMicrosecond(curTime); + timePair.endTime = curTime; + LOGD("[performance_analysis] StepTimeRecordEnd step:%" PRIu32 ", curTime:%" PRIu64, step, curTime); + + if ((timePair.endTime < timePair.startTime) || (timePair.startTime == 0) || (timePair.endTime == 0)) { + return; + } + Timestamp offset = timePair.endTime - timePair.startTime; + if (stepTimeRecordInfo_[step].max < offset) { + stepTimeRecordInfo_[step].max = offset; + } + if (offset < stepTimeRecordInfo_[step].min) { + stepTimeRecordInfo_[step].min = offset; + } + counts_[step]++; + if (counts_[step] == 0) { + stepTimeRecordInfo_[step].average = 0; + return; + } + stepTimeRecordInfo_[step].average += (static_cast(offset) - + stepTimeRecordInfo_[step].average) / counts_[step]; +} + +std::string PerformanceAnalysis::GetStatistics() +{ + std::string result; + for (size_t i = 0; i < stepTimeRecordInfo_.size(); i++) { + if (stepTimeRecordInfo_[i].max != 0) { + result += "\nstep : " + std::to_string(i) + "\n"; + result += "max: " + std::to_string(stepTimeRecordInfo_[i].max) + "\n"; + result += "min: " + std::to_string(stepTimeRecordInfo_[i].min) + "\n"; + result += "average: " + + std::to_string(static_cast(stepTimeRecordInfo_[i].average)) + "\n"; + result += "count: " + std::to_string(counts_[i]) + "\n"; + } + } + OutStatistics(); + Clear(); + return result; +} + +void PerformanceAnalysis::OutStatistics() +{ + std::string addrStatistics = STATISTICAL_DATA_FILE_NAME_HEADER + fileID_ + CSV_FILE_EXTENSION; + outFile.open(addrStatistics, std::ios_base::app); + // This part filters the zeros data + outFile << "stepNum" << "," << "maxTime(us)" << "," << "minTime(us)" << "," << "averageTime(us)" + << "," << "count" << "," << "\n"; + for (size_t i = 0; i < stepTimeRecordInfo_.size(); i++) { // output to performance file + if (stepTimeRecordInfo_[i].max != 0) { + outFile << i << "," << stepTimeRecordInfo_[i].max<< "," << stepTimeRecordInfo_[i].min + << "," << stepTimeRecordInfo_[i].average << "," << counts_[i] << "," << "\n"; + } + } + LOGD("outFile success and exit!"); + outFile.close(); +} + +void PerformanceAnalysis::Clear() +{ + counts_.clear(); + timeRecordData_.timeInfo.clear(); + stepTimeRecordInfo_.clear(); + counts_.resize(stepNum_); + timeRecordData_.timeInfo.resize(stepNum_); + stepTimeRecordInfo_.resize(stepNum_); + for (auto &iter : stepTimeRecordInfo_) { + iter.max = 0; + iter.min = ULLONG_MAX; + iter.average = 0; + } + fileID_ = std::string(DEFAULT_FILE_NAME) + std::to_string(fileNumber_); +} + +void PerformanceAnalysis::SetFileNumber(const std::string &FileID) +{ + fileID_ = FileID; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/platform_specific.cpp b/mock/distributeddb/common/src/platform_specific.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f83f4d03a37648a6c243921f4bd0f0df6bd2449c --- /dev/null +++ b/mock/distributeddb/common/src/platform_specific.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "platform_specific.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "securec.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +namespace OS { +/* + * Common part that is the same between each os + */ +namespace { + const int ACCESS_MODE_EXISTENCE = 0; + const uint64_t MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS = 1000000; +} +bool CheckPathExistence(const std::string &filePath) +{ + return (access(filePath.c_str(), ACCESS_MODE_EXISTENCE) == 0); +} + +int RenameFilePath(const std::string &oldFilePath, const std::string &newFilePath) +{ + int errCode = rename(oldFilePath.c_str(), newFilePath.c_str()); + if (errCode < 0) { + LOGE("[Rename] Rename file fail. err = %d", errno); + return -E_SYSTEM_API_FAIL; + } + LOGI("Rename file path successfully!"); + return E_OK; +} + +int RemoveFile(const std::string &filePath) +{ + int errCode = remove(filePath.c_str()); + if (errCode < 0) { + LOGE("[RemoveFile] Remove file fail. err = %d", errno); + return -E_SYSTEM_API_FAIL; + } + LOGI("Remove file successfully!"); + return E_OK; +} + +int CalFileSize(const std::string &fileUrl, uint64_t &size) +{ + struct stat fileStat; + if (fileUrl.empty() || stat(fileUrl.c_str(), &fileStat) < 0 || fileStat.st_size < 0) { + int errCode = (errno == ENOENT) ? -E_NOT_FOUND : -E_INVALID_DB; + LOGD("Get file[%zu] size failed, errno [%d].", fileUrl.size(), errno); + return errCode; + } + + size = static_cast(fileStat.st_size); + return E_OK; +} + +void SplitFilePath(const std::string &filePath, std::string &fileDir, std::string &fileName) +{ + if (filePath.empty()) { + return; + } + + auto slashPos = filePath.find_last_of('/'); + if (slashPos == std::string::npos) { + fileName = filePath; + fileDir = ""; + return; + } + + fileDir = filePath.substr(0, slashPos); + fileName = filePath.substr(slashPos + 1); + return; +} + +int MakeDBDirectory(const std::string &directory) +{ + int errCode = mkdir(directory.c_str(), (S_IRWXU | S_IRGRP | S_IXGRP)); // The permission is 750 for linux based os + if (errCode < 0) { + LOGE("[MakeDir] Make directory fail:%d.", errno); + return -E_SYSTEM_API_FAIL; + } + return E_OK; +} + +int RemoveDBDirectory(const std::string &directory) +{ + return remove(directory.c_str()); +} + +int CreateFileByFileName(const std::string &fileName) +{ + int fp = open(fileName.c_str(), (O_WRONLY | O_CREAT), (S_IRUSR | S_IWUSR | S_IRGRP)); + if (fp < 0) { + LOGE("[CreateFile] Create file fail:%d.", errno); + return -E_SYSTEM_API_FAIL; + } + close(fp); + return E_OK; +} + +int GetRealPath(const std::string &inOriPath, std::string &outRealPath) +{ + const unsigned int MAX_PATH_LENGTH = PATH_MAX; + if (inOriPath.length() > MAX_PATH_LENGTH || MAX_PATH_LENGTH > 0x10000) { // max limit is 64K(0x10000). + LOGE("[RealPath] OriPath too long."); + return -E_INVALID_ARGS; + } + + char *realPath = new (std::nothrow) char[MAX_PATH_LENGTH + 1]; + if (realPath == nullptr) { + return -E_OUT_OF_MEMORY; + } + if (memset_s(realPath, MAX_PATH_LENGTH + 1, 0, MAX_PATH_LENGTH + 1) != EOK) { + delete []realPath; + return -E_SECUREC_ERROR; + } + + if (realpath(inOriPath.c_str(), realPath) == nullptr) { + LOGE("[OS] Realpath error:%d.", errno); + delete []realPath; + return -E_SYSTEM_API_FAIL; + } + outRealPath = std::string(realPath); + delete []realPath; + return E_OK; +} + +int GetCurrentSysTimeInMicrosecond(uint64_t &outTime) +{ + struct timeval rawTime; + int errCode = gettimeofday(&rawTime, nullptr); + if (errCode < 0) { + LOGE("[GetSysTime] Fail:%d.", errCode); + return -E_SYSTEM_API_FAIL; + } + outTime = static_cast(rawTime.tv_sec) * MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS + + static_cast(rawTime.tv_usec); + return E_OK; +} + +namespace { + const uint64_t MULTIPLES_BETWEEN_MICROSECONDS_AND_NANOSECONDS = 1000; +} + +int GetMonotonicRelativeTimeInMicrosecond(uint64_t &outTime) +{ + struct timespec rawTime; + int errCode = clock_gettime(CLOCK_BOOTTIME, &rawTime); + if (errCode < 0) { + LOGE("[GetMonoTime] Fail."); + return -E_SYSTEM_API_FAIL; + } + outTime = static_cast(rawTime.tv_sec) * MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS + + static_cast(rawTime.tv_nsec) / MULTIPLES_BETWEEN_MICROSECONDS_AND_NANOSECONDS; + return E_OK; +} + +static int GetFilePathAttr(const std::string &topPath, const std::string &relativePath, + std::list &files, bool isNeedAllPath) +{ + DIR *dir = opendir(topPath.c_str()); + if (dir == nullptr) { + LOGE("Open dir error:%d.", errno); + return -E_INVALID_PATH; + } + struct stat fileStat; + std::string fileAbsName; + int errCode = E_OK; + FileAttr file; + for (struct dirent *fileDirInfo = readdir(dir); fileDirInfo != nullptr; fileDirInfo = readdir(dir)) { + switch (fileDirInfo->d_type) { + case DT_REG: + file.fileType = FILE; + break; + case DT_DIR: + file.fileType = PATH; + break; + default: + file.fileType = OTHER; + } + if (strlen(fileDirInfo->d_name) == 0 || strcmp(fileDirInfo->d_name, ".") == 0 || + strcmp(fileDirInfo->d_name, "..") == 0) { + continue; + } + file.fileName = relativePath + fileDirInfo->d_name; + fileAbsName = topPath + "/" + fileDirInfo->d_name; + errCode = stat(fileAbsName.c_str(), &fileStat); + if (errCode != 0) { + LOGE("[GetFileAttr]Get file stat failed, error = %d.", errno); + errCode = -E_INVALID_PATH; + break; + } + if (isNeedAllPath) { + file.fileName = fileAbsName; + } + file.fileLen = static_cast(fileStat.st_size); + files.push_back(file); + if (file.fileType == PATH) { + errCode = GetFilePathAttr(fileAbsName, relativePath + fileDirInfo->d_name + "/", files, isNeedAllPath); + if (errCode != E_OK) { + break; + } + } + } + + closedir(dir); + return errCode; +} + +int GetFileAttrFromPath(const std::string &filePath, std::list &files, bool isNeedAllPath) +{ + return GetFilePathAttr(filePath, std::string(), files, isNeedAllPath); +} + +int GetFilePermissions(const std::string &fileName, uint32_t &permissions) +{ + struct stat fileStat; + int errCode = stat(fileName.c_str(), &fileStat); + if (errCode != E_OK) { + permissions = S_IRUSR | S_IWUSR; + LOGE("Get file stat failed, error = %d.", errno); + return -E_SYSTEM_API_FAIL; + } + permissions = fileStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + return E_OK; +} + +int SetFilePermissions(const std::string &fileName, uint32_t permissions) +{ + if (permissions > (S_IRWXU | S_IRWXG | S_IRWXO)) { + return -E_INVALID_ARGS; + } + int errCode = chmod(fileName.c_str(), permissions); + if (errCode != E_OK) { + LOGE("Set file permissions failed, error = %d.", errno); + return -E_SYSTEM_API_FAIL; + } + return E_OK; +} + +int OpenFile(const std::string &fileName, FileHandle &handle) +{ + handle.handle = open(fileName.c_str(), (O_WRONLY | O_CREAT), (S_IRUSR | S_IWUSR | S_IRGRP)); + if (handle.handle < 0) { + LOGE("[FileLock] can not open file when lock it:[%d]", errno); + return -E_SYSTEM_API_FAIL; + } + return E_OK; +} + +int CloseFile(FileHandle &handle) +{ + if (close(handle.handle) != 0) { + LOGE("close file failed, errno:%d", errno); + return -E_SYSTEM_API_FAIL; + } + handle.handle = -1; + return E_OK; +} + +int FileLock(const FileHandle &handle, bool isBlock) +{ + if (handle.handle < 0) { + LOGE("[FileLock] can not open file when lock it:[%d]", errno); + return -E_SYSTEM_API_FAIL; + } + + struct flock fileLockInfo; + (void)memset_s(&fileLockInfo, sizeof(fileLockInfo), 0, sizeof(fileLockInfo)); + fileLockInfo.l_type = F_WRLCK; + fileLockInfo.l_whence = SEEK_SET; + fileLockInfo.l_start = 0; + fileLockInfo.l_len = 0; + LOGD("Lock file isBlock[%d]", isBlock); + if (fcntl(handle.handle, isBlock ? F_SETLKW : F_SETLK, &fileLockInfo) == -1 && !isBlock) { + LOGD("Lock file is Blocked, please retry!"); + return -E_BUSY; + } + LOGI("file locked! errno:%d", errno); + return E_OK; +} + +int FileUnlock(FileHandle &handle) +{ + if (handle.handle == -1) { + LOGI("[FileUnlock] file handle is invalid!"); + return E_OK; + } + + struct flock fileLockInfo; + (void)memset_s(&fileLockInfo, sizeof(fileLockInfo), 0, sizeof(fileLockInfo)); + fileLockInfo.l_type = F_UNLCK; + fileLockInfo.l_whence = SEEK_SET; + fileLockInfo.l_start = 0; + fileLockInfo.l_len = 0; + if (fcntl(handle.handle, F_SETLK, &fileLockInfo) == -1) { + LOGE("Unlock file failed. errno:%d", errno); + return -E_SYSTEM_API_FAIL; + } + return CloseFile(handle); +} +} // namespace OS +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/query.cpp b/mock/distributeddb/common/src/query.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d2e2fb24799a0a827fba36a02020fd7f3c1e1c1 --- /dev/null +++ b/mock/distributeddb/common/src/query.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "query.h" +namespace DistributedDB { +Query::Query(const std::string &tableName) +{ + queryExpression_.SetTableName(tableName); +} +Query Query::Select() +{ + Query query; + return query; +} + +Query Query::Select(const std::string &tableName) +{ + Query query(tableName); + return query; +} + +Query &Query::BeginGroup() +{ + queryExpression_.BeginGroup(); + return *this; +} + +Query &Query::EndGroup() +{ + queryExpression_.EndGroup(); + return *this; +} + +Query &Query::IsNotNull(const std::string &field) +{ + queryExpression_.IsNotNull(field); + return *this; +} + +Query &Query::PrefixKey(const std::vector &key) +{ + queryExpression_.QueryByPrefixKey(key); + return *this; +} + +Query &Query::SuggestIndex(const std::string &indexName) +{ + queryExpression_.QueryBySuggestIndex(indexName); + return *this; +} + +Query &Query::InKeys(const std::set &keys) +{ + queryExpression_.InKeys(keys); + return *this; +} + +Query &Query::OrderBy(const std::string &field, bool isAsc) +{ + queryExpression_.OrderBy(field, isAsc); + return *this; +} + +Query &Query::Limit(int number, int offset) +{ + queryExpression_.Limit(number, offset); + return *this; +} + +Query &Query::Like(const std::string &field, const std::string &value) +{ + queryExpression_.Like(field, value); + return *this; +} + +Query &Query::NotLike(const std::string &field, const std::string &value) +{ + queryExpression_.NotLike(field, value); + return *this; +} + +Query &Query::IsNull(const std::string &field) +{ + queryExpression_.IsNull(field); + return *this; +} + +Query &Query::And() +{ + queryExpression_.And(); + return *this; +} + +Query &Query::Or() +{ + queryExpression_.Or(); + return *this; +} + +void Query::ExecuteCompareOperation(QueryObjType operType, const std::string &field, const QueryValueType type, + const FieldValue &fieldValue) +{ + switch (operType) { + case QueryObjType::EQUALTO: + queryExpression_.EqualTo(field, type, fieldValue); + break; + case QueryObjType::NOT_EQUALTO: + queryExpression_.NotEqualTo(field, type, fieldValue); + break; + case QueryObjType::GREATER_THAN: + queryExpression_.GreaterThan(field, type, fieldValue); + break; + case QueryObjType::LESS_THAN: + queryExpression_.LessThan(field, type, fieldValue); + break; + case QueryObjType::GREATER_THAN_OR_EQUALTO: + queryExpression_.GreaterThanOrEqualTo(field, type, fieldValue); + break; + case QueryObjType::LESS_THAN_OR_EQUALTO: + queryExpression_.LessThanOrEqualTo(field, type, fieldValue); + break; + default: + return; + } +} + +void Query::ExecuteCompareOperation(QueryObjType operType, const std::string &field, const QueryValueType type, + const std::vector &fieldValues) +{ + switch (operType) { + case QueryObjType::IN: + queryExpression_.In(field, type, fieldValues); + break; + case QueryObjType::NOT_IN: + queryExpression_.NotIn(field, type, fieldValues); + break; + default: + return; + } +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/common/src/query_expression.cpp b/mock/distributeddb/common/src/query_expression.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6de2914eaf437ac7e2ad64bea23a9a2ac73f9b4e --- /dev/null +++ b/mock/distributeddb/common/src/query_expression.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "query_expression.h" +#include "log_print.h" +#include "schema_utils.h" +#include "db_errno.h" + +namespace DistributedDB { +namespace { + const int MAX_OPR_TIMES = 256; +} // namespace + +void QueryExpression::AssemblyQueryInfo(const QueryObjType queryOperType, const std::string& field, + const QueryValueType type, const std::vector &values, bool isNeedFieldPath = true) +{ + if (queryInfo_.size() > MAX_OPR_TIMES) { + SetErrFlag(false); + LOGE("Operate too much times!"); + return; + } + + if (!GetErrFlag()) { + LOGE("Illegal data node!"); + return; + } + + FieldPath outPath; + if (isNeedFieldPath) { + if (SchemaUtils::ParseAndCheckFieldPath(field, outPath) != E_OK) { + SetErrFlag(false); + LOGE("Field path illegal!"); + return; + } + } + std::string formatedField; + if (isTableNameSpecified_) { // remove '$.' prefix in relational query + for (auto it = outPath.begin(); it < outPath.end(); ++it) { + if (it != outPath.begin()) { + formatedField += "."; + } + formatedField += *it; + } + } else { + formatedField = field; + } + queryInfo_.emplace_back(QueryObjNode{queryOperType, formatedField, type, values}); +} + +QueryExpression::QueryExpression() + : errFlag_(true), + tableName_("sync_data"), // default kv type store table name + isTableNameSpecified_(false) // default no specify for kv type store table name +{} + +void QueryExpression::EqualTo(const std::string& field, const QueryValueType type, const FieldValue &value) +{ + std::vector fieldValues{value}; + AssemblyQueryInfo(QueryObjType::EQUALTO, field, type, fieldValues); +} + +void QueryExpression::NotEqualTo(const std::string& field, const QueryValueType type, const FieldValue &value) +{ + std::vector fieldValues{value}; + AssemblyQueryInfo(QueryObjType::NOT_EQUALTO, field, type, fieldValues); +} + +void QueryExpression::GreaterThan(const std::string& field, const QueryValueType type, const FieldValue &value) +{ + if (type == QueryValueType::VALUE_TYPE_BOOL) { + LOGD("Prohibit the use of bool for comparison!"); + SetErrFlag(false); + } + std::vector fieldValues{value}; + AssemblyQueryInfo(QueryObjType::GREATER_THAN, field, type, fieldValues); +} + +void QueryExpression::LessThan(const std::string& field, const QueryValueType type, const FieldValue &value) +{ + if (type == QueryValueType::VALUE_TYPE_BOOL) { + LOGD("Prohibit the use of bool for comparison!"); + SetErrFlag(false); + } + std::vector fieldValues{value}; + AssemblyQueryInfo(QueryObjType::LESS_THAN, field, type, fieldValues); +} + +void QueryExpression::GreaterThanOrEqualTo(const std::string& field, const QueryValueType type, const FieldValue &value) +{ + if (type == QueryValueType::VALUE_TYPE_BOOL) { + LOGD("Prohibit the use of bool for comparison!"); + SetErrFlag(false); + } + std::vector fieldValues{value}; + AssemblyQueryInfo(QueryObjType::GREATER_THAN_OR_EQUALTO, field, type, fieldValues); +} + +void QueryExpression::LessThanOrEqualTo(const std::string& field, const QueryValueType type, const FieldValue &value) +{ + if (type == QueryValueType::VALUE_TYPE_BOOL) { + LOGD("Prohibit the use of bool for comparison!"); + SetErrFlag(false); + } + std::vector fieldValues{value}; + AssemblyQueryInfo(QueryObjType::LESS_THAN_OR_EQUALTO, field, type, fieldValues); +} + +void QueryExpression::OrderBy(const std::string& field, bool isAsc) +{ + FieldValue fieldValue; + fieldValue.boolValue = isAsc; + std::vector fieldValues{fieldValue}; + AssemblyQueryInfo(QueryObjType::ORDERBY, field, QueryValueType::VALUE_TYPE_BOOL, fieldValues); +} + +void QueryExpression::Like(const std::string& field, const std::string &value) +{ + FieldValue fieldValue; + fieldValue.stringValue = value; + std::vector fieldValues{fieldValue}; + AssemblyQueryInfo(QueryObjType::LIKE, field, QueryValueType::VALUE_TYPE_STRING, fieldValues); +} + +void QueryExpression::NotLike(const std::string& field, const std::string &value) +{ + FieldValue fieldValue; + fieldValue.stringValue = value; + std::vector fieldValues{fieldValue}; + AssemblyQueryInfo(QueryObjType::NOT_LIKE, field, QueryValueType::VALUE_TYPE_STRING, fieldValues); +} + +void QueryExpression::Limit(int number, int offset) +{ + FieldValue fieldNumber; + fieldNumber.integerValue = number; + FieldValue fieldOffset; + fieldOffset.integerValue = offset; + std::vector fieldValues{fieldNumber, fieldOffset}; + AssemblyQueryInfo(QueryObjType::LIMIT, std::string(), QueryValueType::VALUE_TYPE_INTEGER, fieldValues, false); +} + +void QueryExpression::IsNull(const std::string& field) +{ + AssemblyQueryInfo(QueryObjType::IS_NULL, field, QueryValueType::VALUE_TYPE_NULL, std::vector()); +} + +void QueryExpression::IsNotNull(const std::string& field) +{ + AssemblyQueryInfo(QueryObjType::IS_NOT_NULL, field, QueryValueType::VALUE_TYPE_NULL, std::vector()); +} + +void QueryExpression::In(const std::string& field, const QueryValueType type, const std::vector &values) +{ + AssemblyQueryInfo(QueryObjType::IN, field, type, values); +} + +void QueryExpression::NotIn(const std::string& field, const QueryValueType type, const std::vector &values) +{ + AssemblyQueryInfo(QueryObjType::NOT_IN, field, type, values); +} + +void QueryExpression::And() +{ + AssemblyQueryInfo(QueryObjType::AND, std::string(), QueryValueType::VALUE_TYPE_NULL, + std::vector(), false); +} + +void QueryExpression::Or() +{ + AssemblyQueryInfo(QueryObjType::OR, std::string(), QueryValueType::VALUE_TYPE_NULL, + std::vector(), false); +} + +void QueryExpression::QueryByPrefixKey(const std::vector &key) +{ + queryInfo_.emplace_back(QueryObjNode{QueryObjType::QUERY_BY_KEY_PREFIX, std::string(), + QueryValueType::VALUE_TYPE_NULL, std::vector()}); + prefixKey_ = key; +} + +void QueryExpression::QueryBySuggestIndex(const std::string &indexName) +{ + queryInfo_.emplace_back(QueryObjNode{QueryObjType::SUGGEST_INDEX, indexName, + QueryValueType::VALUE_TYPE_STRING, std::vector()}); + suggestIndex_ = indexName; +} + +void QueryExpression::InKeys(const std::set &keys) +{ + queryInfo_.emplace_back(QueryObjNode{QueryObjType::IN_KEYS, std::string(), QueryValueType::VALUE_TYPE_NULL, + std::vector()}); + keys_ = keys; +} + +const std::list &QueryExpression::GetQueryExpression() +{ + if (!GetErrFlag()) { + queryInfo_.clear(); + queryInfo_.emplace_back(QueryObjNode{QueryObjType::OPER_ILLEGAL}); + LOGE("Query operate illegal!"); + } + return queryInfo_; +} + +std::vector QueryExpression::GetPreFixKey() const +{ + return prefixKey_; +} + +void QueryExpression::SetTableName(const std::string &tableName) +{ + tableName_ = tableName; + isTableNameSpecified_ = true; +} + +const std::string &QueryExpression::GetTableName() +{ + return tableName_; +} + +bool QueryExpression::IsTableNameSpecified() const +{ + return isTableNameSpecified_; +} + +std::string QueryExpression::GetSuggestIndex() const +{ + return suggestIndex_; +} + +const std::set &QueryExpression::GetKeys() const +{ + return keys_; +} + +void QueryExpression::BeginGroup() +{ + queryInfo_.emplace_back(QueryObjNode{QueryObjType::BEGIN_GROUP, std::string(), + QueryValueType::VALUE_TYPE_NULL, std::vector()}); +} + +void QueryExpression::EndGroup() +{ + queryInfo_.emplace_back(QueryObjNode{QueryObjType::END_GROUP, std::string(), + QueryValueType::VALUE_TYPE_NULL, std::vector()}); +} + +void QueryExpression::Reset() +{ + errFlag_ = true; + queryInfo_.clear(); + prefixKey_.clear(); + prefixKey_.shrink_to_fit(); + suggestIndex_.clear(); + keys_.clear(); +} + +void QueryExpression::SetErrFlag(bool flag) +{ + errFlag_ = flag; +} + +bool QueryExpression::GetErrFlag() +{ + return errFlag_; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/ref_object.cpp b/mock/distributeddb/common/src/ref_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3cd10760d99a936574bb755b853dbfda81925ec --- /dev/null +++ b/mock/distributeddb/common/src/ref_object.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ref_object.h" +#include "log_print.h" + +namespace DistributedDB { +constexpr static int MAX_REF_COUNT = 1024; + +RefObject::AutoLock::AutoLock(const RefObject *obj, bool unlocked) + : refObj_(obj), + IsLocked_(false) +{ + if (refObj_ != nullptr) { + if (unlocked) { + refObj_->LockObj(); + } + IsLocked_ = true; + } +} + +void RefObject::AutoLock::Lock() +{ + if (refObj_ != nullptr) { + if (!IsLocked_) { + refObj_->LockObj(); + IsLocked_ = true; + } else { + LOGE("RefObject-AutoLock: obj' acquires lock more than once."); + } + } +} + +void RefObject::AutoLock::Unlock() +{ + if (refObj_ != nullptr) { + if (IsLocked_) { + refObj_->UnlockObj(); + IsLocked_ = false; + } else { + LOGE("RefObject-AutoLock: obj releases lock more than once."); + } + } +} + +RefObject::AutoLock::~AutoLock() +{ + if (refObj_ != nullptr) { + if (IsLocked_) { + refObj_->UnlockObj(); + IsLocked_ = false; + } + refObj_ = nullptr; + } +} + +RefObject::RefObject() + : refCount_(1), + isKilled_(false) +{} + +RefObject::~RefObject() +{ + int refCount = refCount_.load(std::memory_order_seq_cst); + if (refCount > 0) { + LOGF("object is destructed with ref-count > 0., refCount = %d", refCount); + } +} + +void RefObject::OnLastRef(const std::function &callback) const +{ + if (onLast_) { + std::string tag = GetObjectTag(); + LOGW("%s object set 'OnLastRef()' callback twice.", tag.c_str()); + return; + } + onLast_ = callback; +} + +void RefObject::OnKill(const std::function &callback) +{ + if (onKill_) { + std::string tag = GetObjectTag(); + LOGW("%s object set 'OnKill()' callback twice.", tag.c_str()); + return; + } + onKill_ = callback; +} + +bool RefObject::IsKilled() const +{ + return isKilled_; +} + +void RefObject::KillObj() +{ + std::lock_guard lockGuard(objLock_); + if (!IsKilled()) { + isKilled_ = true; + if (onKill_) { + onKill_(); + } + } +} + +void RefObject::LockObj() const +{ + objLock_.lock(); +} + +void RefObject::UnlockObj() const +{ + objLock_.unlock(); +} + +bool RefObject::WaitLockedUntil(std::condition_variable &cv, + const std::function &condition, int seconds) +{ + // Enter with lock held. + if (!condition) { + return false; + } + + bool waitOk = true; + { + std::unique_lock lock(objLock_, std::adopt_lock_t()); + while (!condition()) { + if (seconds > 0) { + cv.wait_for(lock, std::chrono::seconds(seconds)); + waitOk = condition(); + break; + } else { + cv.wait(lock); + } + } + } + + // Lock has just been dropped in unique_lock::~unique_lock(), + // so we lock it again. + LockObj(); + return waitOk; +} + +void RefObject::IncObjRef(const RefObject *obj) +{ + if (obj == nullptr) { + return; + } + int refCount = obj->refCount_.fetch_add(1, std::memory_order_seq_cst); + if ((refCount <= 0) || (refCount >= MAX_REF_COUNT)) { + std::string tag = obj->GetObjectTag(); + LOGF("%s object is refed with ref-count=%d.", tag.c_str(), refCount); + } +} + +void RefObject::DecObjRef(const RefObject *obj) +{ + if (obj == nullptr) { + return; + } + int refCount = obj->refCount_.fetch_sub(1, std::memory_order_seq_cst); + if (refCount <= 0) { + std::string tag = obj->GetObjectTag(); + LOGF("%s object is unrefed with ref-count(%d) <= 0.", tag.c_str(), refCount); + } else { + if (refCount == 1) { + if (obj->onLast_) { + obj->onLast_(); + } + delete obj; + } + } +} + +void RefObject::KillAndDecObjRef(RefObject *obj) +{ + if (obj == nullptr) { + return; + } + obj->KillObj(); + obj->DecObjRef(obj); + obj = nullptr; +} + +DEFINE_OBJECT_TAG_FACILITIES(RefObject) +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/relational/relational_schema_object.cpp b/mock/distributeddb/common/src/relational/relational_schema_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75e565da778366ebb3e70c05f5f6c8b27cb4f4b2 --- /dev/null +++ b/mock/distributeddb/common/src/relational/relational_schema_object.cpp @@ -0,0 +1,755 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "relational_schema_object.h" + +#include + +#include "json_object.h" +#include "schema_constant.h" +#include "schema_utils.h" + +namespace DistributedDB { +const std::string &FieldInfo::GetFieldName() const +{ + return fieldName_; +} + +void FieldInfo::SetFieldName(const std::string &fileName) +{ + fieldName_ = fileName; +} + +const std::string &FieldInfo::GetDataType() const +{ + return dataType_; +} + +static StorageType AffinityType(const std::string &dataType) +{ + return StorageType::STORAGE_TYPE_NULL; +} + +void FieldInfo::SetDataType(const std::string &dataType) +{ + dataType_ = dataType; + transform(dataType_.begin(), dataType_.end(), dataType_.begin(), ::tolower); + storageType_ = AffinityType(dataType_); +} + +bool FieldInfo::IsNotNull() const +{ + return isNotNull_; +} + +void FieldInfo::SetNotNull(bool isNotNull) +{ + isNotNull_ = isNotNull; +} + +bool FieldInfo::HasDefaultValue() const +{ + return hasDefaultValue_; +} + +const std::string &FieldInfo::GetDefaultValue() const +{ + return defaultValue_; +} + +void FieldInfo::SetDefaultValue(const std::string &value) +{ + hasDefaultValue_ = true; + defaultValue_ = value; +} + +// convert to StorageType according "Determination Of Column Affinity" +StorageType FieldInfo::GetStorageType() const +{ + return storageType_; +} + +void FieldInfo::SetStorageType(StorageType storageType) +{ + storageType_ = storageType; +} + +int FieldInfo::GetColumnId() const +{ + return cid_; +} + +void FieldInfo::SetColumnId(int cid) +{ + cid_ = cid; +} + +std::string FieldInfo::ToAttributeString() const +{ + std::string attrStr = "\"" + fieldName_ + "\": {"; + attrStr += "\"COLUMN_ID\":" + std::to_string(cid_) + ","; + attrStr += "\"TYPE\":\"" + dataType_ + "\","; + attrStr += "\"NOT_NULL\":" + std::string(isNotNull_ ? "true" : "false"); + if (hasDefaultValue_) { + attrStr += ","; + attrStr += "\"DEFAULT\":\"" + defaultValue_ + "\""; + } + attrStr += "}"; + return attrStr; +} + +int FieldInfo::CompareWithField(const FieldInfo &inField) const +{ + if (fieldName_ != inField.GetFieldName() || dataType_ != inField.GetDataType() || + isNotNull_ != inField.IsNotNull()) { + return false; + } + if (hasDefaultValue_ && inField.HasDefaultValue()) { + return defaultValue_ == inField.GetDefaultValue(); + } + return hasDefaultValue_ == inField.HasDefaultValue(); +} + +const std::string &TableInfo::GetTableName() const +{ + return tableName_; +} + +void TableInfo::SetTableName(const std::string &tableName) +{ + tableName_ = tableName; +} + +void TableInfo::SetAutoIncrement(bool autoInc) +{ + autoInc_ = autoInc; +} + +bool TableInfo::GetAutoIncrement() const +{ + return autoInc_; +} + +const std::string &TableInfo::GetCreateTableSql() const +{ + return sql_; +} + +void TableInfo::SetCreateTableSql(std::string sql) +{ + sql_ = sql; + for (auto &c : sql) { + c = static_cast(std::toupper(c)); + } + if (sql.find("AUTOINCREMENT") != std::string::npos) { + autoInc_ = true; + } +} + +const std::map &TableInfo::GetFields() const +{ + return fields_; +} + +const std::vector &TableInfo::GetFieldInfos() const +{ + if (!fieldInfos_.empty() && fieldInfos_.size() == fields_.size()) { + return fieldInfos_; + } + fieldInfos_.resize(fields_.size()); + if (fieldInfos_.size() != fields_.size()) { + LOGE("GetField error, alloc memory failed."); + return fieldInfos_; + } + for (const auto &entry : fields_) { + if (static_cast(entry.second.GetColumnId()) >= fieldInfos_.size()) { + LOGE("Cid is over field size."); + fieldInfos_.clear(); + return fieldInfos_; + } + fieldInfos_.at(entry.second.GetColumnId()) = entry.second; + } + return fieldInfos_; +} + +std::string TableInfo::GetFieldName(uint32_t cid) const +{ + if (cid >= fields_.size() || GetFieldInfos().empty()) { + return {}; + } + return GetFieldInfos().at(cid).GetFieldName(); +} + +bool TableInfo::IsValid() const +{ + return !tableName_.empty(); +} + +void TableInfo::AddField(const FieldInfo &field) +{ + fields_[field.GetFieldName()] = field; +} + +const std::map &TableInfo::GetIndexDefine() const +{ + return indexDefines_; +} + +void TableInfo::AddIndexDefine(const std::string &indexName, const CompositeFields &indexDefine) +{ + indexDefines_[indexName] = indexDefine; +} + +const FieldName &TableInfo::GetPrimaryKey() const +{ + return primaryKey_; +} + +void TableInfo::SetPrimaryKey(const FieldName &fieldName) +{ + primaryKey_ = fieldName; +} + +void TableInfo::AddFieldDefineString(std::string &attrStr) const +{ + if (fields_.empty()) { + return; + } + attrStr += R"("DEFINE": {)"; + for (auto itField = fields_.begin(); itField != fields_.end(); ++itField) { + attrStr += itField->second.ToAttributeString(); + if (itField != std::prev(fields_.end(), 1)) { + attrStr += ","; + } + } + attrStr += "},"; +} + +void TableInfo::AddIndexDefineString(std::string &attrStr) const +{ + if (indexDefines_.empty()) { + return; + } + attrStr += R"(,"INDEX": {)"; + for (auto itIndexDefine = indexDefines_.begin(); itIndexDefine != indexDefines_.end(); ++itIndexDefine) { + attrStr += "\"" + (*itIndexDefine).first + "\": [\""; + for (auto itField = itIndexDefine->second.begin(); itField != itIndexDefine->second.end(); ++itField) { + attrStr += *itField; + if (itField != itIndexDefine->second.end() - 1) { + attrStr += "\",\""; + } + } + attrStr += "\"]"; + if (itIndexDefine != std::prev(indexDefines_.end(), 1)) { + attrStr += ","; + } + } + attrStr += "}"; +} + +int TableInfo::CompareWithTable(const TableInfo &inTableInfo) const +{ + if (tableName_ != inTableInfo.GetTableName()) { + LOGW("[Relational][Compare] Table name is not same"); + return -E_RELATIONAL_TABLE_INCOMPATIBLE; + } + + if (primaryKey_ != inTableInfo.GetPrimaryKey()) { + LOGW("[Relational][Compare] Table primary key is not same"); + return -E_RELATIONAL_TABLE_INCOMPATIBLE; + } + + int fieldCompareResult = CompareWithTableFields(inTableInfo.GetFields()); + if (fieldCompareResult == -E_RELATIONAL_TABLE_INCOMPATIBLE) { + LOGW("[Relational][Compare] Compare table fields with in table, %d", fieldCompareResult); + return -E_RELATIONAL_TABLE_INCOMPATIBLE; + } + + int indexCompareResult = CompareWithTableIndex(inTableInfo.GetIndexDefine()); + return (fieldCompareResult == -E_RELATIONAL_TABLE_EQUAL) ? indexCompareResult : fieldCompareResult; +} + +int TableInfo::CompareWithTableFields(const std::map &inTableFields) const +{ + auto itLocal = fields_.begin(); + auto itInTable = inTableFields.begin(); + int errCode = -E_RELATIONAL_TABLE_EQUAL; + while (itLocal != fields_.end() && itInTable != inTableFields.end()) { + if (itLocal->first == itInTable->first) { // Same field + if (!itLocal->second.CompareWithField(itInTable->second)) { // Compare field + LOGW("[Relational][Compare] Table field is incompatible"); // not compatible + return -E_RELATIONAL_TABLE_INCOMPATIBLE; + } + itLocal++; // Compare next field + } else { // Assume local table fields is a subset of in table + if (itInTable->second.IsNotNull() && !itInTable->second.HasDefaultValue()) { // Upgrade field not compatible + LOGW("[Relational][Compare] Table upgrade field should allowed to be empty or have default value."); + return -E_RELATIONAL_TABLE_INCOMPATIBLE; + } + errCode = -E_RELATIONAL_TABLE_COMPATIBLE_UPGRADE; + } + itInTable++; // Next in table field + } + + if (itLocal != fields_.end()) { + LOGW("[Relational][Compare] Table field is missing"); + return -E_RELATIONAL_TABLE_INCOMPATIBLE; + } + + if (itInTable == inTableFields.end()) { + return errCode; + } + + while (itInTable != inTableFields.end()) { + if (itInTable->second.IsNotNull() && !itInTable->second.HasDefaultValue()) { + LOGW("[Relational][Compare] Table upgrade field should allowed to be empty or have default value."); + return -E_RELATIONAL_TABLE_INCOMPATIBLE; + } + itInTable++; + } + return -E_RELATIONAL_TABLE_COMPATIBLE_UPGRADE; +} + +int TableInfo::CompareWithTableIndex(const std::map &inTableIndex) const +{ + // Index comparison results do not affect synchronization decisions + auto itLocal = indexDefines_.begin(); + auto itInTable = inTableIndex.begin(); + while (itLocal != indexDefines_.end() && itInTable != inTableIndex.end()) { + if (itLocal->first != itInTable->first || itLocal->second != itInTable->second) { + return -E_RELATIONAL_TABLE_COMPATIBLE; + } + itLocal++; + itInTable++; + } + return (itLocal == indexDefines_.end() && itInTable == inTableIndex.end()) ? -E_RELATIONAL_TABLE_EQUAL : + -E_RELATIONAL_TABLE_COMPATIBLE; +} + +std::string TableInfo::ToTableInfoString() const +{ + std::string attrStr; + attrStr += "{"; + attrStr += R"("NAME": ")" + tableName_ + "\","; + AddFieldDefineString(attrStr); + attrStr += R"("AUTOINCREMENT": )"; + if (autoInc_) { + attrStr += "true,"; + } else { + attrStr += "false,"; + } + if (!primaryKey_.empty()) { + attrStr += R"("PRIMARY_KEY": ")" + primaryKey_ + "\""; + } + AddIndexDefineString(attrStr); + attrStr += "}"; + return attrStr; +} + +std::map TableInfo::GetSchemaDefine() const +{ + std::map schemaDefine; + for (const auto &[fieldName, fieldInfo] : GetFields()) { + FieldValue defaultValue; + defaultValue.stringValue = fieldInfo.GetDefaultValue(); + schemaDefine[std::vector { fieldName }] = SchemaAttribute { + .type = FieldType::LEAF_FIELD_NULL, // For relational schema, the json field type is unimportant. + .isIndexable = true, // For relational schema, all field is indexable. + .hasNotNullConstraint = fieldInfo.IsNotNull(), + .hasDefaultValue = fieldInfo.HasDefaultValue(), + .defaultValue = defaultValue, + .customFieldType = {} + }; + } + return schemaDefine; +} + +bool RelationalSchemaObject::IsSchemaValid() const +{ + return isValid_; +} + +SchemaType RelationalSchemaObject::GetSchemaType() const +{ + return schemaType_; +} + +std::string RelationalSchemaObject::ToSchemaString() const +{ + return schemaString_; +} + +int RelationalSchemaObject::ParseFromSchemaString(const std::string &inSchemaString) +{ + if (isValid_) { + return -E_NOT_PERMIT; + } + + if (inSchemaString.empty() || inSchemaString.size() > SchemaConstant::SCHEMA_STRING_SIZE_LIMIT) { + LOGE("[RelationalSchema][Parse] SchemaSize=%zu is invalid.", inSchemaString.size()); + return -E_INVALID_ARGS; + } + JsonObject schemaObj; + int errCode = schemaObj.Parse(inSchemaString); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Schema json string parse failed: %d.", errCode); + return errCode; + } + + errCode = ParseRelationalSchema(schemaObj); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Parse to relational schema failed: %d.", errCode); + return errCode; + } + + schemaType_ = SchemaType::RELATIVE; + schemaString_ = schemaObj.ToString(); + isValid_ = true; + return E_OK; +} + +void RelationalSchemaObject::GenerateSchemaString() +{ + schemaString_ = {}; + schemaString_ += "{"; + schemaString_ += R"("SCHEMA_VERSION":"2.0",)"; + schemaString_ += R"("SCHEMA_TYPE":"RELATIVE",)"; + schemaString_ += R"("TABLES":[)"; + for (auto it = tables_.begin(); it != tables_.end(); it++) { + if (it != tables_.begin()) { + schemaString_ += ","; + } + schemaString_ += it->second.ToTableInfoString(); + } + schemaString_ += R"(])"; + schemaString_ += "}"; +} + +void RelationalSchemaObject::AddRelationalTable(const TableInfo &tb) +{ + tables_[tb.GetTableName()] = tb; + isValid_ = true; + GenerateSchemaString(); +} + + +void RelationalSchemaObject::RemoveRelationalTable(const std::string &tableName) +{ + tables_.erase(tableName); + GenerateSchemaString(); +} + +const std::map &RelationalSchemaObject::GetTables() const +{ + return tables_; +} + +std::vector RelationalSchemaObject::GetTableNames() const +{ + std::vector tableNames; + for (const auto &it : tables_) { + tableNames.emplace_back(it.first); + } + return tableNames; +} + +TableInfo RelationalSchemaObject::GetTable(const std::string &tableName) const +{ + auto it = tables_.find(tableName); + if (it != tables_.end()) { + return it->second; + } + return {}; +} + +int RelationalSchemaObject::CompareAgainstSchemaObject(const std::string &inSchemaString, + std::map &cmpRst) const +{ + return E_OK; +} + +int RelationalSchemaObject::CompareAgainstSchemaObject(const RelationalSchemaObject &inSchemaObject, + std::map &cmpRst) const +{ + return E_OK; +} + +namespace { +int GetMemberFromJsonObject(const JsonObject &inJsonObject, const std::string &fieldName, FieldType expectType, + bool isNecessary, FieldValue &fieldValue) +{ + if (!inJsonObject.IsFieldPathExist(FieldPath {fieldName})) { + if (isNecessary) { + LOGE("[RelationalSchema][Parse] Get schema %s not exist. isNecessary: %d", fieldName.c_str(), isNecessary); + return -E_SCHEMA_PARSE_FAIL; + } + return -E_NOT_FOUND; + } + + FieldType fieldType; + int errCode = inJsonObject.GetFieldTypeByFieldPath(FieldPath {fieldName}, fieldType); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Get schema %s fieldType failed: %d.", fieldName.c_str(), errCode); + return -E_SCHEMA_PARSE_FAIL; + } + + if (fieldType != expectType) { + LOGE("[RelationalSchema][Parse] Expect %s fieldType %d but: %d.", fieldName.c_str(), + static_cast(expectType), static_cast(fieldType)); + return -E_SCHEMA_PARSE_FAIL; + } + + errCode = inJsonObject.GetFieldValueByFieldPath(FieldPath {fieldName}, fieldValue); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Get schema %s value failed: %d.", fieldName.c_str(), errCode); + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; +} +} + +int RelationalSchemaObject::ParseRelationalSchema(const JsonObject &inJsonObject) +{ + int errCode = ParseCheckSchemaVersion(inJsonObject); + if (errCode != E_OK) { + return errCode; + } + errCode = ParseCheckSchemaType(inJsonObject); + if (errCode != E_OK) { + return errCode; + } + return ParseCheckSchemaTableDefine(inJsonObject); +} + +int RelationalSchemaObject::ParseCheckSchemaVersion(const JsonObject &inJsonObject) +{ + FieldValue fieldValue; + int errCode = GetMemberFromJsonObject(inJsonObject, SchemaConstant::KEYWORD_SCHEMA_VERSION, + FieldType::LEAF_FIELD_STRING, true, fieldValue); + if (errCode != E_OK) { + return errCode; + } + + if (SchemaUtils::Strip(fieldValue.stringValue) != SchemaConstant::SCHEMA_SUPPORT_VERSION_V2) { + LOGE("[RelationalSchema][Parse] Unexpected SCHEMA_VERSION=%s.", fieldValue.stringValue.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + schemaVersion_ = SchemaConstant::SCHEMA_SUPPORT_VERSION_V2; + return E_OK; +} + +int RelationalSchemaObject::ParseCheckSchemaType(const JsonObject &inJsonObject) +{ + FieldValue fieldValue; + int errCode = GetMemberFromJsonObject(inJsonObject, SchemaConstant::KEYWORD_SCHEMA_TYPE, + FieldType::LEAF_FIELD_STRING, true, fieldValue); + if (errCode != E_OK) { + return errCode; + } + + if (SchemaUtils::Strip(fieldValue.stringValue) != SchemaConstant::KEYWORD_TYPE_RELATIVE) { + LOGE("[RelationalSchema][Parse] Unexpected SCHEMA_TYPE=%s.", fieldValue.stringValue.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + schemaType_ = SchemaType::RELATIVE; + return E_OK; +} + +int RelationalSchemaObject::ParseCheckSchemaTableDefine(const JsonObject &inJsonObject) +{ + FieldType fieldType; + int errCode = inJsonObject.GetFieldTypeByFieldPath(FieldPath {SchemaConstant::KEYWORD_SCHEMA_TABLE}, fieldType); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Get schema TABLES fieldType failed: %d.", errCode); + return -E_SCHEMA_PARSE_FAIL; + } + if (FieldType::LEAF_FIELD_ARRAY != fieldType) { + LOGE("[RelationalSchema][Parse] Expect TABLES fieldType ARRAY but %s.", + SchemaUtils::FieldTypeString(fieldType).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + std::vector tables; + errCode = inJsonObject.GetObjectArrayByFieldPath(FieldPath{SchemaConstant::KEYWORD_SCHEMA_TABLE}, tables); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Get schema TABLES value failed: %d.", errCode); + return -E_SCHEMA_PARSE_FAIL; + } + for (const JsonObject &table : tables) { + errCode = ParseCheckTableInfo(table); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Parse schema TABLES failed: %d.", errCode); + return errCode; + } + } + return E_OK; +} + +int RelationalSchemaObject::ParseCheckTableInfo(const JsonObject &inJsonObject) +{ + TableInfo resultTable; + int errCode = ParseCheckTableName(inJsonObject, resultTable); + if (errCode != E_OK) { + return errCode; + } + errCode = ParseCheckTableDefine(inJsonObject, resultTable); + if (errCode != E_OK) { + return errCode; + } + errCode = ParseCheckTableAutoInc(inJsonObject, resultTable); + if (errCode != E_OK) { + return errCode; + } + errCode = ParseCheckTablePrimaryKey(inJsonObject, resultTable); + if (errCode != E_OK) { + return errCode; + } + errCode = ParseCheckTableIndex(inJsonObject, resultTable); + if (errCode != E_OK) { + return errCode; + } + tables_[resultTable.GetTableName()] = resultTable; + return E_OK; +} + +int RelationalSchemaObject::ParseCheckTableName(const JsonObject &inJsonObject, TableInfo &resultTable) +{ + FieldValue fieldValue; + int errCode = GetMemberFromJsonObject(inJsonObject, "NAME", FieldType::LEAF_FIELD_STRING, + true, fieldValue); + if (errCode == E_OK) { + resultTable.SetTableName(fieldValue.stringValue); + } + return errCode; +} + +int RelationalSchemaObject::ParseCheckTableDefine(const JsonObject &inJsonObject, TableInfo &resultTable) +{ + std::map tableFields; + int errCode = inJsonObject.GetSubFieldPathAndType(FieldPath {"DEFINE"}, tableFields); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Get schema TABLES DEFINE failed: %d.", errCode); + return -E_SCHEMA_PARSE_FAIL; + } + + for (const auto &field : tableFields) { + if (field.second != FieldType::INTERNAL_FIELD_OBJECT) { + LOGE("[RelationalSchema][Parse] Expect schema TABLES DEFINE fieldType INTERNAL OBJECT but : %s.", + SchemaUtils::FieldTypeString(field.second).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + + JsonObject fieldObj; + errCode = inJsonObject.GetObjectByFieldPath(field.first, fieldObj); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Get table field object failed. %d", errCode); + return errCode; + } + + FieldInfo fieldInfo; + fieldInfo.SetFieldName(field.first[1]); // 1 : table name element in path + errCode = ParseCheckTableFieldInfo(fieldObj, field.first, fieldInfo); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Parse table field info failed. %d", errCode); + return -E_SCHEMA_PARSE_FAIL; + } + resultTable.AddField(fieldInfo); + } + return E_OK; +} + +int RelationalSchemaObject::ParseCheckTableFieldInfo(const JsonObject &inJsonObject, const FieldPath &path, + FieldInfo &field) +{ + FieldValue fieldValue; + int errCode = GetMemberFromJsonObject(inJsonObject, "COLUMN_ID", FieldType::LEAF_FIELD_INTEGER, true, fieldValue); + if (errCode != E_OK) { + return errCode; + } + field.SetColumnId(fieldValue.integerValue); + + errCode = GetMemberFromJsonObject(inJsonObject, "TYPE", FieldType::LEAF_FIELD_STRING, true, fieldValue); + if (errCode != E_OK) { + return errCode; + } + field.SetDataType(fieldValue.stringValue); + + errCode = GetMemberFromJsonObject(inJsonObject, "NOT_NULL", FieldType::LEAF_FIELD_BOOL, true, fieldValue); + if (errCode != E_OK) { + return errCode; + } + field.SetNotNull(fieldValue.boolValue); + + errCode = GetMemberFromJsonObject(inJsonObject, "DEFAULT", FieldType::LEAF_FIELD_STRING, false, fieldValue); + if (errCode == E_OK) { + field.SetDefaultValue(fieldValue.stringValue); + } else if (errCode != -E_NOT_FOUND) { + return errCode; + } + + return E_OK; +} + +int RelationalSchemaObject::ParseCheckTableAutoInc(const JsonObject &inJsonObject, TableInfo &resultTable) +{ + FieldValue fieldValue; + int errCode = GetMemberFromJsonObject(inJsonObject, "AUTOINCREMENT", FieldType::LEAF_FIELD_BOOL, false, fieldValue); + if (errCode == E_OK) { + resultTable.SetAutoIncrement(fieldValue.boolValue); + } else if (errCode != -E_NOT_FOUND) { + return errCode; + } + return E_OK; +} + +int RelationalSchemaObject::ParseCheckTablePrimaryKey(const JsonObject &inJsonObject, TableInfo &resultTable) +{ + FieldValue fieldValue; + int errCode = GetMemberFromJsonObject(inJsonObject, "PRIMARY_KEY", FieldType::LEAF_FIELD_STRING, false, fieldValue); + if (errCode == E_OK) { + resultTable.SetPrimaryKey(fieldValue.stringValue); + } + return errCode; +} + +int RelationalSchemaObject::ParseCheckTableIndex(const JsonObject &inJsonObject, TableInfo &resultTable) +{ + if (!inJsonObject.IsFieldPathExist(FieldPath {"INDEX"})) { // INDEX is not necessary + return E_OK; + } + std::map tableFields; + int errCode = inJsonObject.GetSubFieldPathAndType(FieldPath {"INDEX"}, tableFields); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Get schema TABLES INDEX failed: %d.", errCode); + return -E_SCHEMA_PARSE_FAIL; + } + + for (const auto &field : tableFields) { + if (field.second != FieldType::LEAF_FIELD_ARRAY) { + LOGE("[RelationalSchema][Parse] Expect schema TABLES INDEX fieldType ARRAY but : %s.", + SchemaUtils::FieldTypeString(field.second).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + CompositeFields indexDefine; + errCode = inJsonObject.GetStringArrayByFieldPath(field.first, indexDefine); + if (errCode != E_OK) { + LOGE("[RelationalSchema][Parse] Get schema TABLES INDEX field value failed: %d.", errCode); + return -E_SCHEMA_PARSE_FAIL; + } + resultTable.AddIndexDefine(field.first[1], indexDefine); // 1 : second element in path + } + return E_OK; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/common/src/runtime_context.cpp b/mock/distributeddb/common/src/runtime_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9acf1e19cd5380d306ae5020b029c999d15e3036 --- /dev/null +++ b/mock/distributeddb/common/src/runtime_context.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "runtime_context_impl.h" +#include "version.h" +#include "log_print.h" + +namespace DistributedDB { +RuntimeContext *RuntimeContext::GetInstance() +{ + static char instMemory[sizeof(RuntimeContextImpl)]; + static std::mutex instLock_; + static std::atomic instPtr = nullptr; + // For Double-Checked Locking, we need check insPtr twice + if (instPtr == nullptr) { + std::lock_guard lock(instLock_); + if (instPtr == nullptr) { + // Use instMemory to make sure this singleton not free before other object. + // This operation needn't to malloc memory, we needn't to check nullptr. + instPtr = new (instMemory) RuntimeContextImpl; + LOGI("DistributedDB Version : %s", SOFTWARE_VERSION_STRING.c_str()); + } + } + return instPtr; +} +} // namespace DistributedDB + diff --git a/mock/distributeddb/common/src/runtime_context_impl.cpp b/mock/distributeddb/common/src/runtime_context_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..91a4b45dc23d867720359f374b7aeff84c88b2d8 --- /dev/null +++ b/mock/distributeddb/common/src/runtime_context_impl.cpp @@ -0,0 +1,673 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "runtime_context_impl.h" +#include "db_errno.h" +#include "db_dfx_adapter.h" +#include "log_print.h" +#include "communicator_aggregator.h" +#include "network_adapter.h" + +namespace DistributedDB { +RuntimeContextImpl::RuntimeContextImpl() + : adapter_(nullptr), + communicatorAggregator_(nullptr), + mainLoop_(nullptr), + currentTimerId_(0), + taskPool_(nullptr), + taskPoolReportsTimerId_(0), + timeTickMonitor_(nullptr), + systemApiAdapter_(nullptr), + lockStatusObserver_(nullptr), + currentSessionId_(1) +{ +} + +// Destruct the object. +RuntimeContextImpl::~RuntimeContextImpl() +{ + if (taskPoolReportsTimerId_ > 0) { + RemoveTimer(taskPoolReportsTimerId_, true); + taskPoolReportsTimerId_ = 0; + } + if (taskPool_ != nullptr) { + taskPool_->Stop(); + taskPool_->Release(taskPool_); + taskPool_ = nullptr; + } + if (mainLoop_ != nullptr) { + mainLoop_->KillAndDecObjRef(mainLoop_); + mainLoop_ = nullptr; + } + SetCommunicatorAggregator(nullptr); + (void)SetCommunicatorAdapter(nullptr); + systemApiAdapter_ = nullptr; + delete lockStatusObserver_; + lockStatusObserver_ = nullptr; + userChangeMonitor_ = nullptr; +} + +// Set the label of this process. +void RuntimeContextImpl::SetProcessLabel(const std::string &label) +{ + std::lock_guard labelLock(labelMutex_); + processLabel_ = label; +} + +std::string RuntimeContextImpl::GetProcessLabel() const +{ + std::lock_guard labelLock(labelMutex_); + return processLabel_; +} + +int RuntimeContextImpl::SetCommunicatorAdapter(IAdapter *adapter) +{ + { + std::lock_guard autoLock(communicatorLock_); + if (adapter_ != nullptr) { + if (communicatorAggregator_ != nullptr) { + return -E_NOT_SUPPORT; + } + delete adapter_; + } + adapter_ = adapter; + } + ICommunicatorAggregator *communicatorAggregator = nullptr; + GetCommunicatorAggregator(communicatorAggregator); + autoLaunch_.SetCommunicatorAggregator(communicatorAggregator); + return E_OK; +} + +int RuntimeContextImpl::GetCommunicatorAggregator(ICommunicatorAggregator *&outAggregator) +{ + outAggregator = nullptr; + std::lock_guard lock(communicatorLock_); + if (communicatorAggregator_ != nullptr) { + outAggregator = communicatorAggregator_; + return E_OK; + } + + if (adapter_ == nullptr) { + LOGE("Adapter has not set!"); + return -E_NOT_INIT; + } + + communicatorAggregator_ = new (std::nothrow) CommunicatorAggregator; + if (communicatorAggregator_ == nullptr) { + LOGE("CommunicatorAggregator create failed, may be no available memory!"); + return -E_OUT_OF_MEMORY; + } + + int errCode = communicatorAggregator_->Initialize(adapter_); + if (errCode != E_OK) { + LOGE("CommunicatorAggregator init failed, err = %d!", errCode); + RefObject::KillAndDecObjRef(communicatorAggregator_); + communicatorAggregator_ = nullptr; + } + outAggregator = communicatorAggregator_; + return errCode; +} + +void RuntimeContextImpl::SetCommunicatorAggregator(ICommunicatorAggregator *inAggregator) +{ + std::lock_guard autoLock(communicatorLock_); + if (communicatorAggregator_ != nullptr) { + autoLaunch_.SetCommunicatorAggregator(nullptr); + communicatorAggregator_->Finalize(); + RefObject::KillAndDecObjRef(communicatorAggregator_); + } + communicatorAggregator_ = inAggregator; + autoLaunch_.SetCommunicatorAggregator(communicatorAggregator_); +} + +int RuntimeContextImpl::GetLocalIdentity(std::string &outTarget) +{ + std::lock_guard autoLock(communicatorLock_); + if (communicatorAggregator_ != nullptr) { + return communicatorAggregator_->GetLocalIdentity(outTarget); + } + return -E_NOT_INIT; +} + +// Add and start a timer. +int RuntimeContextImpl::SetTimer(int milliSeconds, const TimerAction &action, + const TimerFinalizer &finalizer, TimerId &timerId) +{ + timerId = 0; + if ((milliSeconds < 0) || !action) { + return -E_INVALID_ARGS; + } + + IEventLoop *loop = nullptr; + int errCode = PrepareLoop(loop); + if (errCode != E_OK) { + LOGE("SetTimer(), prepare loop failed."); + return errCode; + } + + IEvent *evTimer = IEvent::CreateEvent(milliSeconds, errCode); + if (evTimer == nullptr) { + loop->DecObjRef(loop); + loop = nullptr; + return errCode; + } + + errCode = AllocTimerId(evTimer, timerId); + if (errCode != E_OK) { + evTimer->DecObjRef(evTimer); + evTimer = nullptr; + loop->DecObjRef(loop); + loop = nullptr; + return errCode; + } + + evTimer->SetAction([this, timerId, action](EventsMask revents) -> int { + int errCodeInner = action(timerId); + if (errCodeInner != E_OK) { + RemoveTimer(timerId, false); + } + return errCodeInner; + }, + finalizer); + + errCode = loop->Add(evTimer); + if (errCode != E_OK) { + evTimer->IgnoreFinalizer(); + RemoveTimer(timerId, false); + timerId = 0; + } + + loop->DecObjRef(loop); + loop = nullptr; + return errCode; +} + +// Modify the interval of the timer. +int RuntimeContextImpl::ModifyTimer(TimerId timerId, int milliSeconds) +{ + if (milliSeconds < 0) { + return -E_INVALID_ARGS; + } + + std::lock_guard autoLock(timersLock_); + auto iter = timers_.find(timerId); + if (iter == timers_.end()) { + return -E_NO_SUCH_ENTRY; + } + + IEvent *evTimer = iter->second; + if (evTimer == nullptr) { + return -E_INTERNAL_ERROR; + } + return evTimer->SetTimeout(milliSeconds); +} + +// Remove the timer. +void RuntimeContextImpl::RemoveTimer(TimerId timerId, bool wait) +{ + IEvent *evTimer = nullptr; + { + std::lock_guard autoLock(timersLock_); + auto iter = timers_.find(timerId); + if (iter == timers_.end()) { + return; + } + evTimer = iter->second; + timers_.erase(iter); + } + + if (evTimer != nullptr) { + evTimer->Detach(wait); + evTimer->DecObjRef(evTimer); + evTimer = nullptr; + } +} + +// Task interfaces. +int RuntimeContextImpl::ScheduleTask(const TaskAction &task) +{ + std::lock_guard autoLock(taskLock_); + int errCode = PrepareTaskPool(); + if (errCode != E_OK) { + LOGE("Schedule task failed, fail to prepare task pool."); + return errCode; + } + return taskPool_->Schedule(task); +} + +int RuntimeContextImpl::ScheduleQueuedTask(const std::string &queueTag, + const TaskAction &task) +{ + std::lock_guard autoLock(taskLock_); + int errCode = PrepareTaskPool(); + if (errCode != E_OK) { + LOGE("Schedule queued task failed, fail to prepare task pool."); + return errCode; + } + return taskPool_->Schedule(queueTag, task); +} + +void RuntimeContextImpl::ShrinkMemory(const std::string &description) +{ + std::lock_guard autoLock(taskLock_); + if (taskPool_ != nullptr) { + taskPool_->ShrinkMemory(description); + } +} + +NotificationChain::Listener *RuntimeContextImpl::RegisterTimeChangedLister(const TimeChangedAction &action, + int &errCode) +{ + std::lock_guard autoLock(timeTickMonitorLock_); + if (timeTickMonitor_ == nullptr) { + timeTickMonitor_ = std::make_unique(); + errCode = timeTickMonitor_->Start(); + if (errCode != E_OK) { + LOGE("TimeTickMonitor start failed!"); + timeTickMonitor_ = nullptr; + return nullptr; + } + } + return timeTickMonitor_->RegisterTimeChangedLister(action, errCode); +} + +int RuntimeContextImpl::PrepareLoop(IEventLoop *&loop) +{ + std::lock_guard autoLock(loopLock_); + if (mainLoop_ != nullptr) { + loop = mainLoop_; + loop->IncObjRef(loop); // ref 1 returned to caller. + return E_OK; + } + + int errCode = E_OK; + loop = IEventLoop::CreateEventLoop(errCode); + if (loop == nullptr) { + return errCode; + } + + loop->IncObjRef(loop); // ref 1 owned by thread. + std::thread loopThread([loop]() { + loop->Run(); + loop->DecObjRef(loop); // ref 1 dropped by thread. + }); + loopThread.detach(); + + mainLoop_ = loop; + loop->IncObjRef(loop); // ref 1 returned to caller. + return E_OK; +} + +int RuntimeContextImpl::PrepareTaskPool() +{ + if (taskPool_ != nullptr) { + return E_OK; + } + + int errCode = E_OK; + TaskPool *taskPool = TaskPool::Create(MAX_TP_THREADS, MIN_TP_THREADS, errCode); + if (taskPool == nullptr) { + return errCode; + } + + errCode = taskPool->Start(); + if (errCode != E_OK) { + taskPool->Release(taskPool); + return errCode; + } + + taskPool_ = taskPool; + return E_OK; +} + +int RuntimeContextImpl::AllocTimerId(IEvent *evTimer, TimerId &timerId) +{ + if (evTimer == nullptr) { + return -E_INVALID_ARGS; + } + + std::lock_guard autoLock(timersLock_); + TimerId startId = currentTimerId_; + while (++currentTimerId_ != startId) { + if (currentTimerId_ == 0) { + continue; + } + if (timers_.find(currentTimerId_) == timers_.end()) { + timerId = currentTimerId_; + timers_[timerId] = evTimer; + return E_OK; + } + } + return -E_OUT_OF_IDS; +} + +int RuntimeContextImpl::SetPermissionCheckCallback(const PermissionCheckCallback &callback) +{ + std::unique_lock writeLock(permissionCheckCallbackMutex_); + permissionCheckCallback_ = callback; + LOGI("SetPermissionCheckCallback ok"); + return E_OK; +} + +int RuntimeContextImpl::SetPermissionCheckCallback(const PermissionCheckCallbackV2 &callback) +{ + std::unique_lock writeLock(permissionCheckCallbackMutex_); + permissionCheckCallbackV2_ = callback; + LOGI("SetPermissionCheckCallback V2 ok"); + return E_OK; +} + +int RuntimeContextImpl::RunPermissionCheck(const std::string &userId, const std::string &appId, + const std::string &storeId, const std::string &deviceId, uint8_t flag) const +{ + bool checkResult = false; + std::shared_lock autoLock(permissionCheckCallbackMutex_); + if (permissionCheckCallbackV2_) { + checkResult = permissionCheckCallbackV2_(userId, appId, storeId, deviceId, flag); + if (checkResult) { + return E_OK; + } else { + return -E_NOT_PERMIT; + } + } else if (permissionCheckCallback_) { + checkResult = permissionCheckCallback_(userId, appId, storeId, flag); + if (checkResult) { + return E_OK; + } else { + return -E_NOT_PERMIT; + } + } else { + return E_OK; + } +} + +int RuntimeContextImpl::EnableKvStoreAutoLaunch(const KvDBProperties &properties, AutoLaunchNotifier notifier, + const AutoLaunchOption &option) +{ + return autoLaunch_.EnableKvStoreAutoLaunch(properties, notifier, option); +} + +int RuntimeContextImpl::DisableKvStoreAutoLaunch(const std::string &normalIdentifier, + const std::string &dualTupleIdentifier, const std::string &userId) +{ + return autoLaunch_.DisableKvStoreAutoLaunch(normalIdentifier, dualTupleIdentifier, userId); +} + +void RuntimeContextImpl::GetAutoLaunchSyncDevices(const std::string &identifier, + std::vector &devices) const +{ + return autoLaunch_.GetAutoLaunchSyncDevices(identifier, devices); +} + +void RuntimeContextImpl::SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback, DBType type) +{ + autoLaunch_.SetAutoLaunchRequestCallback(callback, type); +} + +NotificationChain::Listener *RuntimeContextImpl::RegisterLockStatusLister(const LockStatusNotifier &action, + int &errCode) +{ + std::lock(lockStatusLock_, systemApiAdapterLock_); + std::lock_guard lockStatusLock(lockStatusLock_, std::adopt_lock); + std::lock_guard systemApiAdapterLock(systemApiAdapterLock_, std::adopt_lock); + if (lockStatusObserver_ == nullptr) { + lockStatusObserver_ = new (std::nothrow) LockStatusObserver(); + if (lockStatusObserver_ == nullptr) { + LOGE("lockStatusObserver_ is nullptr"); + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + } + + if (!lockStatusObserver_->IsStarted()) { + errCode = lockStatusObserver_->Start(); + if (errCode != E_OK) { + LOGE("lockStatusObserver start failed, err = %d", errCode); + delete lockStatusObserver_; + lockStatusObserver_ = nullptr; + return nullptr; + } + + if (systemApiAdapter_ != nullptr) { + auto callback = std::bind(&LockStatusObserver::OnStatusChange, + lockStatusObserver_, std::placeholders::_1); + errCode = systemApiAdapter_->RegOnAccessControlledEvent(callback); + if (errCode != OK) { + LOGE("Register access control event change failed, err = %d", errCode); + delete lockStatusObserver_; + lockStatusObserver_ = nullptr; + return nullptr; + } + } + } + + NotificationChain::Listener *listener = lockStatusObserver_->RegisterLockStatusChangedLister(action, errCode); + if ((listener == nullptr) || (errCode != E_OK)) { + LOGE("Register lock status changed listener failed, err = %d", errCode); + delete lockStatusObserver_; + lockStatusObserver_ = nullptr; + return nullptr; + } + return listener; +} + +bool RuntimeContextImpl::IsAccessControlled() const +{ + std::lock_guard autoLock(systemApiAdapterLock_); + if (systemApiAdapter_ == nullptr) { + return false; + } + return systemApiAdapter_->IsAccessControlled(); +} + +int RuntimeContextImpl::SetSecurityOption(const std::string &filePath, const SecurityOption &option) const +{ + std::lock_guard autoLock(systemApiAdapterLock_); + if (systemApiAdapter_ == nullptr || !OS::CheckPathExistence(filePath)) { + LOGI("Adapter is not set, or path not existed, not support set security option!"); + return -E_NOT_SUPPORT; + } + + if (option == SecurityOption()) { + LOGD("SecurityOption is NOT_SET,Not need to set security option!"); + return E_OK; + } + + std::string fileRealPath; + int errCode = OS::GetRealPath(filePath, fileRealPath); + if (errCode != E_OK) { + LOGE("Get real path failed when set security option!"); + return errCode; + } + + errCode = systemApiAdapter_->SetSecurityOption(fileRealPath, option); + if (errCode != OK) { + if (errCode == NOT_SUPPORT) { + return -E_NOT_SUPPORT; + } + LOGE("SetSecurityOption failed, errCode = %d", errCode); + return -E_SYSTEM_API_ADAPTER_CALL_FAILED; + } + return E_OK; +} + +int RuntimeContextImpl::GetSecurityOption(const std::string &filePath, SecurityOption &option) const +{ + std::lock_guard autoLock(systemApiAdapterLock_); + if (systemApiAdapter_ == nullptr) { + LOGI("Get Security option, but not set system api adapter!"); + return -E_NOT_SUPPORT; + } + int errCode = systemApiAdapter_->GetSecurityOption(filePath, option); + if (errCode != OK) { + if (errCode == NOT_SUPPORT) { + return -E_NOT_SUPPORT; + } + LOGE("GetSecurityOption failed, errCode = %d", errCode); + return -E_SYSTEM_API_ADAPTER_CALL_FAILED; + } + + LOGD("Get security option from system adapter [%d, %d]", option.securityLabel, option.securityFlag); + // This interface may return success but failed to obtain the flag and modified it to -1 + if (option.securityFlag == INVALID_SEC_FLAG) { + // Currently ignoring the failure to obtain flags -1 other than S3, modify the flag to the default value + if (option.securityLabel == S3) { + LOGE("GetSecurityOption failed, SecurityOption is invalid [3, -1]!"); + return -E_SYSTEM_API_ADAPTER_CALL_FAILED; + } + option.securityFlag = 0; // 0 is default value + } + return E_OK; +} + +bool RuntimeContextImpl::CheckDeviceSecurityAbility(const std::string &devId, const SecurityOption &option) const +{ + std::lock_guard autoLock(systemApiAdapterLock_); + if (systemApiAdapter_ == nullptr) { + return true; + } + return systemApiAdapter_->CheckDeviceSecurityAbility(devId, option); +} + +int RuntimeContextImpl::SetProcessSystemApiAdapter(const std::shared_ptr &adapter) +{ + std::lock(lockStatusLock_, systemApiAdapterLock_); + std::lock_guard lockStatusLock(lockStatusLock_, std::adopt_lock); + std::lock_guard systemApiAdapterLock(systemApiAdapterLock_, std::adopt_lock); + systemApiAdapter_ = adapter; + if (systemApiAdapter_ != nullptr && lockStatusObserver_ != nullptr && lockStatusObserver_->IsStarted()) { + auto callback = std::bind(&LockStatusObserver::OnStatusChange, + lockStatusObserver_, std::placeholders::_1); + int errCode = systemApiAdapter_->RegOnAccessControlledEvent(callback); + if (errCode != OK) { + LOGE("Register access controlled event failed while setting adapter, err = %d", errCode); + delete lockStatusObserver_; + lockStatusObserver_ = nullptr; + return -E_SYSTEM_API_ADAPTER_CALL_FAILED; + } + } + return E_OK; +} + +bool RuntimeContextImpl::IsProcessSystemApiAdapterValid() const +{ + std::lock_guard autoLock(systemApiAdapterLock_); + return (systemApiAdapter_ != nullptr); +} + +void RuntimeContextImpl::NotifyTimestampChanged(TimeOffset offset) const +{ + std::lock_guard autoLock(timeTickMonitorLock_); + if (timeTickMonitor_ == nullptr) { + LOGD("NotifyTimestampChanged fail, timeTickMonitor_ is null."); + return; + } + timeTickMonitor_->NotifyTimeChange(offset); +} + +bool RuntimeContextImpl::IsCommunicatorAggregatorValid() const +{ + std::lock_guard autoLock(communicatorLock_); + if (communicatorAggregator_ == nullptr && adapter_ == nullptr) { + return false; + } + return true; +} + +void RuntimeContextImpl::SetStoreStatusNotifier(const StoreStatusNotifier ¬ifier) +{ + std::unique_lock writeLock(databaseStatusCallbackMutex_); + databaseStatusNotifyCallback_ = notifier; + LOGI("SetStoreStatusNotifier ok"); +} + +void RuntimeContextImpl::NotifyDatabaseStatusChange(const std::string &userId, const std::string &appId, + const std::string &storeId, const std::string &deviceId, bool onlineStatus) +{ + ScheduleTask([this, userId, appId, storeId, deviceId, onlineStatus] { + std::shared_lock autoLock(databaseStatusCallbackMutex_); + if (databaseStatusNotifyCallback_) { + LOGI("start notify database status:%d", onlineStatus); + databaseStatusNotifyCallback_(userId, appId, storeId, deviceId, onlineStatus); + } + }); +} + +int RuntimeContextImpl::SetSyncActivationCheckCallback(const SyncActivationCheckCallback &callback) +{ + std::unique_lock writeLock(syncActivationCheckCallbackMutex_); + syncActivationCheckCallback_ = callback; + LOGI("SetSyncActivationCheckCallback ok"); + return E_OK; +} + +bool RuntimeContextImpl::IsSyncerNeedActive(std::string &userId, std::string &appId, std::string &storeId) const +{ + std::shared_lock autoLock(syncActivationCheckCallbackMutex_); + if (syncActivationCheckCallback_) { + return syncActivationCheckCallback_(userId, appId, storeId); + } + return true; +} + +NotificationChain::Listener *RuntimeContextImpl::RegisterUserChangedListerner(const UserChangedAction &action, + EventType event) +{ + int errCode; + std::lock_guard autoLock(userChangeMonitorLock_); + if (userChangeMonitor_ == nullptr) { + userChangeMonitor_ = std::make_unique(); + errCode = userChangeMonitor_->Start(); + if (errCode != E_OK) { + LOGE("UserChangeMonitor start failed!"); + userChangeMonitor_ = nullptr; + return nullptr; + } + } + NotificationChain::Listener *listener = userChangeMonitor_->RegisterUserChangedListerner(action, event, errCode); + if ((listener == nullptr) || (errCode != E_OK)) { + LOGE("Register user status changed listener failed, err = %d", errCode); + return nullptr; + } + return listener; +} + +int RuntimeContextImpl::NotifyUserChanged() const +{ + { + std::lock_guard autoLock(userChangeMonitorLock_); + if (userChangeMonitor_ == nullptr) { + LOGD("userChangeMonitor is null, all db is in normal sync mode"); + return E_OK; + } + } + userChangeMonitor_->NotifyUserChanged(); + return E_OK; +} + +uint32_t RuntimeContextImpl::GenerateSessionId() +{ + uint32_t sessionId = currentSessionId_++; + if (sessionId == 0) { + sessionId = currentSessionId_++; + } + return sessionId; +} + +void RuntimeContextImpl::DumpCommonInfo(int fd) +{ + autoLaunch_.Dump(fd); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/runtime_context_impl.h b/mock/distributeddb/common/src/runtime_context_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..c36fe9a1d472ff3ad086d9903f552f8c5c69c4be --- /dev/null +++ b/mock/distributeddb/common/src/runtime_context_impl.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_CONTEXT_IMPL_H +#define RUNTIME_CONTEXT_IMPL_H + +#include +#include +#include + +#include "runtime_context.h" +#include "task_pool.h" +#include "evloop/include/ievent.h" +#include "evloop/include/ievent_loop.h" +#include "lock_status_observer.h" +#include "time_tick_monitor.h" +#include "icommunicator_aggregator.h" +#include "auto_launch.h" +#include "user_change_monitor.h" + +namespace DistributedDB { +class RuntimeContextImpl final : public RuntimeContext { +public: + RuntimeContextImpl(); + ~RuntimeContextImpl() override; + + // Get/Set the label of this process. + void SetProcessLabel(const std::string &label) override; + std::string GetProcessLabel() const override; + int SetCommunicatorAdapter(IAdapter *adapter) override; + int GetCommunicatorAggregator(ICommunicatorAggregator *&outAggregator) override; + void SetCommunicatorAggregator(ICommunicatorAggregator *inAggregator) override; + int GetLocalIdentity(std::string &outTarget) override; + // Add and start a timer. + int SetTimer(int milliSeconds, const TimerAction &action, + const TimerFinalizer &finalizer, TimerId &timerId) override; + + // Modify the interval of the timer. + int ModifyTimer(TimerId timerId, int milliSeconds) override; + + // Remove the timer. + void RemoveTimer(TimerId timerId, bool wait) override; + + // Task interfaces. + int ScheduleTask(const TaskAction &task) override; + int ScheduleQueuedTask(const std::string &queueTag, const TaskAction &task) override; + + // Shrink as much memory as possible. + void ShrinkMemory(const std::string &description) override; + + // Register a time changed lister, it will be callback when local time changed. + NotificationChain::Listener *RegisterTimeChangedLister(const TimeChangedAction &action, int &errCode) override; + + int SetPermissionCheckCallback(const PermissionCheckCallback &callback) override; + + int SetPermissionCheckCallback(const PermissionCheckCallbackV2 &callback) override; + + int RunPermissionCheck(const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) const override; + + int EnableKvStoreAutoLaunch(const KvDBProperties &properties, AutoLaunchNotifier notifier, + const AutoLaunchOption &option) override; + + int DisableKvStoreAutoLaunch(const std::string &normalIdentifier, const std::string &dualTupleIdentifier, + const std::string &userId) override; + + void GetAutoLaunchSyncDevices(const std::string &identifier, std::vector &devices) const override; + + void SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback, DBType type) override; + + NotificationChain::Listener *RegisterLockStatusLister(const LockStatusNotifier &action, int &errCode) override; + + bool IsAccessControlled() const override; + + int SetSecurityOption(const std::string &filePath, const SecurityOption &option) const override; + + int GetSecurityOption(const std::string &filePath, SecurityOption &option) const override; + + bool CheckDeviceSecurityAbility(const std::string &devId, const SecurityOption &option) const override; + + int SetProcessSystemApiAdapter(const std::shared_ptr &adapter) override; + + bool IsProcessSystemApiAdapterValid() const override; + + bool IsCommunicatorAggregatorValid() const override; + + // Notify TIME_CHANGE_EVENT. + void NotifyTimestampChanged(TimeOffset offset) const override; + + void SetStoreStatusNotifier(const StoreStatusNotifier ¬ifier) override; + + void NotifyDatabaseStatusChange(const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, bool onlineStatus) override; + + int SetSyncActivationCheckCallback(const SyncActivationCheckCallback &callback) override; + + bool IsSyncerNeedActive(std::string &userId, std::string &appId, std::string &storeId) const override; + + // Register a user changed lister, it will be callback when user change. + NotificationChain::Listener *RegisterUserChangedListerner(const UserChangedAction &action, + EventType event) override; + // Notify TIME_CHANGE_EVENT. + int NotifyUserChanged() const override; + + uint32_t GenerateSessionId() override; + + void DumpCommonInfo(int fd) override; +private: + static constexpr int MAX_TP_THREADS = 10; // max threads of the task pool. + static constexpr int MIN_TP_THREADS = 1; // min threads of the task pool. + static constexpr int TASK_POOL_REPORTS_INTERVAL = 10000; // task pool reports its state every 10 seconds. + + int PrepareLoop(IEventLoop *&loop); + int PrepareTaskPool(); + int AllocTimerId(IEvent *evTimer, TimerId &timerId); + + // Context fields + mutable std::mutex labelMutex_; + std::string processLabel_; + + // Communicator + mutable std::mutex communicatorLock_; + IAdapter *adapter_; + ICommunicatorAggregator *communicatorAggregator_; + + // Loop and timer + mutable std::mutex loopLock_; + IEventLoop *mainLoop_; + std::mutex timersLock_; + TimerId currentTimerId_; + std::map timers_; + + // Task pool + std::mutex taskLock_; + TaskPool *taskPool_; + TimerId taskPoolReportsTimerId_; + + // TimeTick + mutable std::mutex timeTickMonitorLock_; + std::unique_ptr timeTickMonitor_; + + mutable std::shared_mutex permissionCheckCallbackMutex_{}; + PermissionCheckCallback permissionCheckCallback_; + PermissionCheckCallbackV2 permissionCheckCallbackV2_; + + AutoLaunch autoLaunch_; + + // System api + mutable std::recursive_mutex systemApiAdapterLock_; + std::shared_ptr systemApiAdapter_; + mutable std::mutex lockStatusLock_; // Mutex for lockStatusObserver_. + LockStatusObserver *lockStatusObserver_; + + mutable std::shared_mutex databaseStatusCallbackMutex_{}; + StoreStatusNotifier databaseStatusNotifyCallback_; + + mutable std::shared_mutex syncActivationCheckCallbackMutex_{}; + SyncActivationCheckCallback syncActivationCheckCallback_; + + mutable std::mutex userChangeMonitorLock_; + std::unique_ptr userChangeMonitor_; + + std::atomic currentSessionId_; +}; +} // namespace DistributedDB + +#endif // RUNTIME_CONTEXT_IMPL_H diff --git a/mock/distributeddb/common/src/schema_constant.cpp b/mock/distributeddb/common/src/schema_constant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d833a7eeb3fd8f0bde96ffc744dacc29b5b6f776 --- /dev/null +++ b/mock/distributeddb/common/src/schema_constant.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "schema_constant.h" + +namespace DistributedDB { +const std::string SchemaConstant::KEYWORD_SCHEMA_VERSION = "SCHEMA_VERSION"; +const std::string SchemaConstant::KEYWORD_SCHEMA_MODE = "SCHEMA_MODE"; +const std::string SchemaConstant::KEYWORD_SCHEMA_DEFINE = "SCHEMA_DEFINE"; +const std::string SchemaConstant::KEYWORD_SCHEMA_INDEXES = "SCHEMA_INDEXES"; +const std::string SchemaConstant::KEYWORD_SCHEMA_SKIPSIZE = "SCHEMA_SKIPSIZE"; +const std::string SchemaConstant::KEYWORD_SCHEMA_TYPE = "SCHEMA_TYPE"; +const std::string SchemaConstant::KEYWORD_SCHEMA_TABLE = "TABLES"; +const std::string SchemaConstant::KEYWORD_INDEX = "INDEX"; // For FlatBuffer-Schema + +const std::string SchemaConstant::KEYWORD_MODE_STRICT = "STRICT"; +const std::string SchemaConstant::KEYWORD_MODE_COMPATIBLE = "COMPATIBLE"; + +const std::string SchemaConstant::KEYWORD_TYPE_BOOL = "BOOL"; +const std::string SchemaConstant::KEYWORD_TYPE_INTEGER = "INTEGER"; +const std::string SchemaConstant::KEYWORD_TYPE_LONG = "LONG"; +const std::string SchemaConstant::KEYWORD_TYPE_DOUBLE = "DOUBLE"; +const std::string SchemaConstant::KEYWORD_TYPE_STRING = "STRING"; + +const std::string SchemaConstant::KEYWORD_ATTR_NOT_NULL = "NOT NULL"; +const std::string SchemaConstant::KEYWORD_ATTR_DEFAULT = "DEFAULT"; +const std::string SchemaConstant::KEYWORD_ATTR_VALUE_NULL = "null"; +const std::string SchemaConstant::KEYWORD_ATTR_VALUE_TRUE = "true"; +const std::string SchemaConstant::KEYWORD_ATTR_VALUE_FALSE = "false"; + +const std::string SchemaConstant::KEYWORD_TYPE_RELATIVE = "RELATIVE"; +const std::string SchemaConstant::SCHEMA_SUPPORT_VERSION = "1.0"; +const std::string SchemaConstant::SCHEMA_SUPPORT_VERSION_V2 = "2.0"; + +const uint32_t SchemaConstant::SCHEMA_META_FEILD_COUNT_MAX = 5; +const uint32_t SchemaConstant::SCHEMA_META_FEILD_COUNT_MIN = 3; +const uint32_t SchemaConstant::SCHEMA_FEILD_NAME_LENGTH_MAX = 64; +const uint32_t SchemaConstant::SCHEMA_FEILD_NAME_LENGTH_MIN = 1; +const uint32_t SchemaConstant::SCHEMA_FEILD_NAME_COUNT_MAX = 256; +const uint32_t SchemaConstant::SCHEMA_FEILD_NAME_COUNT_MIN = 1; +const uint32_t SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX = 4; +const uint32_t SchemaConstant::SCHEMA_INDEX_COUNT_MAX = 32; +const uint32_t SchemaConstant::SCHEMA_STRING_SIZE_LIMIT = 524288; // 512K +const uint32_t SchemaConstant::SCHEMA_DEFAULT_STRING_SIZE_LIMIT = 4096; // 4K +const uint32_t SchemaConstant::SCHEMA_SKIPSIZE_MAX = 4194302; // 4M - 2 Bytes + +const uint32_t SchemaConstant::SECURE_BYTE_ALIGN = 8; // 8 bytes align +} // namespace DistributedDB + diff --git a/mock/distributeddb/common/src/schema_negotiate.cpp b/mock/distributeddb/common/src/schema_negotiate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ad031644b5b1720b922e8a816815af2f7c776281 --- /dev/null +++ b/mock/distributeddb/common/src/schema_negotiate.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "schema_negotiate.h" + +#include "log_print.h" +#include "schema_utils.h" + +namespace DistributedDB { +// Some principle in current version describe below. (Relative-type will be introduced in future but not involved now) +// 1. PermitSync: Be false may because schemaType-unrecognized, schemaType-different, schema-unparsable, +// schemaVersion-unrecognized, schema-incompatible, and so on. +// 2. RequirePeerConvert: Be true normally when permitSync false, for future possible sync and convert(by remote). +// 3. checkOnReceive: Be false when local is KV-DB, or when local is not KV-DB only if schema type equal as well as +// define equal or remote is the upgradation of local. +SyncOpinion SchemaNegotiate::MakeLocalSyncOpinion(const SchemaObject &localSchema, const std::string &remoteSchema, + uint8_t remoteSchemaType) +{ + SchemaType localType = localSchema.GetSchemaType(); // An invalid schemaObject will return SchemaType::NONE + SchemaType remoteType = ReadSchemaType(remoteSchemaType); + // Logic below only be correct in current version, should be redesigned if new type added in the future + // 1. If remote-type unrecognized(Include Relative-type), Do not permit sync. + if (remoteType == SchemaType::UNRECOGNIZED) { + LOGE("[Schema][Opinion] Remote-type=%" PRIu8 " unrecognized.", remoteSchemaType); + return SyncOpinion{false, true, true}; + } + // 2. If local-type is KV(Here remote-type is within recognized), Always permit sync. + if (localType == SchemaType::NONE) { + LOGI("[Schema][Opinion] Local-type KV."); + return SyncOpinion{true, false, false}; + } + // 3. If remote-type is KV(Here local-type can only be JSON or FLATBUFFER), Always permit sync but need check. + if (remoteType == SchemaType::NONE) { + LOGI("[Schema][Opinion] Remote-type KV."); + return SyncOpinion{true, false, true}; + } + // 4. If local-type differ with remote-type(Here both type can only be JSON or FLATBUFFER), Do not permit sync. + if (localType != remoteType) { + LOGE("[Schema][Opinion] Local-type=%s differ remote-type=%s.", SchemaUtils::SchemaTypeString(localType).c_str(), + SchemaUtils::SchemaTypeString(remoteType).c_str()); + return SyncOpinion{false, true, true}; + } + // 5. If schema parse fail, Do not permit sync. + SchemaObject remoteSchemaObj; + int errCode = remoteSchemaObj.ParseFromSchemaString(remoteSchema); + if (errCode != E_OK) { + LOGE("[Schema][Opinion] Parse remote-schema fail, errCode=%d, remote-type=%s.", errCode, + SchemaUtils::SchemaTypeString(remoteType).c_str()); + return SyncOpinion{false, true, true}; + } + // 6. If remote-schema is not incompatible based on local-schema(SchemaDefine Equal), Permit sync and don't check. + errCode = localSchema.CompareAgainstSchemaObject(remoteSchemaObj); + if (errCode != -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + return SyncOpinion{true, false, false}; + } + // 7. If local-schema is not incompatible based on remote-schema(Can only be COMPATIBLE_UPGRADE), Sync and check. + errCode = remoteSchemaObj.CompareAgainstSchemaObject(localSchema); + if (errCode != -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + return SyncOpinion{true, false, true}; + } + // 8. Local-schema incompatible with remote-schema mutually. + LOGE("[Schema][Opinion] Local-schema incompatible with remote-schema mutually."); + return SyncOpinion{false, true, true}; +} + +SyncStrategy SchemaNegotiate::ConcludeSyncStrategy(const SyncOpinion &localOpinion, const SyncOpinion &remoteOpinion) +{ + SyncStrategy outStrategy; + // Any side permit sync, the final conclusion is permit sync. + outStrategy.permitSync = (localOpinion.permitSync || remoteOpinion.permitSync); + bool convertConflict = (localOpinion.requirePeerConvert && remoteOpinion.requirePeerConvert); + if (convertConflict) { + outStrategy.permitSync = false; + } + // Responsible for conversion on send now that local do not require remote to do conversion + outStrategy.convertOnSend = (!localOpinion.requirePeerConvert); + // Responsible for conversion on receive since remote will not do conversion on send and require local to convert + outStrategy.convertOnReceive = remoteOpinion.requirePeerConvert; + // Only depend on local opinion + outStrategy.checkOnReceive = localOpinion.checkOnReceive; + LOGI("[Schema][Strategy] PermitSync=%d, SendConvert=%d, ReceiveConvert=%d, ReceiveCheck=%d.", + outStrategy.permitSync, outStrategy.convertOnSend, outStrategy.convertOnReceive, outStrategy.checkOnReceive); + return outStrategy; +} + +RelationalSyncOpinion SchemaNegotiate::MakeLocalSyncOpinion(const RelationalSchemaObject &localSchema, + const std::string &remoteSchema, uint8_t remoteSchemaType) +{ + SchemaType localType = localSchema.GetSchemaType(); + SchemaType remoteType = ReadSchemaType(remoteSchemaType); + if (remoteType == SchemaType::UNRECOGNIZED) { + LOGW("[RelationalSchema][opinion] Remote schema type %d is unrecognized.", remoteSchemaType); + return {}; + } + + if (remoteType != SchemaType::RELATIVE) { + LOGW("[RelationalSchema][opinion] Not support sync with schema type: local-type=[%s] remote-type=[%s]", + SchemaUtils::SchemaTypeString(localType).c_str(), SchemaUtils::SchemaTypeString(remoteType).c_str()); + return {}; + } + + if (!localSchema.IsSchemaValid()) { + LOGW("[RelationalSchema][opinion] Local schema is not valid"); + return {}; + } + + RelationalSchemaObject remoteSchemaObj; + int errCode = remoteSchemaObj.ParseFromSchemaString(remoteSchema); + if (errCode != E_OK) { + LOGW("[RelationalSchema][opinion] Parse remote schema failed %d, remote schema type %s", errCode, + SchemaUtils::SchemaTypeString(remoteType).c_str()); + return {}; + } + + RelationalSyncOpinion opinion; + for (const auto &it : localSchema.GetTables()) { + if (remoteSchemaObj.GetTable(it.first).GetTableName() != it.first) { + LOGW("[RelationalSchema][opinion] Table was missing in remote schema"); + continue; + } + // remote table is compatible(equal or upgrade) based on local table, permit sync and don't need check + errCode = it.second.CompareWithTable(remoteSchemaObj.GetTable(it.first)); + if (errCode != -E_RELATIONAL_TABLE_INCOMPATIBLE) { + opinion[it.first] = {true, false, false}; + continue; + } + // local table is compatible upgrade based on remote table, permit sync and need check + errCode = remoteSchemaObj.GetTable(it.first).CompareWithTable(it.second); + if (errCode != -E_RELATIONAL_TABLE_INCOMPATIBLE) { + opinion[it.first] = {true, false, true}; + continue; + } + // local table is incompatible with remote table mutually, don't permit sync and need check + LOGW("[RelationalSchema][opinion] Local table is incompatible with remote table mutually."); + opinion[it.first] = {false, true, true}; + } + + return opinion; +} + +RelationalSyncStrategy SchemaNegotiate::ConcludeSyncStrategy(const RelationalSyncOpinion &localOpinion, + const RelationalSyncOpinion &remoteOpinion) +{ + RelationalSyncStrategy syncStrategy; + for (const auto &itLocal : localOpinion) { + if (remoteOpinion.find(itLocal.first) == remoteOpinion.end()) { + LOGW("[RelationalSchema][Strategy] Table opinion is not found from remote."); + continue; + } + SyncOpinion localTableOpinion = itLocal.second; + SyncOpinion remoteTableOpinion = remoteOpinion.at(itLocal.first); + syncStrategy[itLocal.first] = ConcludeSyncStrategy(localTableOpinion, remoteTableOpinion); + } + + return syncStrategy; +} + +namespace { + const std::string MAGIC = "relational_opinion"; + const uint32_t SYNC_OPINION_VERSION = 1; +} // namespace + + +uint32_t SchemaNegotiate::CalculateParcelLen(const RelationalSyncOpinion &opinions) +{ + uint64_t len = Parcel::GetStringLen(MAGIC); + len += Parcel::GetUInt32Len(); + len += Parcel::GetUInt32Len(); + len = Parcel::GetEightByteAlign(len); + for (const auto &it : opinions) { + len += Parcel::GetStringLen(it.first); + len += Parcel::GetUInt32Len(); + len += Parcel::GetUInt32Len(); + len = Parcel::GetEightByteAlign(len); + } + if (len > UINT32_MAX) { + return 0; + } + return static_cast(len); +} + +int SchemaNegotiate::SerializeData(const RelationalSyncOpinion &opinions, Parcel &parcel) +{ + (void)parcel.WriteString(MAGIC); + (void)parcel.WriteUInt32(SYNC_OPINION_VERSION); + (void)parcel.WriteUInt32(static_cast(opinions.size())); + (void)parcel.EightByteAlign(); + for (const auto &it : opinions) { + (void)parcel.WriteString(it.first); + (void)parcel.WriteUInt32(it.second.permitSync); + (void)parcel.WriteUInt32(it.second.requirePeerConvert); + (void)parcel.EightByteAlign(); + } + return parcel.IsError() ? -E_INVALID_ARGS : E_OK; +} + +int SchemaNegotiate::DeserializeData(Parcel &parcel, RelationalSyncOpinion &opinion) +{ + if (!parcel.IsContinueRead()) { + return E_OK; + } + std::string magicStr; + (void)parcel.ReadString(magicStr); + if (magicStr != MAGIC) { + LOGE("Deserialize sync opinion failed while read MAGIC string [%s]", magicStr.c_str()); + return -E_INVALID_ARGS; + } + uint32_t version; + (void)parcel.ReadUInt32(version); + if (version != SYNC_OPINION_VERSION) { + LOGE("Not support sync opinion version: %u", version); + return -E_NOT_SUPPORT; + } + uint32_t opinionSize; + (void)parcel.ReadUInt32(opinionSize); + (void)parcel.EightByteAlign(); + static const uint32_t MAX_OPINION_SIZE = 1024; // max 1024 opinions + if (parcel.IsError() || opinionSize > MAX_OPINION_SIZE) { + return -E_INVALID_ARGS; + } + for (uint32_t i = 0; i < opinionSize; i++) { + std::string tableName; + SyncOpinion tableOpinion; + (void)parcel.ReadString(tableName); + uint32_t permitSync; + (void)parcel.ReadUInt32(permitSync); + tableOpinion.permitSync = static_cast(permitSync); + uint32_t requirePeerConvert; + (void)parcel.ReadUInt32(requirePeerConvert); + tableOpinion.requirePeerConvert = static_cast(requirePeerConvert); + (void)parcel.EightByteAlign(); + opinion[tableName] = tableOpinion; + } + return parcel.IsError() ? -E_INVALID_ARGS : E_OK; +} +} \ No newline at end of file diff --git a/mock/distributeddb/common/src/schema_object.cpp b/mock/distributeddb/common/src/schema_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8bb982d6168011786fcee723e06e03bb618efa3 --- /dev/null +++ b/mock/distributeddb/common/src/schema_object.cpp @@ -0,0 +1,1152 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "schema_object.h" +#include "schema_utils.h" +#include "db_errno.h" +#include "log_print.h" +#include "schema_constant.h" + +namespace DistributedDB { +namespace { +const std::string JSON_EXTRACT_FUNC_NAME = "json_extract_by_path"; +const std::string FLATBUFFER_EXTRACT_FUNC_NAME = "flatbuffer_extract_by_path"; + +// For Json-Schema, display its original content before parse. For FlatBuffer-Schema, only display its parsed content. +void DisplaySchemaLineByLine(SchemaType inType, const std::string &inSchema) +{ + constexpr uint32_t lengthPerLine = 400; // 400 char per line + constexpr uint32_t usualMaxLine = 25; // For normal schema, 25 line for 10k length is quite enough + LOGD("[Schema][Display] IS %s, LENGTH=%zu.", SchemaUtils::SchemaTypeString(inType).c_str(), inSchema.size()); + uint32_t totalLine = (inSchema.size() + lengthPerLine - 1) / lengthPerLine; + for (uint32_t line = 0; line < totalLine; line++) { + if (line >= usualMaxLine) { + LOGD("......(UNCOMPLETED SCHEMA)"); + break; + } + std::string lineStr = inSchema.substr(line * lengthPerLine, lengthPerLine); + LOGD("%s", lineStr.c_str()); + } +} +} + +std::string SchemaObject::GetExtractFuncName(SchemaType inSchemaType) +{ + if (inSchemaType == SchemaType::JSON) { + return JSON_EXTRACT_FUNC_NAME; + } else { + return FLATBUFFER_EXTRACT_FUNC_NAME; + } +} + +std::string SchemaObject::GenerateExtractSQL(SchemaType inSchemaType, const FieldPath &inFieldpath, + FieldType inFieldType, uint32_t skipSize, const std::string &accessStr) +{ + static std::map fieldTypeMapSQLiteType { + {FieldType::LEAF_FIELD_BOOL, "INT"}, + {FieldType::LEAF_FIELD_INTEGER, "INT"}, + {FieldType::LEAF_FIELD_LONG, "INT"}, + {FieldType::LEAF_FIELD_DOUBLE, "REAL"}, + {FieldType::LEAF_FIELD_STRING, "TEXT"}, + }; + if (inFieldpath.empty()) { + LOGE("[Schema][GenExtract] Path empty."); + return ""; + } + if (fieldTypeMapSQLiteType.count(inFieldType) == 0) { + LOGE("[Schema][GenExtract] FieldType not support."); + return ""; + } + std::string resultSql = " CAST("; // Reserve blank at begin for convenience. + resultSql += GetExtractFuncName(inSchemaType); + resultSql += "(" + accessStr + "value, '"; + resultSql += SchemaUtils::FieldPathString(inFieldpath); + resultSql += "', "; + resultSql += std::to_string(skipSize); + resultSql += ") AS "; + resultSql += fieldTypeMapSQLiteType[inFieldType]; + resultSql += ") "; // Reserve blank at end for convenience. + return resultSql; +} + +SchemaObject::SchemaObject() : flatbufferSchema_(*this) {}; + +SchemaObject::SchemaObject(const SchemaObject &other) + : flatbufferSchema_(*this) +{ + isValid_ = other.isValid_; + schemaType_ = other.schemaType_; + schemaString_ = other.schemaString_; + schemaVersion_ = other.schemaVersion_; + schemaMode_ = other.schemaMode_; + schemaSkipSize_ = other.schemaSkipSize_; + schemaIndexes_ = other.schemaIndexes_; + schemaDefine_ = other.schemaDefine_; +} + +SchemaObject& SchemaObject::operator=(const SchemaObject &other) +{ + if (&other != this) { + isValid_ = other.isValid_; + schemaType_ = other.schemaType_; + flatbufferSchema_.CopyFrom(other.flatbufferSchema_); + schemaString_ = other.schemaString_; + schemaVersion_ = other.schemaVersion_; + schemaMode_ = other.schemaMode_; + schemaSkipSize_ = other.schemaSkipSize_; + schemaIndexes_ = other.schemaIndexes_; + schemaDefine_ = other.schemaDefine_; + } + return *this; +} + +#ifdef RELATIONAL_STORE +SchemaObject::SchemaObject(const TableInfo &tableInfo) : flatbufferSchema_(*this) +{ + isValid_ = true; + schemaType_ = SchemaType::NONE; // Default NONE + schemaVersion_ = "1.0"; + SchemaDefine schemaDefine = tableInfo.GetSchemaDefine(); + schemaDefine_.insert({ 0, schemaDefine }); +} +#endif // RELATIONAL_STORE + +int SchemaObject::ParseFromSchemaString(const std::string &inSchemaString) +{ + if (isValid_) { + return -E_NOT_PERMIT; + } + + // Judge whether it is FlatBuffer-Schema then check the schema-size first + SchemaType estimateType = SchemaType::JSON; // Estimate as JSON type firstly + std::string decoded; + if (FlatBufferSchema::IsFlatBufferSchema(inSchemaString, decoded)) { + estimateType = SchemaType::FLATBUFFER; + LOGD("[Schema][Parse] FlatBuffer-Type, Decode before=%zu, after=%zu.", inSchemaString.size(), decoded.size()); + } + const std::string &oriSchema = ((estimateType == SchemaType::FLATBUFFER) ? decoded : inSchemaString); + if (oriSchema.size() > SchemaConstant::SCHEMA_STRING_SIZE_LIMIT) { + LOGE("[Schema][Parse] SchemaSize=%zu Too Large.", oriSchema.size()); + return -E_INVALID_ARGS; + } + + // Parse the corresponding type schema + if (estimateType == SchemaType::FLATBUFFER) { + int errCode = flatbufferSchema_.ParseFlatBufferSchema(oriSchema); + if (errCode != E_OK) { + return errCode; + } + DisplaySchemaLineByLine(SchemaType::FLATBUFFER, flatbufferSchema_.GetDescription()); + schemaType_ = SchemaType::FLATBUFFER; + schemaString_ = oriSchema; + } else { + DisplaySchemaLineByLine(SchemaType::JSON, oriSchema); + JsonObject schemaJson; + int errCode = schemaJson.Parse(oriSchema); + if (errCode != E_OK) { + LOGE("[Schema][Parse] Json parse schema fail, errCode=%d, Not FlatBuffer Not Json.", errCode); + return errCode; + } + errCode = ParseJsonSchema(schemaJson); + if (errCode != E_OK) { + return errCode; + } + schemaType_ = SchemaType::JSON; + schemaString_ = schemaJson.ToString(); // Save the minify type of version string + } + + isValid_ = true; + return E_OK; +} + +bool SchemaObject::IsSchemaValid() const +{ + return isValid_; +} + +SchemaType SchemaObject::GetSchemaType() const +{ + return schemaType_; +} + +std::string SchemaObject::ToSchemaString() const +{ + return schemaString_; +} + +uint32_t SchemaObject::GetSkipSize() const +{ + return schemaSkipSize_; +} + +std::map SchemaObject::GetIndexInfo() const +{ + if (!isValid_) { + // An invalid SchemaObject may contain some dirty info produced by failed parse. + return std::map(); + } + return schemaIndexes_; +} + +bool SchemaObject::IsIndexExist(const IndexName &indexName) const +{ + if (!isValid_) { + return false; + } + return (schemaIndexes_.count(indexName) != 0); +} + +int SchemaObject::CheckQueryableAndGetFieldType(const FieldPath &inPath, FieldType &outType) const +{ + if (inPath.empty()) { + return -E_INVALID_ARGS; + } + if (schemaDefine_.count(inPath.size() - 1) == 0) { + return -E_NOT_FOUND; + } + if (schemaDefine_.at(inPath.size() - 1).count(inPath) == 0) { + return -E_NOT_FOUND; + } + const SchemaAttribute &targetAttr = schemaDefine_.at(inPath.size() - 1).at(inPath); + outType = targetAttr.type; + return (targetAttr.isIndexable ? E_OK : -E_NOT_SUPPORT); +} + +int SchemaObject::CompareAgainstSchemaString(const std::string &inSchemaString) const +{ + IndexDifference indexDiffer; + return CompareAgainstSchemaString(inSchemaString, indexDiffer); +} + +int SchemaObject::CompareAgainstSchemaString(const std::string &inSchemaString, IndexDifference &indexDiffer) const +{ + if (!isValid_) { + return -E_NOT_PERMIT; + } + SchemaObject newSchema; + int errCode = newSchema.ParseFromSchemaString(inSchemaString); + if (errCode != E_OK) { + return errCode; + } + return CompareAgainstSchemaObject(newSchema, indexDiffer); +} + +int SchemaObject::CompareAgainstSchemaObject(const SchemaObject &inSchemaObject) const +{ + IndexDifference indexDiffer; + return CompareAgainstSchemaObject(inSchemaObject, indexDiffer); +} + +int SchemaObject::CompareAgainstSchemaObject(const SchemaObject &inSchemaObject, IndexDifference &indexDiffer) const +{ + if (!isValid_ || !inSchemaObject.isValid_) { + return -E_NOT_PERMIT; + } + if (schemaType_ != inSchemaObject.schemaType_) { + LOGE("[Schema][Compare] Self is %s, other is %s.", SchemaUtils::SchemaTypeString(schemaType_).c_str(), + SchemaUtils::SchemaTypeString(inSchemaObject.schemaType_).c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + + // Return E_SCHEMA_EQUAL_EXACTLY or E_SCHEMA_UNEQUAL_INCOMPATIBLE + int verModeResult = CompareSchemaVersionMode(inSchemaObject); + if (verModeResult == -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + return verModeResult; + } + + // Return E_SCHEMA_EQUAL_EXACTLY or E_SCHEMA_UNEQUAL_INCOMPATIBLE + int skipSizeResult = CompareSchemaSkipSize(inSchemaObject); + if (skipSizeResult == -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + return skipSizeResult; + } + + // Return E_SCHEMA_EQUAL_EXACTLY or E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE or E_SCHEMA_UNEQUAL_INCOMPATIBLE + int defineResult; + if (schemaType_ == SchemaType::JSON) { + defineResult = CompareSchemaDefine(inSchemaObject); + } else { + defineResult = flatbufferSchema_.CompareFlatBufferDefine(inSchemaObject.flatbufferSchema_); + } + if (defineResult == -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + return defineResult; + } + + // Return E_SCHEMA_EQUAL_EXACTLY or E_SCHEMA_UNEQUAL_COMPATIBLE + int indexResult = CompareSchemaIndexes(inSchemaObject, indexDiffer); + return ((defineResult == -E_SCHEMA_EQUAL_EXACTLY) ? indexResult : defineResult); +} + +int SchemaObject::CheckValueAndAmendIfNeed(ValueSource sourceType, ValueObject &inValue) const +{ + if (!isValid_ || schemaType_ != SchemaType::JSON) { // Currently this methed only support Json-Schema + return -E_NOT_PERMIT; + } + + std::set lackingPaths; + int errCode = CheckValue(inValue, lackingPaths); + if (errCode != -E_VALUE_MATCH) { + return errCode; + } + + bool amended = false; + errCode = AmendValueIfNeed(inValue, lackingPaths, amended); + if (errCode != E_OK) { // Unlikely + LOGE("[Schema][CheckAmend] Amend fail, errCode=%d, srcType=%d.", errCode, static_cast(sourceType)); + return -E_INTERNAL_ERROR; + } + return (amended ? -E_VALUE_MATCH_AMENDED : -E_VALUE_MATCH); +} + +int SchemaObject::VerifyValue(ValueSource sourceType, const Value &inValue) const +{ + return VerifyValue(sourceType, RawValue{inValue.data(), inValue.size()}); +} + +int SchemaObject::VerifyValue(ValueSource sourceType, const RawValue &inValue) const +{ + if (inValue.first == nullptr) { + return -E_INVALID_ARGS; + } + if (!isValid_ || schemaType_ != SchemaType::FLATBUFFER) { + return -E_NOT_PERMIT; + } + if (inValue.second <= schemaSkipSize_) { + LOGE("[Schema][Verify] Value length=%" PRIu32 " invalid, skipsize=%" PRIu32, inValue.second, schemaSkipSize_); + return -E_FLATBUFFER_VERIFY_FAIL; + } + + RawValue rawValue; + std::vector cache; + if (schemaSkipSize_ % SchemaConstant::SECURE_BYTE_ALIGN == 0) { + rawValue = {inValue.first + schemaSkipSize_, inValue.second - schemaSkipSize_}; + } else { + cache.assign(inValue.first + schemaSkipSize_, inValue.first + inValue.second); + rawValue = {cache.data(), cache.size()}; + } + + // Currently do not try no sizePrefix, future may depend on sourceType + int errCode = flatbufferSchema_.VerifyFlatBufferValue(rawValue, false); + if (errCode != E_OK) { + LOGE("[Schema][Verify] Value verify fail, srcType=%d.", static_cast(sourceType)); + return errCode; + } + return E_OK; +} + +int SchemaObject::ExtractValue(ValueSource sourceType, RawString inPath, const RawValue &inValue, + TypeValue &outExtract, std::vector *cache) const +{ + // NOTE!!! This function is performance sensitive !!! Carefully not to allocate memory often!!! + if (!isValid_ || schemaType_ != SchemaType::FLATBUFFER) { + return -E_NOT_PERMIT; + } + if (inPath == nullptr || inValue.first == nullptr) { + return -E_INVALID_ARGS; + } + if (inValue.second <= schemaSkipSize_) { + LOGE("[Schema][Extract] Value length=%u invalid, skipsize=%u.", inValue.second, schemaSkipSize_); + return -E_FLATBUFFER_VERIFY_FAIL; + } + + RawValue rawValue; + std::vector *tempCache = nullptr; // A temporary cache for use when input cache can not hold. + if (schemaSkipSize_ % SchemaConstant::SECURE_BYTE_ALIGN == 0) { + rawValue = {inValue.first + schemaSkipSize_, inValue.second - schemaSkipSize_}; + } else if ((cache != nullptr) && (cache->size() >= (inValue.second - schemaSkipSize_))) { + // Do not expand the cache if it can not hold + cache->assign(inValue.first + schemaSkipSize_, inValue.first + inValue.second); + rawValue = {cache->data(), inValue.second - schemaSkipSize_}; // Attention: Do not use cache.size() as second. + } else { + // Use a temporary cache, which will release its memory quickly + tempCache = new (std::nothrow) std::vector; + if (tempCache == nullptr) { + LOGE("[Schema][Extract] OOM."); + return -E_OUT_OF_MEMORY; + } + tempCache->resize(inValue.second - schemaSkipSize_); + tempCache->assign(inValue.first + schemaSkipSize_, inValue.first + inValue.second); + rawValue = {tempCache->data(), tempCache->size()}; + } + + // Currently do not try no sizePrefix, future may depend on sourceType + int errCode = flatbufferSchema_.ExtractFlatBufferValue(inPath, rawValue, outExtract, false); + if (errCode != E_OK) { + LOGE("[Schema][Extract] Fail, path=%s, srcType=%d.", inPath, static_cast(sourceType)); + } + delete tempCache; // delete nullptr is safe + tempCache = nullptr; + return errCode; +} + +int SchemaObject::ParseJsonSchema(const JsonObject &inJsonObject) +{ + // Parse and check mandatory metaField below + int errCode = CheckMetaFieldCountAndType(inJsonObject); + if (errCode != E_OK) { + return errCode; + } + errCode = ParseCheckSchemaVersionMode(inJsonObject); + if (errCode != E_OK) { + return errCode; + } + errCode = ParseCheckSchemaDefine(inJsonObject); + if (errCode != E_OK) { + return errCode; + } + // Parse and check optional metaField below + errCode = ParseCheckSchemaIndexes(inJsonObject); + if (errCode != E_OK) { + return errCode; + } + errCode = ParseCheckSchemaSkipSize(inJsonObject); + if (errCode != E_OK) { + return errCode; + } + return E_OK; +} + +namespace { +int CheckOptionalMetaFieldCountAndType(const std::map &metaFieldPathType) +{ + uint32_t indexMetaFieldCount = 0; + uint32_t skipSizeMetaFieldCount = 0; + if (metaFieldPathType.count(FieldPath{SchemaConstant::KEYWORD_SCHEMA_INDEXES}) != 0) { + indexMetaFieldCount++; + FieldType type = metaFieldPathType.at(FieldPath{SchemaConstant::KEYWORD_SCHEMA_INDEXES}); + if (type != FieldType::LEAF_FIELD_ARRAY) { + LOGE("[Schema][CheckMeta] Expect SCHEMA_INDEXES type ARRAY but %s.", + SchemaUtils::FieldTypeString(type).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + } + if (metaFieldPathType.count(FieldPath{SchemaConstant::KEYWORD_SCHEMA_SKIPSIZE}) != 0) { + skipSizeMetaFieldCount++; + FieldType type = metaFieldPathType.at(FieldPath{SchemaConstant::KEYWORD_SCHEMA_SKIPSIZE}); + if (type != FieldType::LEAF_FIELD_INTEGER) { + LOGE("[Schema][CheckMeta] Expect SCHEMA_SKIPSIZE type INTEGER but %s.", + SchemaUtils::FieldTypeString(type).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + } + if (metaFieldPathType.size() != (SchemaConstant::SCHEMA_META_FEILD_COUNT_MIN + indexMetaFieldCount + + skipSizeMetaFieldCount)) { + LOGE("[Schema][CheckMeta] Unrecognized metaField exist: total=%zu, indexField=%" PRIu32 ", skipSizeField=%" + PRIu32, metaFieldPathType.size(), indexMetaFieldCount, skipSizeMetaFieldCount); + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; +} +} + +int SchemaObject::CheckMetaFieldCountAndType(const JsonObject& inJsonObject) const +{ + std::map metaFieldPathType; + int errCode = inJsonObject.GetSubFieldPathAndType(FieldPath(), metaFieldPathType); + if (errCode != E_OK) { + LOGE("[Schema][CheckMeta] GetSubFieldPathAndType fail, errCode=%d.", errCode); + return errCode; + } + if (metaFieldPathType.size() < SchemaConstant::SCHEMA_META_FEILD_COUNT_MIN || + metaFieldPathType.size() > SchemaConstant::SCHEMA_META_FEILD_COUNT_MAX) { + LOGE("[Schema][CheckMeta] Unexpected metafield count=%zu.", metaFieldPathType.size()); + return -E_SCHEMA_PARSE_FAIL; + } + // Check KeyWord SCHEMA_VERSION + if (metaFieldPathType.count(FieldPath{SchemaConstant::KEYWORD_SCHEMA_VERSION}) == 0) { + LOGE("[Schema][CheckMeta] Expect metafield SCHEMA_VERSION but not find."); + return -E_SCHEMA_PARSE_FAIL; + } + FieldType type = metaFieldPathType.at(FieldPath{SchemaConstant::KEYWORD_SCHEMA_VERSION}); + if (type != FieldType::LEAF_FIELD_STRING) { + LOGE("[Schema][CheckMeta] Expect SCHEMA_VERSION type STRING but %s.", + SchemaUtils::FieldTypeString(type).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + // Check KeyWord SCHEMA_MODE + if (metaFieldPathType.count(FieldPath{SchemaConstant::KEYWORD_SCHEMA_MODE}) == 0) { + LOGE("[Schema][CheckMeta] Expect metafield SCHEMA_MODE but not find."); + return -E_SCHEMA_PARSE_FAIL; + } + type = metaFieldPathType.at(FieldPath{SchemaConstant::KEYWORD_SCHEMA_MODE}); + if (type != FieldType::LEAF_FIELD_STRING) { + LOGE("[Schema][CheckMeta] Expect SCHEMA_MODE type STRING but %s.", SchemaUtils::FieldTypeString(type).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + // Check KeyWord SCHEMA_DEFINE + if (metaFieldPathType.count(FieldPath{SchemaConstant::KEYWORD_SCHEMA_DEFINE}) == 0) { + LOGE("[Schema][CheckMeta] Expect metafield SCHEMA_DEFINE but not find."); + return -E_SCHEMA_PARSE_FAIL; + } + type = metaFieldPathType.at(FieldPath{SchemaConstant::KEYWORD_SCHEMA_DEFINE}); + if (type != FieldType::INTERNAL_FIELD_OBJECT) { // LEAF_FIELD_OBJECT indicate an empty object which is not allowed + LOGE("[Schema][CheckMeta] Expect SCHEMA_DEFINE type INTERNAL_OBJECT but %s.", + SchemaUtils::FieldTypeString(type).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + // Check KeyWord SCHEMA_INDEXES If Need + return CheckOptionalMetaFieldCountAndType(metaFieldPathType); +} + +int SchemaObject::ParseCheckSchemaVersionMode(const JsonObject& inJsonObject) +{ + // Note: it has been checked in CheckMetaFieldCountAndType that SCHEMA_VERSION field exists and its type is string. + FieldValue versionValue; + int errCode = inJsonObject.GetFieldValueByFieldPath(FieldPath{SchemaConstant::KEYWORD_SCHEMA_VERSION}, + versionValue); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + if (SchemaUtils::Strip(versionValue.stringValue) != SchemaConstant::SCHEMA_SUPPORT_VERSION) { + LOGE("[Schema][ParseVerMode] Unexpected SCHEMA_VERSION=%s.", versionValue.stringValue.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + schemaVersion_ = SchemaConstant::SCHEMA_SUPPORT_VERSION; + + // Note: it has been checked in CheckMetaFieldCountAndType that SCHEMA_MODE field exists and its type is string. + FieldValue modeValue; + errCode = inJsonObject.GetFieldValueByFieldPath(FieldPath{SchemaConstant::KEYWORD_SCHEMA_MODE}, modeValue); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + std::string modeStripped = SchemaUtils::Strip(modeValue.stringValue); + if (modeStripped != SchemaConstant::KEYWORD_MODE_STRICT && + modeStripped != SchemaConstant::KEYWORD_MODE_COMPATIBLE) { + LOGE("[Schema][ParseVerMode] Unexpected SCHEMA_MODE=%s.", modeValue.stringValue.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + schemaMode_ = ((modeStripped == SchemaConstant::KEYWORD_MODE_STRICT) ? SchemaMode::STRICT : SchemaMode::COMPATIBLE); + return E_OK; +} + +int SchemaObject::ParseCheckSchemaDefine(const JsonObject& inJsonObject) +{ + // Clear schemaDefine_ to recover from a fail parse + schemaDefine_.clear(); + // Note: it has been checked in CheckMetaFieldCountAndType that SCHEMA_DEFINE field exists and its type is + // internal-object. Nest path refer to those field with type internal object that has sub field. + std::set nestPathCurDepth{FieldPath{SchemaConstant::KEYWORD_SCHEMA_DEFINE}}; + uint32_t fieldNameCount = 0; + for (uint32_t depth = 0; depth < SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX; depth++) { + std::map subPathType; + int errCode = inJsonObject.GetSubFieldPathAndType(nestPathCurDepth, subPathType); + if (errCode != E_OK) { // Unlikely + LOGE("[Schema][ParseDefine] Internal Error: GetSubFieldPathAndType Fail, Depth=%u.", depth); + return -E_INTERNAL_ERROR; + } + fieldNameCount += subPathType.size(); + nestPathCurDepth.clear(); // Clear it for collecting new nestPath + for (const auto &subField : subPathType) { + SchemaAttribute attribute; + errCode = CheckSchemaDefineItemDecideAttribute(inJsonObject, subField.first, subField.second, attribute); + if (errCode != E_OK) { + LOGE("[Schema][ParseDefine] CheckSchemaDefineItemDecideAttribute Fail, Path=%s.", + SchemaUtils::FieldPathString(subField.first).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + // If everything ok, insert this schema item into schema define + // Remember to remove SCHEMA_DEFINE in the front of the fieldpath + schemaDefine_[depth][FieldPath(++(subField.first.begin()), subField.first.end())] = attribute; + // Deal with the nestpath and check depth limitation + if (subField.second == FieldType::INTERNAL_FIELD_OBJECT) { + if (depth == SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX - 1) { // Minus 1 to be the boundary + LOGE("[Schema][ParseDefine] Path=%s is INTERNAL_FIELD_OBJECT but reach schema depth limitation.", + SchemaUtils::FieldPathString(subField.first).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + nestPathCurDepth.insert(subField.first); + } + } + // If no deeper schema define, quit loop in advance + if (nestPathCurDepth.empty()) { + break; + } + } + if (fieldNameCount > SchemaConstant::SCHEMA_FEILD_NAME_COUNT_MAX) { + // Check Field Count Here + LOGE("[Schema][ParseDefine] FieldName count=%u exceed the limitation.", fieldNameCount); + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; +} + +int SchemaObject::CheckSchemaDefineItemDecideAttribute(const JsonObject& inJsonObject, const FieldPath &inPath, + FieldType inType, SchemaAttribute &outAttr) const +{ + // Note: inPath will never be an empty vector, internal logic guarantee it, see the caller logic + if (inPath.empty()) { // Not Possible. Just For Clear CodeDEX. + return -E_INTERNAL_ERROR; + } + int errCode = SchemaUtils::CheckFieldName(inPath.back()); + if (errCode != E_OK) { + LOGE("[Schema][CheckItemDecideAttr] Invalid fieldName=%s, errCode=%d.", inPath.back().c_str(), errCode); + return -E_SCHEMA_PARSE_FAIL; + } + if (inType == FieldType::LEAF_FIELD_STRING) { + FieldValue subFieldValue; + errCode = inJsonObject.GetFieldValueByFieldPath(inPath, subFieldValue); + if (errCode != E_OK) { // Unlikely + LOGE("[Schema][CheckItemDecideAttr] Internal Error: GetFieldValueByFieldPath Fail."); + return -E_INTERNAL_ERROR; + } + errCode = SchemaUtils::ParseAndCheckSchemaAttribute(subFieldValue.stringValue, outAttr); + if (errCode != E_OK) { + LOGE("[Schema][CheckItemDecideAttr] ParseAndCheckSchemaAttribute Fail, errCode=%d.", errCode); + return -E_SCHEMA_PARSE_FAIL; + } + // The ParseAndCheckSchemaAttribute do not cope with isIndexable field. Need to set it true here + outAttr.isIndexable = true; + } else if (inType == FieldType::LEAF_FIELD_ARRAY) { + uint32_t arraySize = 0; + errCode = inJsonObject.GetArraySize(inPath, arraySize); + if (errCode != E_OK) { + LOGE("[Schema][CheckItemDecideAttr] Internal Error: GetArraySize Fail."); + return -E_INTERNAL_ERROR; + } + if (arraySize != 0) { + LOGE("[Schema][CheckItemDecideAttr] Expect array empty but size=%u.", arraySize); + return -E_SCHEMA_PARSE_FAIL; + } + outAttr = SchemaAttribute{inType, false, false, false, FieldValue()}; + } else if (inType == FieldType::LEAF_FIELD_OBJECT) { + outAttr = SchemaAttribute{inType, false, false, false, FieldValue()}; + } else if (inType == FieldType::INTERNAL_FIELD_OBJECT) { + outAttr = SchemaAttribute{inType, false, false, false, FieldValue()}; // hasNotNull set false is OK for this + } else { + LOGE("[Schema][CheckItemDecideAttr] Unexpected FieldType=%s.", SchemaUtils::FieldTypeString(inType).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; +} + +int SchemaObject::ParseCheckSchemaIndexes(const JsonObject& inJsonObject) +{ + // Clear schemaIndexes_ to recover from a fail parse + schemaIndexes_.clear(); + // No SCHEMA_INDEXES field is allowed + if (!inJsonObject.IsFieldPathExist(FieldPath{SchemaConstant::KEYWORD_SCHEMA_INDEXES})) { + LOGD("[Schema][ParseIndex] No SCHEMA_INDEXES Field."); + return E_OK; + } + // The type of SCHEMA_INDEXES field has been checked in CheckMetaFieldCountAndType to be an array + // If not all members of the array are string type or string-array, this call will return error + std::vector> oriIndexArray; + int errCode = inJsonObject.GetArrayContentOfStringOrStringArray(FieldPath{SchemaConstant::KEYWORD_SCHEMA_INDEXES}, + oriIndexArray); + if (errCode != E_OK) { + LOGE("[Schema][ParseIndex] GetArrayContent Fail, errCode=%d.", errCode); + return -E_SCHEMA_PARSE_FAIL; + } + if (oriIndexArray.size() > SchemaConstant::SCHEMA_INDEX_COUNT_MAX) { + LOGE("[Schema][ParseIndex] Index(Ori) count=%zu exceed limitation.", oriIndexArray.size()); + return -E_SCHEMA_PARSE_FAIL; + } + for (const auto &entry : oriIndexArray) { + errCode = ParseCheckEachIndexFromStringArray(entry); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} + +int SchemaObject::ParseCheckSchemaSkipSize(const JsonObject& inJsonObject) +{ + // No SCHEMA_SKIPSIZE field is allowed + if (!inJsonObject.IsFieldPathExist(FieldPath{SchemaConstant::KEYWORD_SCHEMA_SKIPSIZE})) { + LOGD("[Schema][ParseSkipSize] No SCHEMA_SKIPSIZE Field."); + return E_OK; + } + // The type of SCHEMA_SKIPSIZE field has been checked in CheckMetaFieldCountAndType to be an INTEGER + FieldValue skipSizeValue; + int errCode = inJsonObject.GetFieldValueByFieldPath(FieldPath {SchemaConstant::KEYWORD_SCHEMA_SKIPSIZE}, + skipSizeValue); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + if (skipSizeValue.integerValue < 0 || + static_cast(skipSizeValue.integerValue) > SchemaConstant::SCHEMA_SKIPSIZE_MAX) { + LOGE("[Schema][ParseSkipSize] Unexpected SCHEMA_SKIPSIZE=%d.", skipSizeValue.integerValue); + return -E_SCHEMA_PARSE_FAIL; + } + schemaSkipSize_ = static_cast(skipSizeValue.integerValue); + return E_OK; +} + +int SchemaObject::ParseCheckEachIndexFromStringArray(const std::vector &inStrArray) +{ + std::vector indexPathVec; + std::set indexPathSet; + // Parse each indexFieldPathString and check duplication + for (const auto &eachPathStr : inStrArray) { + FieldPath eachPath; + int errCode = SchemaUtils::ParseAndCheckFieldPath(eachPathStr, eachPath); + if (errCode != E_OK) { + LOGE("[Schema][ParseEachIndex] IndexPath=%s Invalid.", eachPathStr.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + if (eachPath.size() == 0 || eachPath.size() > SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX) { + LOGE("[Schema][ParseEachIndex] Root not indexable or path=%s depth exceed limit.", eachPathStr.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + if (indexPathSet.count(eachPath) != 0) { + LOGE("[Schema][ParseEachIndex] IndexPath=%s Duplicated.", eachPathStr.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + indexPathVec.push_back(eachPath); + indexPathSet.insert(eachPath); + } + if (indexPathVec.empty()) { // Unlikely, empty JsonArray had been eliminated by GetArrayContent Method + return -E_INTERNAL_ERROR; + } + // Check indexDefine duplication, Use Sort-Column(the first fieldPath in index) as the indexName. + const IndexName &indexName = indexPathVec.front(); + if (schemaIndexes_.count(indexName) != 0) { + LOGE("[Schema][ParseEachIndex] IndexName=%s Already Defined.", SchemaUtils::FieldPathString(indexName).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + // Create new indexInfo entry, then check indexable for each indexFieldPath against schemaDefine + return CheckFieldPathIndexableThenSave(indexPathVec, schemaIndexes_[indexName]); +} + +int SchemaObject::CheckFieldPathIndexableThenSave(const std::vector &inPathVec, IndexInfo &infoToSave) +{ + for (const auto &eachPath : inPathVec) { + // Previous logic guarantee eachPath.size greater than zero + uint32_t depth = eachPath.size() - 1; // minus 1 to change depth count from zero + std::string eachPathStr = SchemaUtils::FieldPathString(eachPath); + if (schemaDefine_.count(depth) == 0) { + LOGE("[Schema][CheckIndexable] No schema define of this depth, path=%s.", eachPathStr.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + if (schemaDefine_[depth].count(eachPath) == 0) { + LOGE("[Schema][CheckIndexable] No such path in schema define, path=%s.", eachPathStr.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + if (!schemaDefine_[depth][eachPath].isIndexable) { + LOGE("[Schema][CheckIndexable] Path=%s is not indexable.", eachPathStr.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + // Save this indexField to indexInfo + infoToSave.push_back({eachPath, schemaDefine_[depth][eachPath].type}); + } + return E_OK; +} + +int SchemaObject::CompareSchemaVersionMode(const SchemaObject &newSchema) const +{ + static std::map modeMapString = { + {SchemaMode::STRICT, "STRICT"}, + {SchemaMode::COMPATIBLE, "COMPATIBLE"}, + }; + if (schemaVersion_ != newSchema.schemaVersion_) { + LOGE("[Schema][CompareVerMode] OldVer=%s mismatch newVer=%s.", schemaVersion_.c_str(), + newSchema.schemaVersion_.c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + // Only Json-Schema need to compare mode + if (schemaType_ == SchemaType::JSON && schemaMode_ != newSchema.schemaMode_) { + LOGE("[Schema][CompareVerMode] OldMode=%s mismatch newMode=%s.", modeMapString[schemaMode_].c_str(), + modeMapString[newSchema.schemaMode_].c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + // Do not return E_OK here, E_OK is ambiguous. + return -E_SCHEMA_EQUAL_EXACTLY; +} + +int SchemaObject::CompareSchemaSkipSize(const SchemaObject &newSchema) const +{ + if (schemaSkipSize_ != newSchema.schemaSkipSize_) { + LOGE("[Schema][CompareSkipSize] OldSkip=%u mismatch newSkip=%u.", schemaSkipSize_, newSchema.schemaSkipSize_); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + // Do not return E_OK here, E_OK is ambiguous. + return -E_SCHEMA_EQUAL_EXACTLY; +} + +int SchemaObject::CompareSchemaDefine(const SchemaObject &newSchema) const +{ + bool isEqualExactly = true; + for (uint32_t depth = 0; depth < SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX; depth++) { + SchemaDefine emptyDefine; + const SchemaDefine &defineInOldSchema = + (schemaDefine_.count(depth) == 0 ? emptyDefine : schemaDefine_.at(depth)); + const SchemaDefine &defineInNewSchema = + (newSchema.schemaDefine_.count(depth) == 0 ? emptyDefine : newSchema.schemaDefine_.at(depth)); + + // No define at this depth for both schema + if (defineInNewSchema.empty() && defineInOldSchema.empty()) { + break; + } + // No matter strict or compatible mode, newSchema can't have less field than oldSchema + if (defineInNewSchema.size() < defineInOldSchema.size()) { + LOGE("[Schema][CompareDefine] newSize=%zu less than oldSize=%zu at depth=%" PRIu32, + defineInNewSchema.size(), defineInOldSchema.size(), depth); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + if (defineInNewSchema.size() > defineInOldSchema.size()) { + // Strict mode not support increase fieldDefine + if (schemaMode_ == SchemaMode::STRICT) { + LOGE("[Schema][CompareDefine] newSize=%zu more than oldSize=%zu at depth=%" PRIu32 " in STRICT mode.", + defineInNewSchema.size(), defineInOldSchema.size(), depth); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + isEqualExactly = false; + } + + // Compare schema define of this depth, looking for incompatible + int errCode = CompareSchemaDefineByDepth(defineInOldSchema, defineInNewSchema); + if (errCode == -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + return errCode; + } + } + // Do not return E_OK here, E_OK is ambiguous. + return (isEqualExactly ? -E_SCHEMA_EQUAL_EXACTLY : -E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE); +} + +namespace { +inline bool IsExtraFieldConformToCompatibility(const SchemaAttribute &inAttr) +{ + return (!inAttr.hasNotNullConstraint || inAttr.hasDefaultValue); +} +} + +int SchemaObject::CompareSchemaDefineByDepth(const SchemaDefine &oldDefine, const SchemaDefine &newDefine) const +{ + // Looking for incompatible : new define should at least contain all field the old define hold + for (auto &entry : oldDefine) { + if (newDefine.count(entry.first) == 0) { + LOGE("[Schema][CompareDefineDepth] fieldpath=%s not found in new schema.", + SchemaUtils::FieldPathString(entry.first).c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + // SchemaAttribute require to be equal exactly + int errCode = CompareSchemaAttribute(entry.second, newDefine.at(entry.first)); + if (errCode != -E_SCHEMA_EQUAL_EXACTLY) { + LOGE("[Schema][CompareDefineDepth] Attribute mismatch at fieldpath=%s.", + SchemaUtils::FieldPathString(entry.first).c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } + // Looking for incompatible : the extra field in new schema should has default or can be null + for (auto &entry : newDefine) { + if (oldDefine.count(entry.first) != 0) { + continue; + } + if (!IsExtraFieldConformToCompatibility(entry.second)) { + LOGE("[Schema][CompareDefineDepth] ExtraField=%s, {notnull=%d, default=%d}, not conform compatibility.", + SchemaUtils::FieldPathString(entry.first).c_str(), entry.second.hasNotNullConstraint, + entry.second.hasDefaultValue); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } + return -E_SCHEMA_EQUAL_EXACTLY; +} + +int SchemaObject::CompareSchemaAttribute(const SchemaAttribute &oldAttr, const SchemaAttribute &newAttr) const +{ + if (oldAttr.type != newAttr.type) { + // The exceptional case is that the field type changed from the leaf_object to internal_object, + // which indicate that sub fields are added to it. Changed from internal_object to leaf_object will + // sooner or later cause an incompatible detection in next depth, we discern this situation here in advance. + if (!(oldAttr.type == FieldType::LEAF_FIELD_OBJECT && newAttr.type == FieldType::INTERNAL_FIELD_OBJECT)) { + LOGE("[Schema][CompareAttr] OldType=%s mismatch newType=%s.", + SchemaUtils::FieldTypeString(oldAttr.type).c_str(), SchemaUtils::FieldTypeString(newAttr.type).c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } + // Here we use isIndexable info to distinguish two categories of type. + // "BOOL, INTEGER, LONG, DOUBLE, STRING" are all indexable type, NULL type will not appear in Schema + // "ARRAY, LEAF_OBJECT, INTERNAL_OBJECT" are all not indexable type. + // They have been checked same type just above. No need to check more for not indexable type + if (oldAttr.isIndexable) { + if (oldAttr.hasNotNullConstraint != newAttr.hasNotNullConstraint) { + LOGE("[Schema][CompareAttr] OldNotNull=%d mismatch newNotNull=%d.", oldAttr.hasNotNullConstraint, + newAttr.hasNotNullConstraint); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + if (oldAttr.hasDefaultValue != newAttr.hasDefaultValue) { + LOGE("[Schema][CompareAttr] OldHasDefault=%d mismatch newHasDefault=%d.", oldAttr.hasDefaultValue, + newAttr.hasDefaultValue); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + if (oldAttr.hasDefaultValue) { + // DefaultValue require to be equal exactly + int errCode = CompareSchemaDefaultValue(oldAttr, newAttr); + if (errCode != -E_SCHEMA_EQUAL_EXACTLY) { + return errCode; + } + } + } + return -E_SCHEMA_EQUAL_EXACTLY; +} + +namespace { +inline bool IsDoubleBinaryEqual(double left, double right) +{ + return *(reinterpret_cast(&left)) == *(reinterpret_cast(&right)); +} +} + +int SchemaObject::CompareSchemaDefaultValue(const SchemaAttribute &oldAttr, const SchemaAttribute &newAttr) const +{ + // Value type has been check equal for both attribute in the caller + if (oldAttr.type == FieldType::LEAF_FIELD_BOOL) { + if (oldAttr.defaultValue.boolValue != newAttr.defaultValue.boolValue) { + LOGE("[Schema][CompareDefault] OldDefault=%d mismatch newDefault=%d.", oldAttr.defaultValue.boolValue, + newAttr.defaultValue.boolValue); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } else if (oldAttr.type == FieldType::LEAF_FIELD_INTEGER) { + if (oldAttr.defaultValue.integerValue != newAttr.defaultValue.integerValue) { + LOGE("[Schema][CompareDefault] OldDefault=%d mismatch newDefault=%d.", oldAttr.defaultValue.integerValue, + newAttr.defaultValue.integerValue); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } else if (oldAttr.type == FieldType::LEAF_FIELD_LONG) { + if (oldAttr.defaultValue.longValue != newAttr.defaultValue.longValue) { + LOGE("[Schema][CompareDefault] OldDefault=%" PRId64 " mismatch newDefault=%" PRId64 ".", + oldAttr.defaultValue.longValue, newAttr.defaultValue.longValue); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } else if (oldAttr.type == FieldType::LEAF_FIELD_DOUBLE) { + // ATTENTION: Here we should compare two double by their binary layout. We should not judge them equal when + // difference is small enough, since two different default double value may diff so little. The binary + // layout of the double value will be the same if the original string is the same, so we directly compare them. + if (!IsDoubleBinaryEqual(oldAttr.defaultValue.doubleValue, newAttr.defaultValue.doubleValue)) { + LOGE("[Schema][CompareDefault] OldDefault=%f mismatch newDefault=%f.", oldAttr.defaultValue.doubleValue, + newAttr.defaultValue.doubleValue); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } else if (oldAttr.type == FieldType::LEAF_FIELD_STRING) { + if (oldAttr.defaultValue.stringValue != newAttr.defaultValue.stringValue) { + LOGE("[Schema][CompareDefault] OldDefault=%s mismatch newDefault=%s.", + oldAttr.defaultValue.stringValue.c_str(), newAttr.defaultValue.stringValue.c_str()); + return -E_SCHEMA_UNEQUAL_INCOMPATIBLE; + } + } + // The caller logic guarantee that both attribute type will not be null, array, object + return -E_SCHEMA_EQUAL_EXACTLY; +} + +namespace { +inline void ClearIndexDifference(IndexDifference &indexDiffer) +{ + indexDiffer.change.clear(); + indexDiffer.increase.clear(); + indexDiffer.decrease.clear(); +} + +inline bool IsIndexInfoExactlyEqual(const IndexInfo &leftInfo, const IndexInfo &rightInfo) +{ + // Exactly equal require count, order and type of each indexField in the index be the same + return leftInfo == rightInfo; +} + +inline bool IsSchemaIndexesExactlyEqual(const IndexDifference &indexDiffer) +{ + return (indexDiffer.change.empty() && indexDiffer.increase.empty() && indexDiffer.decrease.empty()); +} +} + +int SchemaObject::CompareSchemaIndexes(const SchemaObject &newSchema, IndexDifference &indexDiffer) const +{ + ClearIndexDifference(indexDiffer); + // Find the increase and change index + for (const auto &entry : newSchema.schemaIndexes_) { + if (schemaIndexes_.count(entry.first) == 0) { + LOGD("[Schema][CompareIndex] Increase indexName=%s.", SchemaUtils::FieldPathString(entry.first).c_str()); + indexDiffer.increase[entry.first] = entry.second; + } else { + // Both schema have same IndexName, Check whether indexInfo differs + if (!IsIndexInfoExactlyEqual(entry.second, schemaIndexes_.at(entry.first))) { + LOGD("[Schema][CompareIndex] Change indexName=%s.", SchemaUtils::FieldPathString(entry.first).c_str()); + indexDiffer.change[entry.first] = entry.second; + } + } + } + // Find the decrease index + for (const auto &entry : schemaIndexes_) { + if (newSchema.schemaIndexes_.count(entry.first) == 0) { + LOGD("[Schema][CompareIndex] Decrease indexName=%s.", SchemaUtils::FieldPathString(entry.first).c_str()); + indexDiffer.decrease.insert(entry.first); + } + } + // Do not return E_OK here, E_OK is ambiguous. + return IsSchemaIndexesExactlyEqual(indexDiffer) ? -E_SCHEMA_EQUAL_EXACTLY : -E_SCHEMA_UNEQUAL_COMPATIBLE; +} + +namespace { +int CheckValueItemNumericType(FieldType typeInValue, FieldType typeInSchema) +{ + if (typeInValue == FieldType::LEAF_FIELD_DOUBLE) { + if (typeInSchema != FieldType::LEAF_FIELD_DOUBLE) { + return -E_VALUE_MISMATCH_FEILD_TYPE; + } + } else if (typeInValue == FieldType::LEAF_FIELD_LONG) { + if (typeInSchema != FieldType::LEAF_FIELD_LONG && + typeInSchema != FieldType::LEAF_FIELD_DOUBLE) { + return -E_VALUE_MISMATCH_FEILD_TYPE; + } + } else { + // LEAF_FIELD_INTEGER + if (typeInSchema != FieldType::LEAF_FIELD_INTEGER && + typeInSchema != FieldType::LEAF_FIELD_LONG && + typeInSchema != FieldType::LEAF_FIELD_DOUBLE) { + return -E_VALUE_MISMATCH_FEILD_TYPE; + } + } + return -E_VALUE_MATCH; +} + +inline bool IsTypeMustBeExactlyEqualBetweenSchemaAndValue(FieldType inType) +{ + return (inType == FieldType::LEAF_FIELD_BOOL || + inType == FieldType::LEAF_FIELD_STRING || + inType == FieldType::LEAF_FIELD_ARRAY); +} + +inline bool IsObjectType(FieldType inType) +{ + return (inType == FieldType::LEAF_FIELD_OBJECT || inType == FieldType::INTERNAL_FIELD_OBJECT); +} + +// Check in the value-view for convenience +int CheckValueItem(const SchemaAttribute &refAttr, FieldType typeInValue) +{ + FieldType typeInSchema = refAttr.type; + if (typeInSchema == FieldType::LEAF_FIELD_NULL) { // Unlikely + return -E_INTERNAL_ERROR; + } + // Check NotNull-Constraint first + if (typeInValue == FieldType::LEAF_FIELD_NULL) { + if (refAttr.hasNotNullConstraint) { + return -E_VALUE_MISMATCH_CONSTRAINT; + } + return -E_VALUE_MATCH; + } + // If typeInValue not NULL, check against schema. First check type that must be equal. + if (IsTypeMustBeExactlyEqualBetweenSchemaAndValue(typeInValue)) { + if (typeInValue != typeInSchema) { + return -E_VALUE_MISMATCH_FEILD_TYPE; + } + return -E_VALUE_MATCH; + } + // Check Object related type, lack or more field will be deal with at next depth + // typeInSchema/typeInValue LEAF_OBJECT INTERNAL_OBJECT + // LEAF_OBJECT MATCH MATCH(More field at next depth) + // INTERNAL_OBJECT MATCH(Lack field at next depth) MATCH + // ELSE(POSSIBLE) TYPE_MISMATCH TYPE_MISMATCH + if (IsObjectType(typeInValue)) { + if (!IsObjectType(typeInSchema)) { + return -E_VALUE_MISMATCH_FEILD_TYPE; + } + return -E_VALUE_MATCH; + } + // Check Numeric related type, at last + return CheckValueItemNumericType(typeInValue, typeInSchema); +} + +inline bool IsLackingFieldViolateNotNullConstraint(const SchemaAttribute &refAttr) +{ + return (refAttr.hasNotNullConstraint && !refAttr.hasDefaultValue); +} + +// Function only for split big function +int CheckValueBySchemaItem(const std::pair &schemaItem, + const std::map &subPathType, std::set &lackingPaths) +{ + if (subPathType.count(schemaItem.first) == 0) { // Value do not contain this field + if (IsLackingFieldViolateNotNullConstraint(schemaItem.second)) { + return -E_VALUE_MISMATCH_CONSTRAINT; + } + lackingPaths.insert(schemaItem.first); + return -E_VALUE_MATCH; + } + // Value contain this field, check its type + return CheckValueItem(schemaItem.second, subPathType.at(schemaItem.first)); +} + +inline std::string ValueFieldType(const std::map &subPathType, const FieldPath &inPath) +{ + if (subPathType.count(inPath) == 0) { + return "NotExist"; + } + return SchemaUtils::FieldTypeString(subPathType.at(inPath)); +} +} + +int SchemaObject::CheckValue(const ValueObject &inValue, std::set &lackingPaths) const +{ + std::set nestPathCurDepth{FieldPath()}; // Empty path represent root path + for (uint32_t depth = 0; depth < SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX; depth++) { + if (schemaDefine_.count(depth) == 0 || schemaDefine_.at(depth).empty()) { // No schema define in this depth + break; + } + + std::map subPathType; + int errCode = inValue.GetSubFieldPathAndType(nestPathCurDepth, subPathType); // Value field of current depth + if (errCode != E_OK && errCode != -E_INVALID_PATH) { // E_INVALID_PATH for path not exist + LOGE("[Schema][CheckValue] GetSubFieldPathAndType Fail=%d, Depth=%u.", errCode, depth); + return -E_VALUE_MISMATCH_FEILD_TYPE; + } + nestPathCurDepth.clear(); // Clear it for collecting new nestPath + + if ((schemaMode_ == SchemaMode::STRICT) && (subPathType.size() > schemaDefine_.at(depth).size())) { + LOGE("[Schema][CheckValue] ValueFieldCount=%zu more than SchemaFieldCount=%zu at depth=%u", + subPathType.size(), schemaDefine_.at(depth).size(), depth); + return -E_VALUE_MISMATCH_FEILD_COUNT; // Value contain more field than schema + } + + for (const auto &schemaItem : schemaDefine_.at(depth)) { // Check each field define in schema + if (schemaItem.second.type == FieldType::INTERNAL_FIELD_OBJECT) { + nestPathCurDepth.insert(schemaItem.first); // This field has subfield in schema + } + errCode = CheckValueBySchemaItem(schemaItem, subPathType, lackingPaths); + if (errCode != -E_VALUE_MATCH) { + LOGE("[Schema][CheckValue] Path=%s, schema{NotNull=%d,Default=%d,Type=%s}, Value{Type=%s}, errCode=%d.", + SchemaUtils::FieldPathString(schemaItem.first).c_str(), schemaItem.second.hasNotNullConstraint, + schemaItem.second.hasDefaultValue, SchemaUtils::FieldTypeString(schemaItem.second.type).c_str(), + ValueFieldType(subPathType, schemaItem.first).c_str(), errCode); + return errCode; + } + } + } + return -E_VALUE_MATCH; +} + +int SchemaObject::AmendValueIfNeed(ValueObject &inValue, const std::set &lackingPaths, bool &amended) const +{ + for (const auto &eachLackingPath : lackingPaths) { + // Note: The upper code logic guarantee that eachLackingPath won't be empty and must exist in schemaDefine_ + uint32_t depth = eachLackingPath.size() - 1; // Depth count from zero + const SchemaAttribute &lackingPathAttr = schemaDefine_.at(depth).at(eachLackingPath); + // If no default value, just ignore this lackingPath + if (!lackingPathAttr.hasDefaultValue) { + continue; + } + // If has default value, the ParseSchema logic guarantee that fieldType won't be NULL, ARRAY or OBJECT + // The lacking intermediate field will be automatically insert for this lackingPath + int errCode = inValue.InsertField(eachLackingPath, lackingPathAttr.type, lackingPathAttr.defaultValue); + if (errCode != E_OK) { // Unlikely + LOGE("[Schema][AmendValue] InsertField fail, errCode=%d, Path=%s, Type=%s.", errCode, + SchemaUtils::FieldPathString(eachLackingPath).c_str(), + SchemaUtils::FieldTypeString(lackingPathAttr.type).c_str()); + return -E_INTERNAL_ERROR; + } + amended = true; + } + return E_OK; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/schema_utils.cpp b/mock/distributeddb/common/src/schema_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bb454dcddc34b1c08ab5d45ad99725109d4f8858 --- /dev/null +++ b/mock/distributeddb/common/src/schema_utils.cpp @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "schema_utils.h" +#include +#include +#include +#include +#include "db_errno.h" +#include "log_print.h" +#include "schema_constant.h" + +namespace DistributedDB { +namespace { + bool IsLegalFieldCharacter(char character) + { + return (std::isalnum(character) || character == '_'); + } + void TrimFiled(std::string &inString) + { + inString.erase(0, inString.find_first_not_of("\r\t ")); + size_t temp = inString.find_last_not_of("\r\t "); + if (temp < inString.size()) { + inString.erase(temp + 1); + } + } + + // TYPE, [NOT NULL,] [DEFAULT X] + // DEFAULT at last + // State transition matrix + const int STATE_TRANSFER[8][6] = { // 5 type input and 7 type state + // blank, NOT NULL, DEFAULT, OTHER AlNUM, COMMA + {0, -1, -1, 1, -1}, // state 0: empty + {1, -1, -1, 1, 2}, // state 1: only type + {2, 3, 5, -1, -1}, // state 2: alnum , + {3, -1, -1, -1, 4}, // state 3: alnum , notnull + {4, -1, 5, -1, -1}, // state 4: alnum , notnull , + {6, -1, -1, -1, -1}, // state 5: finish with DEFAULT + {6, -1, -1, 7, -1}, // state 6: finish with DEFAULT and blank + {7, 7, 7, 7, 7}, // state 7: finish with DEFAULT and blank and no matter what value + }; + enum StateTransferColNum { + COLUMN_ILLEGAL = -1, + COLUMN_BLANK, + COLUMN_NOT_NULL, + COLUMN_DEFAULT, + COLUMN_OTHER_ALNUM, + COLUMN_COMMA, + }; +} // namespace + +// compare function can make sure not to cross the border, pos < oriContent.size() - 1 +// Get symbol type and Converts to the corresponding column of the state transition matrix +int SchemaUtils::MakeTrans(const std::string &oriContent, size_t &pos) +{ + if (isspace(oriContent[pos])) { + return COLUMN_BLANK; + } else if (oriContent.compare(pos, SchemaConstant::KEYWORD_ATTR_NOT_NULL.size(), + SchemaConstant::KEYWORD_ATTR_NOT_NULL) == 0) { + pos = pos + SchemaConstant::KEYWORD_ATTR_NOT_NULL.size() - 1; + return COLUMN_NOT_NULL; + } else if (oriContent.compare(pos, SchemaConstant::KEYWORD_ATTR_DEFAULT.size(), + SchemaConstant::KEYWORD_ATTR_DEFAULT) == 0) { + pos = pos + SchemaConstant::KEYWORD_ATTR_DEFAULT.size() - 1; + return COLUMN_DEFAULT; + } else if (std::isalnum(oriContent[pos]) || oriContent[pos] == '\'' || + oriContent[pos] == '+' || oriContent[pos] == '-') { + return COLUMN_OTHER_ALNUM; + } else if (oriContent[pos] == ',') { + return COLUMN_COMMA; + } else { + return COLUMN_ILLEGAL; + } +} + +// Use DFA to check and Parsing +// You can get the corresponding state meaning in the state transition matrix STATE_TRANSFER +int SchemaUtils::SplitSchemaAttribute(const std::string &inAttrString, std::vector &outAttrString) +{ + int state = 0; + outAttrString.resize(3); // attribute have 3 type keywords + for (size_t i = 0; i < inAttrString.size(); i++) { + int id = MakeTrans(inAttrString, i); + if (id < 0) { + LOGD("Split Schema Attribute err, Contains unrecognized content [%c]", inAttrString[i]); + return -E_SCHEMA_PARSE_FAIL; + } + state = STATE_TRANSFER[state][id]; + if (state < 0) { + LOGD("Split Schema Attribute err, err state [%d]", state); + return -E_SCHEMA_PARSE_FAIL; + } + switch (state) { + case 1: // state 1 :Indicates that only type information is currently available + outAttrString[0].push_back(inAttrString[i]); + break; + case 3: // state 3 :Gets the NOT_NULL keyword + outAttrString[1] = SchemaConstant::KEYWORD_ATTR_NOT_NULL; + break; + case 7: // state 7 :Contains complete information + // Get default string. Now transfer matrix can ensure > 1, but you should pay attention when fix it + if (i <= 1) { + LOGE("default string size must be over 1."); + return -E_SCHEMA_PARSE_FAIL; + } + outAttrString[2] = inAttrString.substr(i - 1); + return E_OK; + default: + break; + } + } + // Only these states are legal, The meaning of the state can be seen in the matrix STATE_TRANSFER explanation + if (!(state == 1 || state == 3 || state == 7)) { + LOGD("Split Schema Attribute err, err state [%d]", state); + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; +} + +int SchemaUtils::TransToBool(const std::string &defaultContent, SchemaAttribute &outAttr) +{ + // Have been trim + if (defaultContent.compare(SchemaConstant::KEYWORD_ATTR_VALUE_TRUE) == 0) { + outAttr.defaultValue.boolValue = true; + return E_OK; + } else if (defaultContent.compare(SchemaConstant::KEYWORD_ATTR_VALUE_FALSE) == 0) { + outAttr.defaultValue.boolValue = false; + return E_OK; + } + LOGE("Default value can not transform to bool!!"); + return -E_SCHEMA_PARSE_FAIL; +} + +int SchemaUtils::TransToString(const std::string &defaultContent, SchemaAttribute &outAttr) +{ + // Have been trim, Strip leading and trailing ' + if (defaultContent.size() > 1 && defaultContent.front() == '\'' && defaultContent.back() == '\'') { + outAttr.defaultValue.stringValue = defaultContent.substr(1, defaultContent.size() - 2); + if (outAttr.defaultValue.stringValue.size() > SchemaConstant::SCHEMA_DEFAULT_STRING_SIZE_LIMIT) { + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; + } + LOGE("Substandard format! Default value can not transform to string!!"); + return -E_SCHEMA_PARSE_FAIL; +} + +int SchemaUtils::TransToInteger(const std::string &defaultContent, SchemaAttribute &outAttr) +{ + // defaultContent can not be null + if (defaultContent.empty()) { + return -E_SCHEMA_PARSE_FAIL; + } + int transRes = strtol(defaultContent.c_str(), nullptr, 10); // 10: decimal + std::string resReview = std::to_string(transRes); + if (defaultContent.compare(defaultContent.find_first_not_of("+- "), defaultContent.size(), + resReview, resReview.find_first_not_of("+- "), resReview.size()) == 0) { + // Check the sign of the number + if ((defaultContent[0] == '-' && resReview[0] == '-') || + (defaultContent[0] != '-' && resReview[0] != '-') || + transRes == 0) { + outAttr.defaultValue.integerValue = transRes; + return E_OK; + } + } + LOGE("Default value can not transform to Integer!!"); + return -E_SCHEMA_PARSE_FAIL; +} + +int SchemaUtils::TransToLong(const std::string &defaultContent, SchemaAttribute &outAttr) +{ + // defaultContent can not be null + if (defaultContent.empty()) { + return -E_SCHEMA_PARSE_FAIL; + } + int64_t transRes = strtoll(defaultContent.c_str(), nullptr, 10); // 10: decimal + std::string resReview = std::to_string(transRes); + if (defaultContent.compare(defaultContent.find_first_not_of("+- "), defaultContent.size(), + resReview, resReview.find_first_not_of("+- "), resReview.size()) == 0) { + // Check the sign of the number + if ((defaultContent[0] == '-' && resReview[0] == '-') || + (defaultContent[0] != '-' && resReview[0] != '-') || + transRes == 0) { + outAttr.defaultValue.longValue = transRes; + return E_OK; + } + } + + LOGE("Default value[%s] can not transform to LONG!!", resReview.c_str()); + return -E_SCHEMA_PARSE_FAIL; +} + +int SchemaUtils::TransToDouble(const std::string &defaultContent, SchemaAttribute &outAttr) +{ + // defaultContent can not be null + if (defaultContent.empty()) { + return -E_SCHEMA_PARSE_FAIL; + } + + // Disable scientific notation + int dotCount = 0; + for (const auto &iter : defaultContent) { + if (!(std::isdigit(iter) || iter == '.' || iter == '-' || iter == '+')) { + LOGE("Default value to double, exist invalid symbol[%c]", iter); + return -E_SCHEMA_PARSE_FAIL; + } + if (iter == '.') { + dotCount++; + } + if (dotCount > 1) { + LOGE("Default value to double, exist invalid extra dot"); + return -E_SCHEMA_PARSE_FAIL; + } + } + + char *end = nullptr; + double transRes = std::strtod(defaultContent.c_str(), &end); + // Double exist problems with accuracy, overflow is subject to the legality of the c++ conversion. + if (transRes > -HUGE_VAL && transRes < HUGE_VAL && std::isfinite(transRes)) { + // Cleared blank + if (end != &defaultContent.back() + 1) { + LOGD("Termination of parsing due to exception symbol"); + return -E_SCHEMA_PARSE_FAIL; + } + outAttr.defaultValue.doubleValue = transRes; + return E_OK; + } + LOGE("Default value can not transform to double, overflow double max!"); + return -E_SCHEMA_PARSE_FAIL; +} + +int SchemaUtils::TransformDefaultValue(std::string &defaultContent, SchemaAttribute &outAttr) +{ + TrimFiled(defaultContent); + if (defaultContent.compare(SchemaConstant::KEYWORD_ATTR_VALUE_NULL) == 0 && outAttr.hasNotNullConstraint) { + LOGE("NOT NULL and DEFAULT null Simultaneously"); + return -E_SCHEMA_PARSE_FAIL; + } else if (defaultContent.compare(SchemaConstant::KEYWORD_ATTR_VALUE_NULL) == 0) { + outAttr.hasDefaultValue = false; + return E_OK; + } + + int errCode = E_OK; + switch (outAttr.type) { + case FieldType::LEAF_FIELD_BOOL: + errCode = TransToBool(defaultContent, outAttr); + break; + case FieldType::LEAF_FIELD_INTEGER: + errCode = TransToInteger(defaultContent, outAttr); + break; + case FieldType::LEAF_FIELD_LONG: + errCode = TransToLong(defaultContent, outAttr); + break; + case FieldType::LEAF_FIELD_DOUBLE: + errCode = TransToDouble(defaultContent, outAttr); + break; + case FieldType::LEAF_FIELD_STRING: + errCode = TransToString(defaultContent, outAttr); + break; + default: + LOGE("Unrecognized or unsupported type, please check!!"); + errCode = -E_SCHEMA_PARSE_FAIL; + break; + } + + LOGD("SchemaAttribute type is [%d], transfer result is [%d]", static_cast(outAttr.type), errCode); + return errCode; +} + +int SchemaUtils::ParseAndCheckSchemaAttribute(const std::string &inAttrString, SchemaAttribute &outAttr, + bool useAffinity) +{ + if (inAttrString.empty()) { + return -E_SCHEMA_PARSE_FAIL; + } + std::string tempinAttrString = inAttrString; + TrimFiled(tempinAttrString); + + std::vector attrContext; + int errCode = SplitSchemaAttribute(inAttrString, attrContext); + if (errCode != E_OK) { + LOGD("Syntax error, please check!"); + return errCode; + } + errCode = ParseSchemaAttribute(attrContext, outAttr, useAffinity); + if (errCode != E_OK) { + LOGD("Grammatical error, please check!"); + return errCode; + } + + return E_OK; +} + +int SchemaUtils::ParseSchemaAttribute(std::vector &attrContext, SchemaAttribute &outAttr, bool useAffinity) +{ + // Currently supported types + static const std::map FIELD_TYPE_DIC = { + {SchemaConstant::KEYWORD_TYPE_BOOL, FieldType::LEAF_FIELD_BOOL}, + {SchemaConstant::KEYWORD_TYPE_INTEGER, FieldType::LEAF_FIELD_INTEGER}, + {SchemaConstant::KEYWORD_TYPE_LONG, FieldType::LEAF_FIELD_LONG}, + {SchemaConstant::KEYWORD_TYPE_DOUBLE, FieldType::LEAF_FIELD_DOUBLE}, + {SchemaConstant::KEYWORD_TYPE_STRING, FieldType::LEAF_FIELD_STRING}, + }; + + // After split attribute? attrContext include 3 type field + if (attrContext.size() < 3) { + LOGE("No parsing preprocessing!!"); + return -E_SCHEMA_PARSE_FAIL; + } + TrimFiled(attrContext[0]); + if (!useAffinity) { + if (FIELD_TYPE_DIC.find(attrContext[0]) == FIELD_TYPE_DIC.end()) { + LOGE("Errno schema field type [%s]!!", attrContext[0].c_str()); + return -E_SCHEMA_PARSE_FAIL; + } else { + outAttr.type = FIELD_TYPE_DIC.at(attrContext[0]); + } + } else { + outAttr.type = FieldType::LEAF_FIELD_NULL; + outAttr.customFieldType = attrContext[0]; + } + + outAttr.hasNotNullConstraint = !attrContext[1].empty(); + + // if DEFAULT value context exist, fix hasDefaultValue flag, 2nd represents the default value + if (attrContext[2].empty()) { + outAttr.hasDefaultValue = false; + } else { + outAttr.hasDefaultValue = true; + int errCode = TransformDefaultValue(attrContext[2], outAttr); // 2nd element is DEFAULT value + if (errCode != E_OK) { + LOGE("Default value is malformed!!"); + return -E_SCHEMA_PARSE_FAIL; + } + } + return E_OK; +} + +namespace { +// Check prefix and attempt to find any illegal, returns E_OK if nothing illegal and an hasPrefix indicator. +int CheckDollarDotPrefix(const std::string &inPathStr, bool &hasPrefix) +{ + if (inPathStr.empty()) { + return -E_SCHEMA_PARSE_FAIL; + } + if (inPathStr.size() >= std::string("$.").size()) { + // In this case, $. prefix may exist, but also may not exist. + if (inPathStr[0] == '$' && inPathStr[1] == '.') { // 1 for second char + // $. prefix may exist + hasPrefix = true; + return E_OK; + } + if (inPathStr[0] == '$' && inPathStr[1] != '.') { // 1 for second char + return -E_SCHEMA_PARSE_FAIL; + } + if (inPathStr[1] == '$') { // 1 for second char + return -E_SCHEMA_PARSE_FAIL; + } + } + // here, inPathStr not empty, has at least one char, should not begin with '.' + if (inPathStr[0] == '.') { + return -E_SCHEMA_PARSE_FAIL; + } + hasPrefix = false; + return E_OK; +} +} + +int SchemaUtils::ParseAndCheckFieldPath(const std::string &inPathString, FieldPath &outPath, bool permitPrefix) +{ + std::string tempInPathString = inPathString; + TrimFiled(tempInPathString); + bool hasPrefix = false; + int errCode = CheckDollarDotPrefix(tempInPathString, hasPrefix); + if (errCode != E_OK) { + LOGE("CheckDollarDotPrefix Fail."); + return errCode; + } + + if (!permitPrefix && hasPrefix) { + LOGE("Not permit $. prefix."); + return -E_SCHEMA_PARSE_FAIL; + } + + if (!hasPrefix) { + tempInPathString = std::string("$.") + tempInPathString; + } + + for (size_t curPos = 1; curPos < tempInPathString.size();) { + if (curPos + 1 == tempInPathString.size()) { + LOGE("Dot at end will generate empty illegal path!"); + return -E_SCHEMA_PARSE_FAIL; + } + size_t nextPointPos = tempInPathString.find_first_of(".", curPos + 1); + outPath.push_back(tempInPathString.substr(curPos + 1, nextPointPos - curPos - 1)); + curPos = nextPointPos; + } + + if (outPath.size() > SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX) { + LOGE("Parse Schema Index depth illegality!"); + return -E_SCHEMA_PARSE_FAIL; + } + + for (const auto &iter : outPath) { + if (CheckFieldName(iter) != E_OK) { + LOGE("Parse Schema Index field illegality!"); + return -E_SCHEMA_PARSE_FAIL; + } + } + return E_OK; +} + +int SchemaUtils::CheckFieldName(const FieldName &inName) +{ + if (inName.empty() || inName.size() > SchemaConstant::SCHEMA_FEILD_NAME_LENGTH_MAX) { + LOGE("Schema FieldName have invalid size!"); + return -E_SCHEMA_PARSE_FAIL; + } + + // The first letter must be a number or an underscore + if (!(std::isalpha(inName[0]) || inName[0] == '_')) { + LOGE("Schema FieldName begin with un support symbol!"); + return -E_SCHEMA_PARSE_FAIL; + } + + // Must consist of numeric underscore letters + for (const auto &iter : inName) { + if (!(IsLegalFieldCharacter(iter))) { + LOGE("Schema FieldName exist un support symbol!"); + return -E_SCHEMA_PARSE_FAIL; + } + } + + return E_OK; +} + +std::string SchemaUtils::Strip(const std::string &inString) +{ + std::string stripRes = inString; + TrimFiled(stripRes); + return stripRes; +} + +std::string SchemaUtils::StripNameSpace(const std::string &inFullName) +{ + auto pos = inFullName.find_last_of('.'); + if (pos == std::string::npos) { // No '.', so no namespace + return inFullName; + } + return inFullName.substr(pos + 1); +} + +std::string SchemaUtils::FieldTypeString(FieldType inType) +{ + static std::map fieldTypeMapString = { + {FieldType::LEAF_FIELD_NULL, "NULL"}, + {FieldType::LEAF_FIELD_BOOL, "BOOL"}, + {FieldType::LEAF_FIELD_INTEGER, "INTEGER"}, + {FieldType::LEAF_FIELD_LONG, "LONG"}, + {FieldType::LEAF_FIELD_DOUBLE, "DOUBLE"}, + {FieldType::LEAF_FIELD_STRING, "STRING"}, + {FieldType::LEAF_FIELD_ARRAY, "ARRAY"}, + {FieldType::LEAF_FIELD_OBJECT, "LEAF_OBJECT"}, + {FieldType::INTERNAL_FIELD_OBJECT, "INTERNAL_OBJECT"}, + }; + return fieldTypeMapString[inType]; +} + +std::string SchemaUtils::SchemaTypeString(SchemaType inType) +{ + static std::map schemaTypeMapString { + {SchemaType::NONE, "NONE"}, + {SchemaType::JSON, "JSON-SCHEMA"}, + {SchemaType::FLATBUFFER, "FLATBUFFER-SCHEMA"}, + {SchemaType::RELATIVE, "RELATIVE"}, + {SchemaType::UNRECOGNIZED, "UNRECOGNIZED"}, + }; + return schemaTypeMapString[inType]; +} + +std::string SchemaUtils::FieldPathString(const FieldPath &inPath) +{ + std::string outString = "$"; + for (const auto &entry : inPath) { + outString += "."; + outString += entry; + } + return outString; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/semaphore_utils.cpp b/mock/distributeddb/common/src/semaphore_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..05d99dd3c1cdb76e577faf964b3f4536956a82c2 --- /dev/null +++ b/mock/distributeddb/common/src/semaphore_utils.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "semaphore_utils.h" + +#include + +namespace DistributedDB { +using std::unique_lock; +using std::lock_guard; +using std::mutex; +using std::condition_variable; + +SemaphoreUtils::SemaphoreUtils(int count) + : count_(count) +{} + +SemaphoreUtils::~SemaphoreUtils() +{} + +bool SemaphoreUtils::WaitSemaphore(int waitSecond) +{ + unique_lock lock(lockMutex_); + bool result = cv_.wait_for(lock, std::chrono::seconds(waitSecond), + std::bind(&SemaphoreUtils::CompareCount, this)); + if (result == true) { + --count_; + } + return result; +} + +void SemaphoreUtils::WaitSemaphore() +{ + unique_lock lock(lockMutex_); + cv_.wait(lock, std::bind(&SemaphoreUtils::CompareCount, this)); + --count_; +} + +void SemaphoreUtils::SendSemaphore() +{ + lock_guard lock(lockMutex_); + count_++; + cv_.notify_one(); +} + +bool SemaphoreUtils::CompareCount() const +{ + return count_ > 0; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/task_pool.cpp b/mock/distributeddb/common/src/task_pool.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2cdeb5d34fa376f9bfd15c0ff4dd9f9a0d2f5437 --- /dev/null +++ b/mock/distributeddb/common/src/task_pool.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "task_pool.h" +#include "db_errno.h" +#include "log_print.h" +#include "task_pool_impl.h" + +namespace DistributedDB { +TaskPool *TaskPool::Create(int maxThreads, int minThreads, int &errCode) +{ + TaskPool *taskPool = new (std::nothrow) TaskPoolImpl(maxThreads, minThreads); + if (taskPool == nullptr) { + LOGE("alloc task pool failed."); + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + errCode = E_OK; + return taskPool; +} + +void TaskPool::Release(TaskPool *&taskPool) +{ + if (taskPool != nullptr) { + delete taskPool; + taskPool = nullptr; + } +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/task_pool_impl.cpp b/mock/distributeddb/common/src/task_pool_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40b66cb393453fe1e2a4e460c9e411d5f4ef19f5 --- /dev/null +++ b/mock/distributeddb/common/src/task_pool_impl.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "task_pool_impl.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +constexpr int TaskPoolImpl::IDLE_WAIT_PERIOD; + +TaskPoolImpl::TaskPoolImpl(int maxThreads, int minThreads) + : genericTasks_(false), + genericTaskCount_(0), + queuedTaskCount_(0), + isStarted_(false), + isStopping_(false), + maxThreads_(maxThreads), + minThreads_(minThreads), + curThreads_(0), + idleThreads_(0) +{} + +TaskPoolImpl::~TaskPoolImpl() +{} + +int TaskPoolImpl::Start() +{ + if (maxThreads_ < minThreads_) { + LOGE("Start task pool failed, maxThreads(%d) < minThreads(%d).", + maxThreads_, minThreads_); + return -E_INVALID_ARGS; + } + if (maxThreads_ <= 0) { + LOGE("Start task pool failed, maxThreads(%d) <= 0.", maxThreads_); + return -E_INVALID_ARGS; + } + if (minThreads_ < 0) { + LOGE("Start task pool failed, minThreads(%d) < 0.", minThreads_); + return -E_INVALID_ARGS; + } + LOGI("Start task pool min:%d, max:%d", minThreads_, maxThreads_); + std::lock_guard guard(tasksMutex_); + isStarted_ = true; // parameters checked ok. + isStopping_ = false; + int errCode = SpawnThreads(true); + if (errCode != E_OK) { + LOGW("Spawn threads failed when starting the task pool."); + // ignore the error, we will try when schedule(). + } + return E_OK; +} + +void TaskPoolImpl::Stop() +{ + std::unique_lock lock(tasksMutex_); + if (!isStarted_) { + return; + } + isStopping_ = true; + hasTasks_.notify_all(); + allThreadsExited_.wait(lock, [this]() { + return this->curThreads_ <= 0; + }); + isStarted_ = false; +} + +int TaskPoolImpl::Schedule(const Task &task) +{ + if (!task) { + return -E_INVALID_ARGS; + } + std::lock_guard guard(tasksMutex_); + if (!isStarted_) { + LOGE("Schedule failed, the task pool is not started."); + return -E_NOT_PERMIT; + } + if (isStopping_) { + LOGI("Schedule failed, the task pool is stopping."); + return -E_STALE; + } + genericTasks_.PutTask(task); + ++genericTaskCount_; + hasTasks_.notify_one(); + TryToSpawnThreads(); + return E_OK; +} + +int TaskPoolImpl::Schedule(const std::string &queueTag, const Task &task) +{ + if (!task) { + return -E_INVALID_ARGS; + } + std::lock_guard guard(tasksMutex_); + if (!isStarted_) { + LOGE("Schedule failed, the task pool is not started."); + return -E_NOT_PERMIT; + } + if (isStopping_) { + LOGI("Schedule failed, the task pool is stopping."); + return -E_STALE; + } + queuedTasks_[queueTag].PutTask(task); + ++queuedTaskCount_; + hasTasks_.notify_all(); + TryToSpawnThreads(); + return E_OK; +} + +void TaskPoolImpl::ShrinkMemory(const std::string &tag) +{ + std::lock_guard guard(tasksMutex_); + auto iter = queuedTasks_.find(tag); + if (iter != queuedTasks_.end()) { + if (iter->second.IsEmptyAndUnlocked()) { + queuedTasks_.erase(iter); + } + } +} + +bool TaskPoolImpl::IdleExit(std::unique_lock &lock) +{ + if (isStopping_) { + return true; + } + ++idleThreads_; + bool isGenericWorker = IsGenericWorker(); + if (!isGenericWorker && (curThreads_ > minThreads_)) { + std::cv_status status = hasTasks_.wait_for(lock, + std::chrono::seconds(IDLE_WAIT_PERIOD)); + if (status == std::cv_status::timeout && + genericTaskCount_ <= 0) { + --idleThreads_; + return true; + } + } else { + if (isGenericWorker) { + hasTasks_.notify_all(); + } + hasTasks_.wait(lock); + } + --idleThreads_; + return false; +} + +void TaskPoolImpl::SetThreadFree() +{ + for (auto &pair : queuedTasks_) { + TaskQueue *tq = &pair.second; + tq->ReleaseLock(); + } +} + +Task TaskPoolImpl::ReapTask(TaskQueue *&queue) +{ + Task task = genericTasks_.GetTaskAutoLock(); + if (task != nullptr) { + queue = nullptr; + return task; + } + + queue = nullptr; + if (IsGenericWorker() && (curThreads_ > 1)) { // 1 indicates self. + SetThreadFree(); + return nullptr; + } + for (auto &pair : queuedTasks_) { + TaskQueue *tq = &pair.second; + task = tq->GetTaskAutoLock(); + if (task != nullptr) { + queue = tq; + return task; + } + } + return nullptr; +} + +int TaskPoolImpl::GetTask(Task &task, TaskQueue *&queue) +{ + std::unique_lock lock(tasksMutex_); + + while (true) { + task = ReapTask(queue); + if (task != nullptr) { + return E_OK; + } + + if (IdleExit(lock)) { + break; + } + } + return E_OK; +} + +int TaskPoolImpl::SpawnThreads(bool isStart) +{ + if (!isStarted_) { + LOGE("Spawn task pool threads failed, pool is not started."); + return -E_NOT_PERMIT; + } + if (curThreads_ >= maxThreads_) { + // the pool is full of threads. + return E_OK; + } + + int limits = isStart ? minThreads_ : (curThreads_ + 1); + while (curThreads_ < limits) { + ++curThreads_; + std::thread thread([this]() { + TaskWorker(); + }); + LOGI("Task pool spawn cur:%d idle:%d.", curThreads_, idleThreads_); + thread.detach(); + } + return E_OK; +} + +bool TaskPoolImpl::IsGenericWorker() const +{ + return genericThread_ == std::this_thread::get_id(); +} + +void TaskPoolImpl::BecomeGenericWorker() +{ + std::lock_guard guard(tasksMutex_); + if (genericThread_ == std::thread::id()) { + genericThread_ = std::this_thread::get_id(); + } +} + +void TaskPoolImpl::ExitWorker() +{ + std::lock_guard guard(tasksMutex_); + if (IsGenericWorker()) { + genericThread_ = std::thread::id(); + } + --curThreads_; + allThreadsExited_.notify_all(); + LOGI("Task pool thread exit, cur:%d idle:%d, genericTaskCount:%d, queuedTaskCount:%d.", + curThreads_, idleThreads_, genericTaskCount_, queuedTaskCount_); +} + +void TaskPoolImpl::TaskWorker() +{ + BecomeGenericWorker(); + + while (true) { + TaskQueue *taskQueue = nullptr; + Task task = nullptr; + + int errCode = GetTask(task, taskQueue); + if (errCode != E_OK) { + LOGE("Thread worker gets task failed, err:'%d'.", errCode); + break; + } + if (task == nullptr) { + // Idle thread exit. + break; + } + + task(); + FinishExecuteTask(taskQueue); + } + + ExitWorker(); +} + +void TaskPoolImpl::FinishExecuteTask(TaskQueue *taskQueue) +{ + std::lock_guard guard(tasksMutex_); + if (taskQueue != nullptr) { + taskQueue->ReleaseLock(); + --queuedTaskCount_; + } else { + --genericTaskCount_; + } +} + +void TaskPoolImpl::TryToSpawnThreads() +{ + if ((curThreads_ >= maxThreads_) || + (curThreads_ >= (queuedTaskCount_ + genericTaskCount_))) { + return; + } + (void)(SpawnThreads(false)); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/task_pool_impl.h b/mock/distributeddb/common/src/task_pool_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..7e533a2d64edc06eb4cfeeb556df749f2da5cbc3 --- /dev/null +++ b/mock/distributeddb/common/src/task_pool_impl.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TASK_POOL_IMPL_H +#define TASK_POOL_IMPL_H + +#include +#include +#include +#include +#include +#include "task_pool.h" +#include "task_queue.h" + +namespace DistributedDB { +class TaskPoolImpl : public TaskPool { +public: + // maxThreads > 0. + TaskPoolImpl(int maxThreads, int minThreads); + + // Start the task pool. + int Start() override; + + // Stop the task pool. + void Stop() override; + + // Schedule a task, the task can be ran in any thread. + int Schedule(const Task &task) override; + + // Schedule tasks one by one. + int Schedule(const std::string &queueTag, const Task &task) override; + + // Shrink memory associated with the given tag if possible. + void ShrinkMemory(const std::string &tag) override; + +protected: + ~TaskPoolImpl(); + +private: + int SpawnThreads(bool isStart); + bool IdleExit(std::unique_lock &lock); + void SetThreadFree(); + Task ReapTask(TaskQueue *&queue); + int GetTask(Task &task, TaskQueue *&queue); + bool IsGenericWorker() const; + void BecomeGenericWorker(); + void ExitWorker(); + void TaskWorker(); + void FinishExecuteTask(TaskQueue *taskQueue); + void TryToSpawnThreads(); + + // Member Variables. + static constexpr int IDLE_WAIT_PERIOD = 1; // wait 1 second before exiting. + std::mutex tasksMutex_; + std::condition_variable hasTasks_; + std::map queuedTasks_; + TaskQueue genericTasks_; + std::thread::id genericThread_; // execute generic task only. + int genericTaskCount_; + int queuedTaskCount_; + bool isStarted_; + bool isStopping_; // Stop() invoked. + std::condition_variable allThreadsExited_; + + // Thread counter. + int maxThreads_; + int minThreads_; + int curThreads_; + int idleThreads_; +}; +} // namespace DistributedDB + +#endif // TASK_POOL_IMPL_H diff --git a/mock/distributeddb/common/src/task_queue.cpp b/mock/distributeddb/common/src/task_queue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..764ff4a2ac011c4237ed35ad9e4b07610609baee --- /dev/null +++ b/mock/distributeddb/common/src/task_queue.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "task_queue.h" + +namespace DistributedDB { +TaskQueue::TaskQueue(bool lockable) + :lockable_(lockable) +{} + +TaskQueue::~TaskQueue() +{} + +void TaskQueue::PutTask(const Task &task) +{ + if (!task) { + return; + } + tasks_.push(task); +} + +Task TaskQueue::GetTaskAutoLock() +{ + if (lockable_) { + std::thread::id thisId = std::this_thread::get_id(); + if (thisId != lockThread_) { + if (lockThread_ == std::thread::id()) { + lockThread_ = thisId; + } else { + return nullptr; + } + } + } + if (tasks_.empty()) { + ReleaseLock(); + return nullptr; + } + // copy and return + Task task = tasks_.front(); + tasks_.pop(); + return task; +} + +void TaskQueue::ReleaseLock() +{ + if (!lockable_) { + return; + } + if (lockThread_ == std::this_thread::get_id()) { + lockThread_ = std::thread::id(); + } +} + +bool TaskQueue::IsEmptyAndUnlocked() const +{ + if (lockable_) { + if (lockThread_ != std::thread::id()) { + return false; + } + } + return tasks_.empty(); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/task_queue.h b/mock/distributeddb/common/src/task_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..ea8337b7f3c1fa7fe8c47ff481f6f4bcc4acf582 --- /dev/null +++ b/mock/distributeddb/common/src/task_queue.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TASK_QUEUE_H +#define TASK_QUEUE_H + +#include +#include +#include "task_pool.h" + +namespace DistributedDB { +class TaskQueue { +public: + explicit TaskQueue(bool lockable = true); + ~TaskQueue(); + void PutTask(const Task &task); + Task GetTaskAutoLock(); + void ReleaseLock(); + bool IsEmptyAndUnlocked() const; + +private: + bool lockable_; + std::thread::id lockThread_; + std::queue tasks_; +}; +} // namespace DistributedDB + +#endif // TASK_QUEUE_H diff --git a/mock/distributeddb/common/src/time_tick_monitor.cpp b/mock/distributeddb/common/src/time_tick_monitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1bb415e6899cf18cbb78895efb8a5414f6613d4a --- /dev/null +++ b/mock/distributeddb/common/src/time_tick_monitor.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "time_tick_monitor.h" + +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +TimeTickMonitor::TimeTickMonitor() + : timeChangedNotifier_(nullptr), + runtimeCxt_(nullptr), + monitorTimerId_(0), + monitorCallback_(0), + lastMonotonicTime_(0), + lastSystemTime_(0), + isStarted_(false) +{ +} + +TimeTickMonitor::~TimeTickMonitor() +{ + Stop(); + runtimeCxt_ = nullptr; +} + +int TimeTickMonitor::Start() +{ + if (isStarted_) { + return E_OK; + } + + int errCode = PrepareNotifierChain(); + if (errCode != E_OK) { + return errCode; + } + + lastMonotonicTime_ = GetMonotonicTime(); + lastSystemTime_ = GetSysCurrentTime(); + monitorCallback_ = std::bind(&TimeTickMonitor::TimeTick, this, std::placeholders::_1); + runtimeCxt_ = RuntimeContext::GetInstance(); + monitorTimerId_ = 0; + errCode = runtimeCxt_->SetTimer(MONITOR_INTERVAL, monitorCallback_, nullptr, monitorTimerId_); + if (errCode != E_OK) { + return errCode; + } + isStarted_ = true; + return E_OK; +} + +void TimeTickMonitor::Stop() +{ + if (!isStarted_) { + return; + } + + timeChangedNotifier_->UnRegisterEventType(TIME_CHANGE_EVENT); + RefObject::KillAndDecObjRef(timeChangedNotifier_); + timeChangedNotifier_ = nullptr; + runtimeCxt_->RemoveTimer(monitorTimerId_); + isStarted_ = false; +} + +NotificationChain::Listener *TimeTickMonitor::RegisterTimeChangedLister(const TimeChangedAction &action, int &errCode) +{ + if (timeChangedNotifier_ == nullptr) { + errCode = -E_NOT_INIT; + return nullptr; + } + + if (action == nullptr) { + errCode = -E_INVALID_ARGS; + return nullptr; + } + + return timeChangedNotifier_->RegisterListener(TIME_CHANGE_EVENT, action, nullptr, errCode); +} + +int TimeTickMonitor::PrepareNotifierChain() +{ + std::lock_guard autoLock(timeTickMonitorLock_); + if (timeChangedNotifier_ != nullptr) { + return E_OK; + } + + timeChangedNotifier_ = new (std::nothrow) NotificationChain(); + if (timeChangedNotifier_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + + int errCode = timeChangedNotifier_->RegisterEventType(TIME_CHANGE_EVENT); + if (errCode != E_OK) { + RefObject::KillAndDecObjRef(timeChangedNotifier_); + timeChangedNotifier_ = nullptr; + } + return errCode; +} + +int TimeTickMonitor::TimeTick(TimerId timerId) +{ + if (timerId != monitorTimerId_) { + return -E_INVALID_ARGS; + } + + uint64_t monotonicTime = GetMonotonicTime(); + uint64_t systemTime = GetSysCurrentTime(); + int64_t monotonicOffset = static_cast(monotonicTime - lastMonotonicTime_); + int64_t systemOffset = static_cast(systemTime - lastSystemTime_); + lastMonotonicTime_ = monotonicTime; + lastSystemTime_ = systemTime; + int64_t changedOffset = systemOffset - monotonicOffset; + if (std::abs(changedOffset) > MAX_NOISE) { + LOGI("Local system time may be changed! changedOffset %ld", changedOffset); + int ret = RuntimeContext::GetInstance()->ScheduleTask([this, changedOffset](){ + int64_t offset = changedOffset; + timeChangedNotifier_->NotifyEvent(TIME_CHANGE_EVENT, &offset); + }); + if (ret != E_OK) { + LOGE("TimeTickMonitor ScheduleTask failed %d", ret); + } + } + return E_OK; +} + +Timestamp TimeTickMonitor::GetSysCurrentTime() +{ + uint64_t curTime = 0; + int errCode = OS::GetCurrentSysTimeInMicrosecond(curTime); + if (errCode != E_OK) { + LOGE("TimeTickMonitor:get system time failed!"); + return INVALID_TIMESTAMP; + } + return curTime; +} + +Timestamp TimeTickMonitor::GetMonotonicTime() +{ + uint64_t time; + int errCode = OS::GetMonotonicRelativeTimeInMicrosecond(time); + if (errCode != E_OK) { + LOGE("GetMonotonicTime ERR! err = %d", errCode); + return INVALID_TIMESTAMP; + } + return time; +} + +void TimeTickMonitor::NotifyTimeChange(TimeOffset offset) const +{ + std::lock_guard lock(timeTickMonitorLock_); + if (timeChangedNotifier_ == nullptr) { + LOGD("NotifyTimeChange fail, timeChangedNotifier_ is null."); + return; + } + timeChangedNotifier_->NotifyEvent(TIME_CHANGE_EVENT, static_cast(&offset)); +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/common/src/time_tick_monitor.h b/mock/distributeddb/common/src/time_tick_monitor.h new file mode 100644 index 0000000000000000000000000000000000000000..0bb42c5d0c2bebf80724ddd5b8f5f4a2bdd510a1 --- /dev/null +++ b/mock/distributeddb/common/src/time_tick_monitor.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TIME_TICK_MONITOR_H +#define TIME_TICK_MONITOR_H + +#include "runtime_context.h" +#include "db_types.h" +#include "platform_specific.h" +#include "macro_utils.h" + +namespace DistributedDB { +class TimeTickMonitor final { +public: + TimeTickMonitor(); + ~TimeTickMonitor(); + + DISABLE_COPY_ASSIGN_MOVE(TimeTickMonitor); + + // Start the TimeTickMonitor + int Start(); + + // Stop the TimeTickMonitor + void Stop(); + + // Register a time changed lister, it will be callback when local time changed. + NotificationChain::Listener *RegisterTimeChangedLister(const TimeChangedAction &action, int &errCode); + + // Notify TIME_CHANGE_EVENT. + void NotifyTimeChange(TimeOffset offset) const; +private: + static constexpr uint64_t MONITOR_INTERVAL = 1 * 1000; // 1s + static constexpr int64_t MAX_NOISE = 9 * 100 * 1000; // 900ms + static const EventType TIME_CHANGE_EVENT = 1; + static const uint64_t INVALID_TIMESTAMP = 0; + + // Get the current system time + static Timestamp GetSysCurrentTime(); + + // Get the Monotonic time + static Timestamp GetMonotonicTime(); + + // prepare notifier chain + int PrepareNotifierChain(); + + // Callback for the Timer + int TimeTick(TimerId timerId); + + mutable std::mutex timeTickMonitorLock_; + NotificationChain *timeChangedNotifier_; + RuntimeContext *runtimeCxt_; + TimerId monitorTimerId_ = 0; + TimerAction monitorCallback_; + Timestamp lastMonotonicTime_ = 0; + Timestamp lastSystemTime_ = 0; + bool isStarted_ = false; +}; +} // namespace DistributedDB + +#endif // TIME_TICK_MONITOR_H \ No newline at end of file diff --git a/mock/distributeddb/common/src/types_export.cpp b/mock/distributeddb/common/src/types_export.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a4ee25eaf574ec5e1f45e3680316125b9b45fad --- /dev/null +++ b/mock/distributeddb/common/src/types_export.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "types_export.h" + +#include +#include + +namespace DistributedDB { +const size_t CipherPassword::MAX_PASSWORD_SIZE; + +CipherPassword::CipherPassword() +{} + +CipherPassword::~CipherPassword() +{ + (void)Clear(); +} + +bool CipherPassword::operator==(const CipherPassword &input) const +{ + if (size_ != input.GetSize()) { + return false; + } + return memcmp(data_, input.GetData(), size_) == 0; +} + +bool CipherPassword::operator!=(const CipherPassword &input) const +{ + return !(*this == input); +} + +size_t CipherPassword::GetSize() const +{ + return size_; +} + +const uint8_t* CipherPassword::GetData() const +{ + return data_; +} + +int CipherPassword::SetValue(const uint8_t *inputData, size_t inputSize) +{ + if (inputSize > MAX_PASSWORD_SIZE) { + return ErrorCode::OVERSIZE; + } + if (inputSize != 0 && inputData == nullptr) { + return ErrorCode::INVALID_INPUT; + } + + if (inputSize != 0) { + std::copy(inputData, inputData + inputSize, data_); + } + + size_t filledSize = std::min(size_, MAX_PASSWORD_SIZE); + if (inputSize < filledSize) { + std::fill(data_ + inputSize, data_ + filledSize, UCHAR_MAX); + } + + size_ = inputSize; + return ErrorCode::OK; +} + +int CipherPassword::Clear() +{ + return SetValue(nullptr, 0); +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/common/src/user_change_monitor.cpp b/mock/distributeddb/common/src/user_change_monitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f670ce3182d2848be3cbf1def83b4171759e1617 --- /dev/null +++ b/mock/distributeddb/common/src/user_change_monitor.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "user_change_monitor.h" + +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +UserChangeMonitor::UserChangeMonitor() + : userNotifier_(nullptr), + isStarted_(false) +{ +} + +UserChangeMonitor::~UserChangeMonitor() +{ + Stop(); +} + +int UserChangeMonitor::Start() +{ + if (isStarted_) { + return E_OK; + } + + int errCode = PrepareNotifierChain(); + if (errCode != E_OK) { + return errCode; + } + isStarted_ = true; + return E_OK; +} + +void UserChangeMonitor::Stop() +{ + if (!isStarted_) { + return; + } + if (userNotifier_ != nullptr) { + userNotifier_->UnRegisterEventType(USER_ACTIVE_EVENT); + userNotifier_->UnRegisterEventType(USER_NON_ACTIVE_EVENT); + userNotifier_->UnRegisterEventType(USER_ACTIVE_TO_NON_ACTIVE_EVENT); + RefObject::KillAndDecObjRef(userNotifier_); + userNotifier_ = nullptr; + } + isStarted_ = false; +} + +NotificationChain::Listener *UserChangeMonitor::RegisterUserChangedListerner(const UserChangedAction &action, + EventType event, int &errCode) +{ + std::shared_lock lockGuard(userChangeMonitorLock_); + if (action == nullptr) { + errCode = -E_INVALID_ARGS; + return nullptr; + } + if (userNotifier_ == nullptr) { + errCode = -E_NOT_INIT; + return nullptr; + } + LOGI("[UserChangeMonitor] RegisterUserChangedListerner event=%d", event); + return userNotifier_->RegisterListener(event, action, nullptr, errCode); +} + +int UserChangeMonitor::PrepareNotifierChain() +{ + int errCode = E_OK; + std::unique_lock lockGuard(userChangeMonitorLock_); + if (userNotifier_ != nullptr) { + return E_OK; + } + userNotifier_ = new (std::nothrow) NotificationChain(); + if (userNotifier_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + errCode = userNotifier_->RegisterEventType(USER_ACTIVE_EVENT); + if (errCode != E_OK) { + goto ERROR_HANDLE; + } + errCode = userNotifier_->RegisterEventType(USER_NON_ACTIVE_EVENT); + if (errCode != E_OK) { + userNotifier_->UnRegisterEventType(USER_ACTIVE_EVENT); + goto ERROR_HANDLE; + } + errCode = userNotifier_->RegisterEventType(USER_ACTIVE_TO_NON_ACTIVE_EVENT); + if (errCode != E_OK) { + userNotifier_->UnRegisterEventType(USER_ACTIVE_EVENT); + userNotifier_->UnRegisterEventType(USER_NON_ACTIVE_EVENT); + goto ERROR_HANDLE; + } + return errCode; +ERROR_HANDLE: + RefObject::KillAndDecObjRef(userNotifier_); + userNotifier_ = nullptr; + return errCode; +} + +void UserChangeMonitor::NotifyUserChanged() const +{ + std::shared_lock lockGuard(userChangeMonitorLock_); + if (userNotifier_ == nullptr) { + LOGD("NotifyUNotifyUserChangedserChange fail, userChangedNotifier is null."); + return; + } + LOGI("[UserChangeMonitor] begin to notify event"); + userNotifier_->NotifyEvent(USER_ACTIVE_EVENT, nullptr); + userNotifier_->NotifyEvent(USER_NON_ACTIVE_EVENT, nullptr); + userNotifier_->NotifyEvent(USER_ACTIVE_TO_NON_ACTIVE_EVENT, nullptr); +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/common/src/value_object.cpp b/mock/distributeddb/common/src/value_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3ec6ee999f052209e2771ca5334a0b5a3c2cf835 --- /dev/null +++ b/mock/distributeddb/common/src/value_object.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "value_object.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +ValueObject::ValueObject(const ValueObject &other) +{ + isValid_ = other.isValid_; + value_ = other.value_; + dataBeforeOffset_ = other.dataBeforeOffset_; +} + +ValueObject& ValueObject::operator=(const ValueObject &other) +{ + if (&other != this) { + isValid_ = other.isValid_; + value_ = other.value_; + dataBeforeOffset_ = other.dataBeforeOffset_; + } + return *this; +} + +int ValueObject::Parse(const std::string &inString) +{ + if (isValid_) { + return -E_NOT_PERMIT; + } + int errCode = value_.Parse(inString); + isValid_ = (errCode == E_OK); + return errCode; +} + +int ValueObject::Parse(const std::vector &inData) +{ + if (isValid_) { + return -E_NOT_PERMIT; + } + int errCode = value_.Parse(inData); + isValid_ = (errCode == E_OK); + return errCode; +} + +int ValueObject::Parse(const uint8_t *dataBegin, const uint8_t *dataEnd, uint32_t offset) +{ + if (isValid_) { + return -E_NOT_PERMIT; + } + if (dataBegin == nullptr || dataBegin >= dataEnd || offset >= static_cast(dataEnd - dataBegin)) { + LOGE("[Value][Parse] Data range invalid: dataEnd - dataBegin=%" PRId64 ", offset=%" PRIu32, + static_cast(dataEnd - dataBegin), offset); + return -E_INVALID_ARGS; + } + int errCode = value_.Parse(dataBegin + offset, dataEnd); + if (errCode != E_OK) { + return errCode; + } + dataBeforeOffset_.assign(dataBegin, dataBegin + offset); + isValid_ = true; + return E_OK; +} + +bool ValueObject::IsValid() const +{ + return isValid_; +} + +std::string ValueObject::ToString() const +{ + if (dataBeforeOffset_.empty()) { + return value_.ToString(); + } + // It is OK if '\0' exist in dataBeforeOffset_, when call string.size, '\0' will not disturb + std::string outString(dataBeforeOffset_.begin(), dataBeforeOffset_.end()); + outString += value_.ToString(); + return outString; +} + +void ValueObject::WriteIntoVector(std::vector &outData) const +{ + // If not valid, valueStr and dataBeforeOffset_ will be empty + std::string valueStr = value_.ToString(); + // If valid, dataBeforeOffset_ may be empty + outData.insert(outData.end(), dataBeforeOffset_.begin(), dataBeforeOffset_.end()); + outData.insert(outData.end(), valueStr.begin(), valueStr.end()); +} + +bool ValueObject::IsFieldPathExist(const FieldPath &inPath) const +{ + return value_.IsFieldPathExist(inPath); +} + +int ValueObject::GetFieldTypeByFieldPath(const FieldPath &inPath, FieldType &outType) const +{ + if (!isValid_) { + return -E_NOT_PERMIT; + } + return value_.GetFieldTypeByFieldPath(inPath, outType); +} + +int ValueObject::GetFieldValueByFieldPath(const FieldPath &inPath, FieldValue &outValue) const +{ + if (!isValid_) { + return -E_NOT_PERMIT; + } + return value_.GetFieldValueByFieldPath(inPath, outValue); +} + +int ValueObject::GetSubFieldPath(const FieldPath &inPath, std::set &outSubPath) const +{ + if (!isValid_) { + return -E_NOT_PERMIT; + } + return value_.GetSubFieldPath(inPath, outSubPath); +} + +int ValueObject::GetSubFieldPath(const std::set &inPath, std::set &outSubPath) const +{ + if (!isValid_) { + return -E_NOT_PERMIT; + } + return value_.GetSubFieldPath(inPath, outSubPath); +} + +int ValueObject::GetSubFieldPathAndType(const FieldPath &inPath, std::map &outSubPathType) const +{ + if (!isValid_) { + return -E_NOT_PERMIT; + } + return value_.GetSubFieldPathAndType(inPath, outSubPathType); +} + +int ValueObject::GetSubFieldPathAndType(const std::set &inPath, + std::map &outSubPathType) const +{ + if (!isValid_) { + return -E_NOT_PERMIT; + } + return value_.GetSubFieldPathAndType(inPath, outSubPathType); +} + +int ValueObject::InsertField(const FieldPath &inPath, FieldType inType, const FieldValue &inValue) +{ + int errCode = value_.InsertField(inPath, inType, inValue); + if (errCode == E_OK) { + isValid_ = true; + } + return errCode; +} + +int ValueObject::DeleteField(const FieldPath &inPath) +{ + if (!isValid_) { + return -E_NOT_PERMIT; + } + return value_.DeleteField(inPath); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/common/src/zlib_compression.cpp b/mock/distributeddb/common/src/zlib_compression.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28a24db401182731c156e4832eb9aac38539e81f --- /dev/null +++ b/mock/distributeddb/common/src/zlib_compression.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "zlib_compression.h" +#ifndef OMIT_ZLIB +#include + +#include "db_constant.h" +#include "db_errno.h" +#include "log_print.h" +#include "types_export.h" + +namespace DistributedDB { +static ZlibCompression g_zlibInstance; + +ZlibCompression::ZlibCompression() +{ + DataCompression::Register(CompressAlgorithm::ZLIB, this); +} + +int ZlibCompression::Compress(const std::vector &srcData, std::vector &destData) const +{ + auto srcLen = srcData.size(); + auto destLen = compressBound(srcLen); + if (srcLen > DBConstant::MAX_SYNC_BLOCK_SIZE || destLen > DBConstant::MAX_SYNC_BLOCK_SIZE) { + LOGE("Too long to compress, srcLen:%zu, destLen:%lu.", srcLen, destLen); + return -E_INVALID_ARGS; + } + + // Alloc memory. + destData.resize(destLen); + + // Compress. + int errCode = compress(destData.data(), &destLen, srcData.data(), srcLen); + if (errCode != Z_OK) { + LOGE("Compress parcel failed, errCode = %d", errCode); + return -E_SYSTEM_API_FAIL; + } + + destData.resize(destLen); + destData.shrink_to_fit(); + return E_OK; +} + +int ZlibCompression::Uncompress(const std::vector &srcData, std::vector &destData, + uint32_t destLen) const +{ + auto srcLen = srcData.size(); + if (srcLen > DBConstant::MAX_SYNC_BLOCK_SIZE || destLen > DBConstant::MAX_SYNC_BLOCK_SIZE) { + LOGE("Too long to uncompress, srcLen:%zu, destLen:%lu.", srcLen, destLen); + return -E_INVALID_ARGS; + } + + // Alloc dest memory. + destData.resize(destLen); + + // Uncompress. + uLongf destDataLen = destLen; + int errCode = uncompress(destData.data(), &destDataLen, srcData.data(), srcData.size()); + if (errCode != Z_OK) { + LOGE("Uncompress failed, errCode = %d", errCode); + return -E_SYSTEM_API_FAIL; + } + + destData.resize(destDataLen); + destData.shrink_to_fit(); + return E_OK; +} +} // namespace DistributedDB +#endif // OMIT_ZLIB diff --git a/mock/distributeddb/communicator/include/combine_status.h b/mock/distributeddb/communicator/include/combine_status.h new file mode 100644 index 0000000000000000000000000000000000000000..931f84ce0558198ac6feb18fe54a4abe1f812901 --- /dev/null +++ b/mock/distributeddb/communicator/include/combine_status.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMBINE_STATUS_H +#define COMBINE_STATUS_H + +#include +#include + +namespace DistributedDB { +/* + * Class CombineStatus does not support multi-thread. + * It should be protected by mutex in multi-thread environment + */ +class CombineStatus { +public: + void UpdateProgressId(uint64_t inProgressId); + uint64_t GetProgressId() const; + bool CheckProgress(); + + void SetFragmentLen(uint32_t inFragLen); + void SetLastFragmentLen(uint32_t inLastFragLen); + uint32_t GetThisFragmentLength(uint16_t inFragNo) const; + uint32_t GetThisFragmentOffset(uint16_t inFragNo) const; + + void SetFragmentCount(uint16_t inFragCount); + bool IsFragNoAlreadyExist(uint16_t inFragNo) const; + void CheckInFragmentNo(uint16_t inFragNo); + bool IsCombineDone() const; + +private: + uint64_t progressId_ = 0; + bool hasProgressFlag_ = true; + + uint32_t fragmentLen_ = 0; // Indicate the length of fragment that is split from a frame except the last one + uint32_t lastFragmentLen_ = 0; // Indicate the length of the last fragment that is split from a frame + + uint16_t fragmentCount_ = 0; + std::set combinedFragmentNo_; +}; +} // namespace DistributedDB + +#endif // COMBINE_STATUS_H diff --git a/mock/distributeddb/communicator/include/communicator_aggregator.h b/mock/distributeddb/communicator/include/communicator_aggregator.h new file mode 100644 index 0000000000000000000000000000000000000000..30ca55f93cf29aecb48df41b115b33eca8e391ac --- /dev/null +++ b/mock/distributeddb/communicator/include/communicator_aggregator.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATORAGGREGATOR_H +#define COMMUNICATORAGGREGATOR_H + +#include +#include +#include +#include +#include +#include +#include +#include "iadapter.h" +#include "parse_result.h" +#include "icommunicator.h" +#include "frame_combiner.h" +#include "frame_retainer.h" +#include "send_task_scheduler.h" +#include "icommunicator_aggregator.h" + +namespace DistributedDB { +// Forward Declarations +class Communicator; +class SerialBuffer; +class CommunicatorLinker; + +struct TaskConfig { + bool nonBlock; + uint32_t timeout; + Priority prio; +}; + +/* + * Upper layer Module should comply with calling convention, Inner Module interface will not do excessive check + */ +class CommunicatorAggregator : public ICommunicatorAggregator { +public: + CommunicatorAggregator(); + ~CommunicatorAggregator() override; + + DISABLE_COPY_ASSIGN_MOVE(CommunicatorAggregator); + + // See ICommunicatorAggregator for detail + int Initialize(IAdapter *inAdapter) override; + + // Must not call any other functions if Finalize had been called. In fact, Finalize has no chance to be called. + void Finalize() override; + + ICommunicator *AllocCommunicator(uint64_t commLabel, int &outErrorNo) override; + ICommunicator *AllocCommunicator(const LabelType &commLabel, int &outErrorNo) override; + + void ReleaseCommunicator(ICommunicator *inCommunicator) override; + + int RegCommunicatorLackCallback(const CommunicatorLackCallback &onCommLack, const Finalizer &inOper) override; + int RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) override; + + // return optimal allowed data size(Some header is taken into account and subtract) + uint32_t GetCommunicatorAggregatorMtuSize() const; + uint32_t GetCommunicatorAggregatorMtuSize(const std::string &target) const; + + // return timeout in range [5s, 60s] + uint32_t GetCommunicatorAggregatorTimeout() const; + uint32_t GetCommunicatorAggregatorTimeout(const std::string &target) const; + bool IsDeviceOnline(const std::string &device) const; + int GetLocalIdentity(std::string &outTarget) const override; + + // Get the protocol version of remote target. Return -E_NOT_FOUND if no record. + int GetRemoteCommunicatorVersion(const std::string &target, uint16_t &outVersion) const; + + // Called by communicator to make itself really in work + void ActivateCommunicator(const LabelType &commLabel); + + // SerialBuffer surely is heap memory, CreateSendTask responsible for lifecycle + int CreateSendTask(const std::string &dstTarget, SerialBuffer *inBuff, FrameType inType, + const TaskConfig &inConfig, const OnSendEnd &onEnd = nullptr); + + static void EnableCommunicatorNotFoundFeedback(bool isEnable); + + std::shared_ptr GetExtendHeaderHandle(const ExtendInfo ¶mInfo); + +private: + // Working in a dedicated thread + void SendDataRoutine(); + void SendPacketsAndDisposeTask(const SendTask &inTask, + const std::vector>> &eachPacket); + + int RetryUntilTimeout(SendTask &inTask, uint32_t timeout, Priority inPrio); + void TaskFinalizer(const SendTask &inTask, int result); + void NotifySendableToAllCommunicator(); + + // Call from Adapter by register these function + void OnBytesReceive(const std::string &srcTarget, const uint8_t *bytes, uint32_t length, + const std::string &userId); + void OnTargetChange(const std::string &target, bool isConnect); + void OnSendable(const std::string &target); + + void OnFragmentReceive(const std::string &srcTarget, const uint8_t *bytes, uint32_t length, + const ParseResult &inResult, const std::string &userId); + + int OnCommLayerFrameReceive(const std::string &srcTarget, const ParseResult &inResult); + int OnAppLayerFrameReceive(const std::string &srcTarget, const uint8_t *bytes, + uint32_t length, const ParseResult &inResult, const std::string &userId); + int OnAppLayerFrameReceive(const std::string &srcTarget, SerialBuffer *&inFrameBuffer, + const ParseResult &inResult, const std::string &userId); + + // Function with suffix NoMutex should be called with mutex in the caller + int TryDeliverAppLayerFrameToCommunicatorNoMutex(const std::string &srcTarget, SerialBuffer *&inFrameBuffer, + const LabelType &toLabel); + + // Auxiliary function for cutting short primary function + int RegCallbackToAdapter(); + void UnRegCallbackFromAdapter(); + void GenerateLocalSourceId(); + bool ReGenerateLocalSourceIdIfNeed(); + + // Feedback related functions + void TriggerVersionNegotiation(const std::string &dstTarget); + void TryToFeedbackWhenCommunicatorNotFound(const std::string &dstTarget, const LabelType &dstLabel, + const SerialBuffer *inOriFrame); + void TriggerCommunicatorNotFoundFeedback(const std::string &dstTarget, const LabelType &dstLabel, Message* &oriMsg); + + // Record the protocol version of remote target. + void SetRemoteCommunicatorVersion(const std::string &target, uint16_t version); + + DECLARE_OBJECT_TAG(CommunicatorAggregator); + + static std::atomic isCommunicatorNotFoundFeedbackEnable_; + + std::atomic shutdown_; + std::atomic incFrameId_; + std::atomic localSourceId_; + + // Handle related + mutable std::mutex commMapMutex_; + std::map> commMap_; // bool true indicate communicator activated + FrameCombiner combiner_; + FrameRetainer retainer_; + SendTaskScheduler scheduler_; + IAdapter *adapterHandle_ = nullptr; + CommunicatorLinker *commLinker_ = nullptr; + + // Thread related + std::thread exclusiveThread_; + bool wakingSignal_ = false; + mutable std::mutex wakingMutex_; + std::condition_variable wakingCv_; + + // RetryCreateTask related + mutable std::mutex retryMutex_; + std::condition_variable retryCv_; + + // Remote target version related + mutable std::mutex versionMapMutex_; + std::map versionMap_; + + // CommLack Callback related + CommunicatorLackCallback onCommLackHandle_; + Finalizer onCommLackFinalizer_; + mutable std::mutex onCommLackMutex_; + + // Connect Callback related + OnConnectCallback onConnectHandle_; + Finalizer onConnectFinalizer_; + mutable std::mutex onConnectMutex_; +}; +} // namespace DistributedDB + +#endif // COMMUNICATORAGGREGATOR_H diff --git a/mock/distributeddb/communicator/include/communicator_type_define.h b/mock/distributeddb/communicator/include/communicator_type_define.h new file mode 100644 index 0000000000000000000000000000000000000000..af35a296282d15319b3a7ea5070c045955b8fa35 --- /dev/null +++ b/mock/distributeddb/communicator/include/communicator_type_define.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATOR_TYPE_DEFINE_H +#define COMMUNICATOR_TYPE_DEFINE_H + +#include +#include +#include +#include +#include "db_errno.h" + +namespace DistributedDB { +using LabelType = std::vector; +using Finalizer = std::function; +using OnSendEnd = std::function; +using OnConnectCallback = std::function; +constexpr unsigned int COMM_LABEL_LENGTH = 32; // Using SHA256 which length is 32 +constexpr uint32_t MAX_TOTAL_LEN = 104857600; // 100M Limitation For Max Total Length + +template +int RegCallBack(const T &newCallback, T &oldCallback, const Finalizer &newFinalizer, Finalizer &oldFinalizer) +{ + if (newCallback && oldCallback) { + // Already registered, not allowed + return -E_ALREADY_REGISTER; + } + if (newCallback && !oldCallback) { + // Do register + oldCallback = newCallback; + oldFinalizer = newFinalizer; + return E_OK; + } + if (!newCallback && oldCallback) { + // Do unregister + if (oldFinalizer) { + oldFinalizer(); + } + oldCallback = nullptr; + oldFinalizer = nullptr; + return E_OK; + } + return -E_NOT_PERMIT; +} + +enum class Priority { + LOW = 0, // Usually for datasync and its response + NORMAL = 1, // Usually for timesync and its response + HIGH = 2, // Only for communicator inside +}; + +enum class FrameType { + EMPTY = 0, // Used for gossip or help version negotiation + APPLICATION_MESSAGE = 1, + COMMUNICATION_LABEL_EXCHANGE = 2, + COMMUNICATION_LABEL_EXCHANGE_ACK = 3, + INVALID_MAX_FRAME_TYPE = 4, +}; +} // namespace DistributedDB + +#endif // COMMUNICATOR_TYPE_DEFINE_H diff --git a/mock/distributeddb/communicator/include/frame_combiner.h b/mock/distributeddb/communicator/include/frame_combiner.h new file mode 100644 index 0000000000000000000000000000000000000000..33124c497c406cbd976f569d8614838d7af6e688 --- /dev/null +++ b/mock/distributeddb/communicator/include/frame_combiner.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FRAME_COMBINER_H +#define FRAME_COMBINER_H + +#include +#include +#include +#include "semaphore_utils.h" +#include "macro_utils.h" +#include "parse_result.h" +#include "combine_status.h" +#include "runtime_context.h" + +namespace DistributedDB { +class SerialBuffer; // Forward Declarations + +struct CombineWork { + SerialBuffer *buffer; + CombineStatus status; + ParseResult frameInfo; +}; + +class FrameCombiner { +public: + FrameCombiner() = default; // Default constructor must be explicitly provided due to DISABLE_COPY_ASSIGN_MOVE + ~FrameCombiner() = default; // Since constructor must be provided, codedex demand deconstructor be provided as well + DISABLE_COPY_ASSIGN_MOVE(FrameCombiner); + + // Start the timer to supervise the progress + void Initialize(); + + // Clear the CombineWorkPool and stop the timer + void Finalize(); + + // outErrorNo is set E_OK if nothing error happened. + // Return nullptr if error happened or no combination is done. + // Return a valid buffer as well as a valid outFrameResult if combination done. + // The caller is responsible for release the buffer. + SerialBuffer *AssembleFrameFragment(const uint8_t *bytes, uint32_t length, const ParseResult &inPacketInfo, + ParseResult &outFrameInfo, int &outErrorNo); + +private: + // This methed called from timer, it has overallMutex_ protect itself inside the method + void PeriodicalSurveillance(); + + // Following method should be called under protection of overallMutex_ outside the method + int ContinueExistCombineWork(const uint8_t *bytes, uint32_t length, const ParseResult &inPacketInfo); + int CreateNewCombineWork(const uint8_t *bytes, uint32_t length, const ParseResult &inPacketInfo); + void AbortCombineWorkBySource(uint64_t inSourceId); + + bool CheckPacketWithOriWork(const ParseResult &inPacketInfo, const CombineWork &inWork); + SerialBuffer *CreateNewFrameBuffer(const ParseResult &inInfo); + + mutable std::mutex overallMutex_; + + TimerId timerId_ = 0; // 0 is invalid timerId + bool isTimerWork_ = false; + SemaphoreUtils timerRemovedIndicator_ {0}; + uint64_t incProgressId_ = 0; + uint64_t totalSizeByByte_ = 0; + std::map> combineWorkPool_; +}; +} // namespace DistributedDB + +#endif // FRAME_COMBINER_H diff --git a/mock/distributeddb/communicator/include/frame_retainer.h b/mock/distributeddb/communicator/include/frame_retainer.h new file mode 100644 index 0000000000000000000000000000000000000000..259007369b33411447f08540796833d1b75c028e --- /dev/null +++ b/mock/distributeddb/communicator/include/frame_retainer.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FRAME_RETAINER_H +#define FRAME_RETAINER_H + +#include +#include +#include +#include +#include "macro_utils.h" +#include "runtime_context.h" + +namespace DistributedDB { +class SerialBuffer; // Forward Declarations + +struct FrameInfo { + SerialBuffer *buffer; + std::string srcTarget; + LabelType commLabel; + uint32_t frameId; +}; + +struct RetainWork { + SerialBuffer *buffer; + uint32_t frameId; + uint32_t remainTime; // in second +}; + +class FrameRetainer { +public: + FrameRetainer() = default; // Default constructor must be explicitly provided due to DISABLE_COPY_ASSIGN_MOVE + ~FrameRetainer() = default; // Since constructor must be provided, codedex demand deconstructor be provided as well + DISABLE_COPY_ASSIGN_MOVE(FrameRetainer); + + // Start the timer to clear up overtime frames + void Initialize(); + + // Stop the timer and clear the RetainWorkPool + void Finalize(); + + // Always accept the frame, which may be retained actually or perhaps discarded immediately. + void RetainFrame(const FrameInfo &inFrame); + + // Out frames will be in the order of retention. The retainer no longer in charge of the returned frames. + std::list FetchFramesForSpecificCommunicator(const LabelType &inCommLabel); + +private: + // This methed called from timer, it has overallMutex_ protect itself inside the method + void PeriodicalSurveillance(); + + // Following method should be called under protection of overallMutex_ outside the method + void DiscardObsoleteFramesIfNeed(); + void ShrinkRetainWorkPool(); + + mutable std::mutex overallMutex_; + + TimerId timerId_ = 0; // 0 is invalid timerId + bool isTimerWork_ = false; + + uint32_t totalSizeByByte_ = 0; + uint32_t totalRetainFrames_ = 0; + + uint64_t incRetainOrder_ = 0; + std::map>> retainWorkPool_; +}; +} // namespace DistributedDB + +#endif // FRAME_RETAINER_H diff --git a/mock/distributeddb/communicator/include/iadapter.h b/mock/distributeddb/communicator/include/iadapter.h new file mode 100644 index 0000000000000000000000000000000000000000..f238114ca4b16f8c80e900dfa7e09465bfe47c02 --- /dev/null +++ b/mock/distributeddb/communicator/include/iadapter.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IADAPTER_H +#define IADAPTER_H + +#include +#include +#include +#include +#include "communicator_type_define.h" +#include "iprocess_communicator.h" + +namespace DistributedDB { +// SendableCallback only notify when status changed from unsendable to sendable +using BytesReceiveCallback = std::function; +using TargetChangeCallback = std::function; +using SendableCallback = std::function; + +class IAdapter { +public: + // Register all callback before call StartAdapter. + // Return 0 as success. Return negative as error + // The StartAdapter should only be called by its user not owner + virtual int StartAdapter() = 0; + + // The StopAdapter may be called by its user in precondition of StartAdapter success + // The StopAdapter should only be called by its user not owner + virtual void StopAdapter() = 0; + + // Should returns the multiples of 8 + virtual uint32_t GetMtuSize() = 0; + virtual uint32_t GetMtuSize(const std::string &target) = 0; + + // Should returns timeout in range [5s, 60s] + virtual uint32_t GetTimeout() = 0; + virtual uint32_t GetTimeout(const std::string &target) = 0; + + // Get local target name for identify self + virtual int GetLocalIdentity(std::string &outTarget) = 0; + + // Not assume bytes to be heap memory. Not assume SendBytes to be not blocking + // Return 0 as success. Return negative as error + virtual int SendBytes(const std::string &dstTarget, const uint8_t *bytes, uint32_t length) = 0; + + // Pass nullptr as inHandle to do unReg if need (inDecRef also nullptr) + // Return 0 as success. Return negative as error + virtual int RegBytesReceiveCallback(const BytesReceiveCallback &onReceive, const Finalizer &inOper) = 0; + + // Pass nullptr as inHandle to do unReg if need (inDecRef also nullptr) + // Return 0 as success. Return negative as error + virtual int RegTargetChangeCallback(const TargetChangeCallback &onChange, const Finalizer &inOper) = 0; + + // Pass nullptr as inHandle to do unReg if need (inDecRef also nullptr) + // Return 0 as success. Return negative as error + virtual int RegSendableCallback(const SendableCallback &onSendable, const Finalizer &inOper) = 0; + + virtual bool IsDeviceOnline(const std::string &device) = 0; + + virtual std::shared_ptr GetExtendHeaderHandle(const ExtendInfo ¶mInfo) = 0; + + virtual ~IAdapter() {}; +}; +} // namespace DistributedDB + +#endif // IADAPTER_H diff --git a/mock/distributeddb/communicator/include/icommunicator.h b/mock/distributeddb/communicator/include/icommunicator.h new file mode 100644 index 0000000000000000000000000000000000000000..0234c2dc8549c6976ed96885acaa83321ed31006 --- /dev/null +++ b/mock/distributeddb/communicator/include/icommunicator.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ICOMMUNICATOR_H +#define ICOMMUNICATOR_H + +#include +#include +#include "message.h" +#include "ref_object.h" +#include "communicator_type_define.h" +#include "iprocess_communicator.h" +#include "db_properties.h" + +namespace DistributedDB { +// inMsg is heap memory, its ownership transfers by calling OnMessageCallback +using OnMessageCallback = std::function; +constexpr uint32_t SEND_TIME_OUT = 3000; // 3s + +struct SendConfig { + bool nonBlock = false; + bool isNeedExtendHead = false; + uint32_t timeout = SEND_TIME_OUT; + ExtendInfo paramInfo; +}; + +inline void SetSendConfigParam(const DBProperties &dbProperty, const std::string &dstTarget, bool nonBlock, + uint32_t timeout, SendConfig &sendConf) +{ + sendConf.nonBlock = nonBlock; + sendConf.timeout = timeout; + sendConf.isNeedExtendHead = dbProperty.GetBoolProp(DBProperties::SYNC_DUAL_TUPLE_MODE, + false); + sendConf.paramInfo.appId = dbProperty.GetStringProp(DBProperties::APP_ID, ""); + sendConf.paramInfo.userId = dbProperty.GetStringProp(DBProperties::USER_ID, ""); + sendConf.paramInfo.storeId = dbProperty.GetStringProp(DBProperties::STORE_ID, ""); + sendConf.paramInfo.dstTarget = dstTarget; +} + +class ICommunicator : public virtual RefObject { +public: + // Message heap memory + // Return 0 as success. Return negative as error + virtual int RegOnMessageCallback(const OnMessageCallback &onMessage, const Finalizer &inOper) = 0; + virtual int RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) = 0; + virtual int RegOnSendableCallback(const std::function &onSendable, const Finalizer &inOper) = 0; + + virtual void Activate() = 0; + + // return optimal allowed data size(Some header is taken into account and subtract) + virtual uint32_t GetCommunicatorMtuSize() const = 0; + virtual uint32_t GetCommunicatorMtuSize(const std::string &target) const = 0; + + // return timeout in range [5s, 60s] + virtual uint32_t GetTimeout() const = 0; + virtual uint32_t GetTimeout(const std::string &target) const = 0; + + virtual bool IsDeviceOnline(const std::string &device) const = 0; + + // Get local target name for identify self + virtual int GetLocalIdentity(std::string &outTarget) const = 0; + + // Get the protocol version of remote target. Return -E_NOT_FOUND if no record. + virtual int GetRemoteCommunicatorVersion(const std::string &target, uint16_t &outVersion) const = 0; + + // inMsg is heap memory, its ownership transfers by calling SendMessage + // If send fail in SendMessage, nonBlock true will return, nonBlock false will block and retry + // timeout is ignore if nonBlock true. OnSendEnd won't always be called such as when in finalize stage. + // Return 0 as success. Return negative as error + virtual int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config) = 0; + virtual int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config, + const OnSendEnd &onEnd) = 0; // HW Code Regulation do not allow to use default parameters on virtual function + + virtual ~ICommunicator() {}; +}; +} // namespace DistributedDB + +#endif // ICOMMUNICATOR_H diff --git a/mock/distributeddb/communicator/include/icommunicator_aggregator.h b/mock/distributeddb/communicator/include/icommunicator_aggregator.h new file mode 100644 index 0000000000000000000000000000000000000000..ee5c59dfda2febf2acba05f9bc6a86cf63eaf89b --- /dev/null +++ b/mock/distributeddb/communicator/include/icommunicator_aggregator.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ICOMMUNICATORAGGREGATOR_H +#define ICOMMUNICATORAGGREGATOR_H + +#include +#include "iadapter.h" +#include "ref_object.h" +#include "communicator_type_define.h" + +namespace DistributedDB { +class ICommunicator; // Forward Declaration +// Return E_OK to indicate to retain received frame. Do not block during callback. +using CommunicatorLackCallback = std::function; + +class ICommunicatorAggregator : public virtual RefObject { +public: + // Return 0 as success. Return negative as error + // The caller is the owner of inAdapter and responsible for manage its lifecycle. + // The ICommunicatorAggregator is only the user of inAdapter + // If Initialize fail, the ICommunicatorAggregator will rollback what had done to inAdapter so it can be reuse. + virtual int Initialize(IAdapter *inAdapter) = 0; + + // Call this method after Initialize successfully and before destroy the ICommunicatorAggregator + // Emphasize again : DO NOT CALL Finalize IF Initialize FAIL. + // Must not call any other functions if Finalize had been called. + // More likely, The Finalize has no chance to be called. since it is process level. + virtual void Finalize() = 0; + + // If not success, return nullptr and set outErrorNo + virtual ICommunicator *AllocCommunicator(uint64_t commLabel, int &outErrorNo) = 0; + virtual ICommunicator *AllocCommunicator(const LabelType &commLabel, int &outErrorNo) = 0; + virtual void ReleaseCommunicator(ICommunicator *inCommunicator) = 0; + virtual int RegCommunicatorLackCallback(const CommunicatorLackCallback &onCommLack, const Finalizer &inOper) = 0; + virtual int RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) = 0; + virtual int GetLocalIdentity(std::string &outTarget) const = 0; + virtual ~ICommunicatorAggregator() {}; +}; +} // namespace DistributedDB + +#endif // ICOMMUNICATORAGGREGATOR_H diff --git a/mock/distributeddb/communicator/include/message.h b/mock/distributeddb/communicator/include/message.h new file mode 100644 index 0000000000000000000000000000000000000000..416e083169cfcdc97e4f464f378ee21e9820d6af --- /dev/null +++ b/mock/distributeddb/communicator/include/message.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MESSAGE_H +#define MESSAGE_H + +#include +#include +#include +#include "db_errno.h" +#include "macro_utils.h" +#include "object_holder.h" +#include "object_holder_typed.h" +#include "communicator_type_define.h" + +namespace DistributedDB { +constexpr uint32_t INVALID_MESSAGE_ID = 0; +constexpr uint16_t TYPE_INVALID = 0; +constexpr uint16_t TYPE_REQUEST = 1; +constexpr uint16_t TYPE_RESPONSE = 2; +constexpr uint16_t TYPE_NOTIFY = 3; +constexpr uint32_t NO_ERROR = 0; +constexpr uint16_t MSG_VERSION_BASE = 0; +constexpr uint16_t MSG_VERSION_EXT = 1; + +class Message { +public: + Message() = default; + + explicit Message(uint32_t inMsgId) + { + messageId_ = inMsgId; + } + + ~Message() + { + if (holderPtr_ != nullptr) { + delete holderPtr_; + holderPtr_ = nullptr; + } + } + + DISABLE_COPY_ASSIGN_MOVE(Message); + + // For user convenience, inObj can be a stack object, provided that it supports copy construct + // Set Object again will delete object that set before if successfully, otherwise impact no change + template + int SetCopiedObject(const T &inObj) + { + T *copiedObject = new (std::nothrow) T(inObj); + if (copiedObject == nullptr) { + return -E_OUT_OF_MEMORY; + } + ObjectHolder *tmpHolderPtr = new (std::nothrow) ObjectHolderTyped(copiedObject); + if (tmpHolderPtr == nullptr) { + delete copiedObject; + return -E_OUT_OF_MEMORY; + } + if (holderPtr_ != nullptr) { + delete holderPtr_; + } + holderPtr_ = tmpHolderPtr; + return E_OK; + } + + // By calling this method successfully, The ownership of inObj will be taken up by this class + // Thus this class is responsible for delete the inObj + // If calling this method unsuccessfully, The ownership of inObj is not changed + // Set Object again will delete object that set before if successfully, otherwise impact no change + template + int SetExternalObject(T *&inObj) + { + if (inObj == nullptr) { + return -E_INVALID_ARGS; + } + ObjectHolder *tmpHolderPtr = new (std::nothrow) ObjectHolderTyped(inObj); + if (tmpHolderPtr == nullptr) { + return -E_OUT_OF_MEMORY; + } + if (holderPtr_ != nullptr) { + delete holderPtr_; + } + holderPtr_ = tmpHolderPtr; + inObj = nullptr; + return E_OK; + } + + // Calling this method in form of GetObject() to specify return type based on the MessageId + template + const T *GetObject() const + { + if (holderPtr_ == nullptr) { + return nullptr; + } + ObjectHolderTyped *realHolderPtr = static_cast *>(holderPtr_); + return realHolderPtr->GetObject(); + } + + int SetMessageType(uint16_t inMsgType) + { + if (inMsgType != TYPE_REQUEST && inMsgType != TYPE_RESPONSE && inMsgType != TYPE_NOTIFY) { + return -E_INVALID_ARGS; + } + messageType_ = inMsgType; + return E_OK; + } + + void SetMessageId(uint32_t inMessageId) + { + messageId_ = inMessageId; + } + + void SetSessionId(uint32_t inSessionId) + { + sessionId_ = inSessionId; + } + + void SetSequenceId(uint32_t inSequenceId) + { + sequenceId_ = inSequenceId; + } + + void SetErrorNo(uint32_t inErrorNo) + { + errorNo_ = inErrorNo; + } + + void SetTarget(const std::string &inTarget) + { + target_ = inTarget; + } + + void SetPriority(Priority inPriority) + { + prio_ = inPriority; + } + + void SetVersion(uint16_t inVersion) + { + if (inVersion != MSG_VERSION_BASE && inVersion != MSG_VERSION_EXT) { + return; + } + version_ = inVersion; + } + + uint16_t GetMessageType() const + { + return messageType_; + } + + uint32_t GetMessageId() const + { + return messageId_; + } + + uint32_t GetSessionId() const + { + return sessionId_; + } + + uint32_t GetSequenceId() const + { + return sequenceId_; + } + + uint32_t GetErrorNo() const + { + return errorNo_; + } + + std::string GetTarget() const + { + return target_; + } + + Priority GetPriority() const + { + return prio_; + } + + uint16_t GetVersion() const + { + return version_; + } + + bool IsFeedbackError() const + { + return (errorNo_ == E_FEEDBACK_UNKNOWN_MESSAGE || errorNo_ == E_FEEDBACK_COMMUNICATOR_NOT_FOUND); + } + +private: + // Field or content that will be serialized for bytes transfer + uint16_t version_ = MSG_VERSION_BASE; + uint16_t messageType_ = TYPE_INVALID; + uint32_t messageId_ = INVALID_MESSAGE_ID; + uint32_t sessionId_ = 0; // Distinguish different conversation + uint32_t sequenceId_ = 0; // Distinguish different message even in same session with same content in retry case + uint32_t errorNo_ = NO_ERROR; + ObjectHolder *holderPtr_ = nullptr; + + // Field carry supplemental info + std::string target_; + Priority prio_ = Priority::LOW; +}; +} // namespace DistributedDB + +#endif // MESSAGE_H diff --git a/mock/distributeddb/communicator/include/message_transform.h b/mock/distributeddb/communicator/include/message_transform.h new file mode 100644 index 0000000000000000000000000000000000000000..59b4503804b81075f797bd9207979a2e5bfcd2fb --- /dev/null +++ b/mock/distributeddb/communicator/include/message_transform.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MESSAGE_TRANSFORM_H +#define MESSAGE_TRANSFORM_H + +#include +#include +#include +#include "message.h" + +namespace DistributedDB { +using ComputeLengthFunc = std::function; +using SerializeFunc = std::function; +using DeserializeFunc = std::function; + +struct TransformFunc { + ComputeLengthFunc computeFunc; + SerializeFunc serializeFunc; + DeserializeFunc deserializeFunc; +}; + +class MessageTransform { +public: + // Must not be called in multi-thread + // Return E_ALREADY_REGISTER if msgId is already registered + // Return E_INVALID_ARGS if member of inFunc not all valid + // Calling ProtocolProto::RegTransformFunction + static int RegTransformFunction(uint32_t msgId, const TransformFunc &inFunc); +}; +} + +#endif // MESSAGE_TRANSFORM_H \ No newline at end of file diff --git a/mock/distributeddb/communicator/include/network_adapter.h b/mock/distributeddb/communicator/include/network_adapter.h new file mode 100644 index 0000000000000000000000000000000000000000..580e48c0deacd6ce296b702f60b5ff6a83bca80b --- /dev/null +++ b/mock/distributeddb/communicator/include/network_adapter.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NETWORK_ADAPTER_H +#define NETWORK_ADAPTER_H + +#include +#include +#include +#include +#include +#include +#include "iadapter.h" +#include "iprocess_communicator.h" + +namespace DistributedDB { +class NetworkAdapter : public IAdapter { +public: + NetworkAdapter(); + explicit NetworkAdapter(const std::string &inProcessLabel); + NetworkAdapter(const std::string &inProcessLabel, const std::shared_ptr &inCommunicator); + + ~NetworkAdapter() override; + + int StartAdapter() override; + void StopAdapter() override; + + uint32_t GetMtuSize() override; + uint32_t GetMtuSize(const std::string &target) override; + + uint32_t GetTimeout() override; + uint32_t GetTimeout(const std::string &target) override; + int GetLocalIdentity(std::string &outTarget) override; + + int SendBytes(const std::string &dstTarget, const uint8_t *bytes, uint32_t length) override; + + int RegBytesReceiveCallback(const BytesReceiveCallback &onReceive, const Finalizer &inOper) override; + int RegTargetChangeCallback(const TargetChangeCallback &onChange, const Finalizer &inOper) override; + int RegSendableCallback(const SendableCallback &onSendable, const Finalizer &inOper) override; + + bool IsDeviceOnline(const std::string &device) override; + std::shared_ptr GetExtendHeaderHandle(const ExtendInfo ¶mInfo) override; + +private: + void OnDataReceiveHandler(const DeviceInfos &srcDevInfo, const uint8_t *data, uint32_t length); + void OnDeviceChangeHandler(const DeviceInfos &devInfo, bool isOnline); + + void SearchOnlineRemoteDeviceAtStartup(); + void CheckDeviceOnlineAfterReception(const DeviceInfos &devInfo); + void CheckDeviceOfflineAfterSendFail(const DeviceInfos &devInfo); + + std::string processLabel_; + std::shared_ptr processCommunicator_; + + // For protecting "LocalIdentity" and "MtuSize", these info only need to get from peripheral interface once + mutable std::mutex identityMutex_; + + std::string localIdentity_; + mutable std::mutex mtuSizeMutex_; + bool isMtuSizeValid_ = false; + uint32_t mtuSize_ = 0; + std::map devMapMtuSize_; + + mutable std::mutex onlineRemoteDevMutex_; + std::set onlineRemoteDev_; // Refer to devices that has peer process + + std::atomic pendingAsyncTaskCount_{0}; + mutable std::mutex asyncTaskDoneMutex_; + std::condition_variable asyncTaskDoneCv_; + + BytesReceiveCallback onReceiveHandle_; + TargetChangeCallback onChangeHandle_; + SendableCallback onSendableHandle_; + Finalizer onReceiveFinalizer_; + Finalizer onChangeFinalizer_; + Finalizer onSendableFinalizer_; + mutable std::mutex onReceiveMutex_; + mutable std::mutex onChangeMutex_; + mutable std::mutex onSendableMutex_; +}; +} // namespace DistributedDB + +#endif diff --git a/mock/distributeddb/communicator/include/object_holder.h b/mock/distributeddb/communicator/include/object_holder.h new file mode 100644 index 0000000000000000000000000000000000000000..348eab4b50c5738b9929ceb21dd0606e863ed0de --- /dev/null +++ b/mock/distributeddb/communicator/include/object_holder.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBJECTHOLDER_H +#define OBJECTHOLDER_H + +namespace DistributedDB { +class ObjectHolder { +public: + virtual ~ObjectHolder() {}; +}; +} // namespace DistributedDB + +#endif // OBJECTHOLDER_H diff --git a/mock/distributeddb/communicator/include/object_holder_typed.h b/mock/distributeddb/communicator/include/object_holder_typed.h new file mode 100644 index 0000000000000000000000000000000000000000..4156e3dc7b95b73db9cccd7cb5fde8167083b0bb --- /dev/null +++ b/mock/distributeddb/communicator/include/object_holder_typed.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBJECTHOLDERTYPED_H +#define OBJECTHOLDERTYPED_H + +#include "object_holder.h" + +namespace DistributedDB { +template +class ObjectHolderTyped : public ObjectHolder { +public: + // Accept a heap object + explicit ObjectHolderTyped(T *inObject) + { + objectPtr_ = inObject; + } + + ~ObjectHolderTyped() override + { + if (objectPtr_ != nullptr) { + delete objectPtr_; + objectPtr_ = nullptr; + } + } + + const T *GetObject() const + { + return objectPtr_; + } +private: + T *objectPtr_ = nullptr; +}; +} // namespace DistributedDB + +#endif // OBJECTHOLDERTYPED_H diff --git a/mock/distributeddb/communicator/include/parse_result.h b/mock/distributeddb/communicator/include/parse_result.h new file mode 100644 index 0000000000000000000000000000000000000000..0e1278821a1492a77cf03e5780ac876fcb4a60ec --- /dev/null +++ b/mock/distributeddb/communicator/include/parse_result.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PARSE_RESULT_H +#define PARSE_RESULT_H + +#include +#include +#include "communicator_type_define.h" + +namespace DistributedDB { +class ParseResult { +public: + void SetFrameId(uint32_t inFrameId) + { + frameId_ = inFrameId; + } + void SetSourceId(uint64_t inSourceId) + { + sourceId_ = inSourceId; + } + void SetPacketLen(uint32_t inPacketLen) + { + packetLen_ = inPacketLen; + } + void SetPaddingLen(uint32_t inPaddingLen) + { + paddingLen_ = inPaddingLen; + } + void SetFragmentFlag(bool inFlag) + { + isFragment_ = inFlag; + } + void SetFrameTypeInfo(FrameType inFrameType) + { + frameType_ = inFrameType; + } + void SetFrameLen(uint32_t inFrameLen) + { + frameLen_ = inFrameLen; + } + void SetFragCount(uint16_t inFragCount) + { + fragCount_ = inFragCount; + } + void SetFragNo(uint16_t inFragNo) + { + fragNo_ = inFragNo; + } + void SetPayloadLen(uint32_t inPayloadLen) + { + payloadLen_ = inPayloadLen; + } + void SetCommLabel(const LabelType &inCommLabel) + { + commLabel_ = inCommLabel; + } + void SetLabelExchangeDistinctValue(uint64_t inDistinctValue) + { + labelExchangeDistinctValue_ = inDistinctValue; + } + void SetLabelExchangeSequenceId(uint64_t inSequenceId) + { + labelExchangeSequenceId_ = inSequenceId; + } + void SetLatestCommLabels(const std::set &inLatestCommLabels) + { + latestCommLabels_ = inLatestCommLabels; + } + + uint32_t GetFrameId() const + { + return frameId_; + } + uint64_t GetSourceId() const + { + return sourceId_; + } + uint32_t GetPacketLen() const + { + return packetLen_; + } + uint32_t GetPaddingLen() const + { + return paddingLen_; + } + bool IsFragment() const + { + return isFragment_; + } + FrameType GetFrameTypeInfo() const + { + return frameType_; + } + uint32_t GetFrameLen() const + { + return frameLen_; + } + uint16_t GetFragCount() const + { + return fragCount_; + } + uint16_t GetFragNo() const + { + return fragNo_; + } + uint32_t GetPayloadLen() const + { + return payloadLen_; + } + LabelType GetCommLabel() const + { + return commLabel_; + } + uint64_t GetLabelExchangeDistinctValue() const + { + return labelExchangeDistinctValue_; + } + uint64_t GetLabelExchangeSequenceId() const + { + return labelExchangeSequenceId_; + } + const std::set& GetLatestCommLabels() const + { + return latestCommLabels_; + } + + void SetDbVersion(uint16_t dbVersion) + { + dbVersion_ = dbVersion; + } + + uint16_t GetDbVersion() const + { + return dbVersion_; + } +private: + // For CommPhyHeader + uint32_t frameId_ = 0; + uint64_t sourceId_ = 0; + uint32_t packetLen_ = 0; + uint8_t paddingLen_ = 0; + bool isFragment_ = false; + FrameType frameType_ = FrameType::INVALID_MAX_FRAME_TYPE; + + // For CommPhyOptHeader + uint32_t frameLen_ = 0; + uint16_t fragCount_ = 0; + uint16_t fragNo_ = 0; + + // For Application Layer Frame + uint32_t payloadLen_ = 0; + LabelType commLabel_; + + // For Communication Layer Frame + uint64_t labelExchangeDistinctValue_ = 0; // For Both LabelExchange And LabelExchangeAck Frame + uint64_t labelExchangeSequenceId_ = 0; // For Both LabelExchange And LabelExchangeAck Frame + std::set latestCommLabels_; // For Only LabelExchange Frame + uint16_t dbVersion_ = 0; +}; +} + +#endif // PARSE_RESULT_H diff --git a/mock/distributeddb/communicator/include/send_task_scheduler.h b/mock/distributeddb/communicator/include/send_task_scheduler.h new file mode 100644 index 0000000000000000000000000000000000000000..8c1e3e12ec32d62d6dd55ad06ba06e00473b05fa --- /dev/null +++ b/mock/distributeddb/communicator/include/send_task_scheduler.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SEND_TASK_SCHEDULER_H +#define SEND_TASK_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include "macro_utils.h" +#include "communicator_type_define.h" + +namespace DistributedDB { +enum class TargetPolicy { + NO_DELAY = 0, + DELAY = 1, +}; + +class SerialBuffer; // Forward Declaration + +struct SendTask { + SerialBuffer *buffer; + std::string dstTarget; + OnSendEnd onEnd; +}; + +struct SendTaskInfo { + bool delayFlag; + Priority taskPrio; +}; + +using TaskListByTarget = std::map>; + +class SendTaskScheduler { +public: + SendTaskScheduler() = default; // Default constructor must be explicitly provided due to DISABLE_COPY_ASSIGN_MOVE + ~SendTaskScheduler(); + + DISABLE_COPY_ASSIGN_MOVE(SendTaskScheduler); + + void Initialize(); + + // This method for consumer + void Finalize(); + + // This method for producer, support multiple thread + int AddSendTaskIntoSchedule(const SendTask &inTask, Priority inPrio); + + // This method for consumer, not recommend for multiple thread + int ScheduleOutSendTask(SendTask &outTask); + int ScheduleOutSendTask(SendTask &outTask, SendTaskInfo &outTaskInfo); + + // This method for consumer, call ScheduleOutSendTask at least one time before each calling this + int FinalizeLastScheduleTask(); + + // These two mothods influence the task that will be schedule out next time + int DelayTaskByTarget(const std::string &inTarget); + int NoDelayTaskByTarget(const std::string &inTarget); + + uint32_t GetTotalTaskCount() const; + uint32_t GetNoDelayTaskCount() const; + +private: + int ScheduleDelayTask(SendTask &outTask, SendTaskInfo &outTaskInfo); + int ScheduleNoDelayTask(SendTask &outTask, SendTaskInfo &outTaskInfo); + + mutable std::mutex overallMutex_; + uint32_t curTotalSizeByByte_ = 0; + uint32_t curTotalSizeByTask_ = 0; + uint32_t delayTaskCount_ = 0; + + std::vector priorityOrder_; + std::map extraCapacityInByteByPrio_; + std::map policyMap_; + + std::map taskCountByPrio_; + std::map taskDelayCountByPrio_; + std::map> taskOrderByPrio_; + std::map taskGroupByPrio_; + + bool scheduledFlag_ = false; + std::string lastScheduleTarget_; + Priority lastSchedulePriority_ = Priority::LOW; +}; +} + +#endif \ No newline at end of file diff --git a/mock/distributeddb/communicator/src/combine_status.cpp b/mock/distributeddb/communicator/src/combine_status.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3ecf81e66a6c39b7f922b220ddffc4962efd0c1 --- /dev/null +++ b/mock/distributeddb/communicator/src/combine_status.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "combine_status.h" + +namespace DistributedDB { +void CombineStatus::UpdateProgressId(uint64_t inProgressId) +{ + progressId_ = inProgressId; + hasProgressFlag_ = true; +} + +uint64_t CombineStatus::GetProgressId() const +{ + return progressId_; +} + +bool CombineStatus::CheckProgress() +{ + bool preFlag = hasProgressFlag_; + hasProgressFlag_ = false; + return preFlag; +} + +void CombineStatus::SetFragmentLen(uint32_t inFragLen) +{ + fragmentLen_ = inFragLen; +} + +void CombineStatus::SetLastFragmentLen(uint32_t inLastFragLen) +{ + lastFragmentLen_ = inLastFragLen; +} + +uint32_t CombineStatus::GetThisFragmentLength(uint16_t inFragNo) const +{ + // It had already been checked outside that inFragNo smaller than fragmentCount_ + return ((inFragNo != fragmentCount_ - 1) ? fragmentLen_ : lastFragmentLen_); // subtract by 1 for index +} + +uint32_t CombineStatus::GetThisFragmentOffset(uint16_t inFragNo) const +{ + // It had already been checked outside that inFragNo smaller than fragmentCount_ + return fragmentLen_ * inFragNo; // It can be guaranteed no overflow will happen by multiply +} + +void CombineStatus::SetFragmentCount(uint16_t inFragCount) +{ + fragmentCount_ = inFragCount; +} + +bool CombineStatus::IsFragNoAlreadyExist(uint16_t inFragNo) const +{ + return (combinedFragmentNo_.count(inFragNo) != 0) ? true : false; +} + +void CombineStatus::CheckInFragmentNo(uint16_t inFragNo) +{ + if (inFragNo >= fragmentCount_) { + return; + } + combinedFragmentNo_.insert(inFragNo); +} + +bool CombineStatus::IsCombineDone() const +{ + return (combinedFragmentNo_.size() >= fragmentCount_); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/communicator/src/communicator.cpp b/mock/distributeddb/communicator/src/communicator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5c97f4126738d07594a7101a7fe5464d81b48c3 --- /dev/null +++ b/mock/distributeddb/communicator/src/communicator.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "communicator.h" +#include "db_common.h" +#include "log_print.h" +#include "protocol_proto.h" + +namespace DistributedDB { +Communicator::Communicator(CommunicatorAggregator *inCommAggregator, const LabelType &inLabel) + : commAggrHandle_(inCommAggregator), commLabel_(inLabel) +{ + RefObject::IncObjRef(commAggrHandle_); // Rely on CommunicatorAggregator, hold its reference. +} + +Communicator:: ~Communicator() +{ + RefObject::DecObjRef(commAggrHandle_); // Communicator no longer hold the reference of CommunicatorAggregator. + onMessageHandle_ = nullptr; + onConnectHandle_ = nullptr; + onSendableHandle_ = nullptr; + commAggrHandle_ = nullptr; +} + +int Communicator::RegOnMessageCallback(const OnMessageCallback &onMessage, const Finalizer &inOper) +{ + std::lock_guard messageHandleLockGuard(messageHandleMutex_); + return RegCallBack(onMessage, onMessageHandle_, inOper, onMessageFinalizer_); +} + +int Communicator::RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) +{ + std::lock_guard connectHandleLockGuard(connectHandleMutex_); + int errCode = RegCallBack(onConnect, onConnectHandle_, inOper, onConnectFinalizer_); + if (onConnect && errCode == E_OK) { + // Register action and success + for (auto &entry : onlineTargets_) { + LOGI("[Comm][RegConnect] Label=%.6s, online target=%s{private}.", VEC_TO_STR(commLabel_), entry.c_str()); + onConnectHandle_(entry, true); + } + } + return errCode; +} + +int Communicator::RegOnSendableCallback(const std::function &onSendable, const Finalizer &inOper) +{ + std::lock_guard sendableHandleLockGuard(sendableHandleMutex_); + return RegCallBack(onSendable, onSendableHandle_, inOper, onSendableFinalizer_); +} + +void Communicator::Activate() +{ + commAggrHandle_->ActivateCommunicator(commLabel_); +} + +uint32_t Communicator::GetCommunicatorMtuSize() const +{ + return commAggrHandle_->GetCommunicatorAggregatorMtuSize(); +} + +uint32_t Communicator::GetCommunicatorMtuSize(const std::string &target) const +{ + return commAggrHandle_->GetCommunicatorAggregatorMtuSize(target); +} + +int Communicator::GetLocalIdentity(std::string &outTarget) const +{ + return commAggrHandle_->GetLocalIdentity(outTarget); +} + +uint32_t Communicator::GetTimeout() const +{ + return commAggrHandle_->GetCommunicatorAggregatorTimeout(); +} + +uint32_t Communicator::GetTimeout(const std::string &target) const +{ + return commAggrHandle_->GetCommunicatorAggregatorTimeout(target); +} + +bool Communicator::IsDeviceOnline(const std::string &device) const +{ + return commAggrHandle_->IsDeviceOnline(device); +} + +int Communicator::SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config) +{ + return SendMessage(dstTarget, inMsg, config, nullptr); +} + +int Communicator::SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config, + const OnSendEnd &onEnd) +{ + if (dstTarget.empty() || inMsg == nullptr) { + return -E_INVALID_ARGS; + } + std::shared_ptr extendHandle = nullptr; + if (config.isNeedExtendHead) { + extendHandle = commAggrHandle_->GetExtendHeaderHandle(config.paramInfo); + if (extendHandle == nullptr) { + LOGE("[Comm][Send] get extendHandle failed"); + return -E_FEEDBACK_COMMUNICATOR_NOT_FOUND; + } + } + int error = E_OK; + // if error is not E_OK , null pointer will be returned + SerialBuffer *buffer = ProtocolProto::ToSerialBuffer(inMsg, error, extendHandle, false); + extendHandle = nullptr; + if (error != E_OK) { + LOGE("[Comm][Send] Serial fail, label=%s, error=%d.", VEC_TO_STR(commLabel_), error); + return error; + } + int errCode = ProtocolProto::SetDivergeHeader(buffer, commLabel_); + if (errCode != E_OK) { + LOGE("[Comm][Send] Set header fail, label=%s, errCode=%d.", VEC_TO_STR(commLabel_), errCode); + delete buffer; + buffer = nullptr; + return errCode; + } + + TaskConfig taskConfig {config.nonBlock, config.timeout, inMsg->GetPriority()}; + errCode = commAggrHandle_->CreateSendTask(dstTarget, buffer, FrameType::APPLICATION_MESSAGE, taskConfig, onEnd); + if (errCode == E_OK) { + // if ok, free inMsg, otherwise the caller should take over inMsg + delete inMsg; + inMsg = nullptr; + } else { + // if send fails, free buffer, otherwise buffer should be taked over by comminucator aggregator + delete buffer; + buffer = nullptr; + } + return errCode; +} + +void Communicator::OnBufferReceive(const std::string &srcTarget, const SerialBuffer *inBuf) +{ + std::lock_guard messageHandleLockGuard(messageHandleMutex_); + if (srcTarget.size() != 0 && inBuf != nullptr && onMessageHandle_) { + int error = E_OK; + // if error is not E_OK, null pointer will be returned + Message *message = ProtocolProto::ToMessage(inBuf, error); + delete inBuf; + inBuf = nullptr; + // message is not nullptr if error is E_OK or error is E_NOT_REGISTER. + // for the former case the message will be handled and release by sync module. + // for the latter case the message is released in TriggerUnknownMessageFeedback. + if (error != E_OK) { + LOGE("[Comm][Receive] ToMessage fail, label=%s, error=%d.", VEC_TO_STR(commLabel_), error); + if (error == -E_VERSION_NOT_SUPPORT) { + TriggerVersionNegotiation(srcTarget); + } else if (error == -E_NOT_REGISTER) { + TriggerUnknownMessageFeedback(srcTarget, message); + } + return; + } + LOGI("[Comm][Receive] label=%s, srcTarget=%s{private}.", VEC_TO_STR(commLabel_), srcTarget.c_str()); + onMessageHandle_(srcTarget, message); + } else { + LOGE("[Comm][Receive] label=%s, src.size=%zu or buf or handle invalid.", VEC_TO_STR(commLabel_), + srcTarget.size()); + if (inBuf != nullptr) { + delete inBuf; + inBuf = nullptr; + } + } +} + +void Communicator::OnConnectChange(const std::string &target, bool isConnect) +{ + std::lock_guard connectHandleLockGuard(connectHandleMutex_); + if (target.size() == 0) { + LOGE("[Comm][Connect] Target size zero, label=%s.", VEC_TO_STR(commLabel_)); + return; + } + if (isConnect) { + onlineTargets_.insert(target); + } else { + onlineTargets_.erase(target); + } + LOGI("[Comm][Connect] Label=%s, target=%s{private}, Online=%d", VEC_TO_STR(commLabel_), target.c_str(), isConnect); + if (onConnectHandle_) { + onConnectHandle_(target, isConnect); + } else { + LOGI("[Comm][Connect] Handle invalid currently."); + } +} + +void Communicator::OnSendAvailable() +{ + std::lock_guard sendableHandleLockGuard(sendableHandleMutex_); + if (onSendableHandle_) { + onSendableHandle_(); + } +} + +LabelType Communicator::GetCommunicatorLabel() const +{ + return commLabel_; +} + +int Communicator::GetRemoteCommunicatorVersion(const std::string &target, uint16_t &outVersion) const +{ + return commAggrHandle_->GetRemoteCommunicatorVersion(target, outVersion); +} + +void Communicator::TriggerVersionNegotiation(const std::string &dstTarget) +{ + LOGI("[Comm][TrigVer] Do version negotiate with target=%s{private}.", dstTarget.c_str()); + int errCode = E_OK; + SerialBuffer *buffer = ProtocolProto::BuildEmptyFrameForVersionNegotiate(errCode); + if (errCode != E_OK) { + LOGE("[Comm][TrigVer] Build empty frame fail, errCode=%d", errCode); + return; + } + + TaskConfig config{true, 0, Priority::HIGH}; + errCode = commAggrHandle_->CreateSendTask(dstTarget, buffer, FrameType::EMPTY, config); + if (errCode != E_OK) { + LOGE("[Comm][TrigVer] Send empty frame fail, errCode=%d", errCode); + // if send fails, free buffer, otherwise buffer will be taked over by comminucator aggregator + delete buffer; + buffer = nullptr; + } +} + +void Communicator::TriggerUnknownMessageFeedback(const std::string &dstTarget, Message* &oriMsg) +{ + if (oriMsg == nullptr || oriMsg->GetMessageType() != TYPE_REQUEST) { + LOGI("[Comm][TrigFeedback] Do nothing for unknown message with type not request."); + // Do not have to do feedback if the message is not a request type message + delete oriMsg; + oriMsg = nullptr; + return; + } + + LOGI("[Comm][TrigFeedback] Do unknown message feedback with target=%s{private}.", dstTarget.c_str()); + oriMsg->SetMessageType(TYPE_RESPONSE); + oriMsg->SetErrorNo(E_FEEDBACK_UNKNOWN_MESSAGE); + + int errCode = E_OK; + SerialBuffer *buffer = ProtocolProto::BuildFeedbackMessageFrame(oriMsg, commLabel_, errCode); + delete oriMsg; + oriMsg = nullptr; + if (errCode != E_OK) { + LOGE("[Comm][TrigFeedback] Build unknown message feedback frame fail, errCode=%d", errCode); + return; + } + + TaskConfig config{true, 0, Priority::HIGH}; + errCode = commAggrHandle_->CreateSendTask(dstTarget, buffer, FrameType::APPLICATION_MESSAGE, config); + if (errCode != E_OK) { + LOGE("[Comm][TrigFeedback] Send unknown message feedback frame fail, errCode=%d", errCode); + // if send fails, free buffer, otherwise buffer will be taked over by comminucator aggregator + delete buffer; + buffer = nullptr; + } +} + +DEFINE_OBJECT_TAG_FACILITIES(Communicator) +} // namespace DistributedDB diff --git a/mock/distributeddb/communicator/src/communicator.h b/mock/distributeddb/communicator/src/communicator.h new file mode 100644 index 0000000000000000000000000000000000000000..c1f2ce39d4a8139905cfa57a6acf19e31b0e90be --- /dev/null +++ b/mock/distributeddb/communicator/src/communicator.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATOR_H +#define COMMUNICATOR_H + +#include +#include +#include +#include +#include +#include +#include +#include "serial_buffer.h" +#include "icommunicator.h" +#include "communicator_aggregator.h" + +namespace DistributedDB { +class Communicator : public ICommunicator { +public: + Communicator(CommunicatorAggregator *inCommAggregator, const LabelType &inLabel); + ~Communicator() override; + + DISABLE_COPY_ASSIGN_MOVE(Communicator); + + int RegOnMessageCallback(const OnMessageCallback &onMessage, const Finalizer &inOper) override; + int RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) override; + int RegOnSendableCallback(const std::function &onSendable, const Finalizer &inOper) override; + + void Activate() override; + + uint32_t GetCommunicatorMtuSize() const override; + uint32_t GetCommunicatorMtuSize(const std::string &target) const override; + + uint32_t GetTimeout() const override; + uint32_t GetTimeout(const std::string &target) const override; + bool IsDeviceOnline(const std::string &device) const override; + int GetLocalIdentity(std::string &outTarget) const override; + // Get the protocol version of remote target. Return -E_NOT_FOUND if no record. + int GetRemoteCommunicatorVersion(const std::string &target, uint16_t &outVersion) const override; + + int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config) override; + int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config, + const OnSendEnd &onEnd) override; + + // Call by CommunicatorAggregator directly + void OnBufferReceive(const std::string &srcTarget, const SerialBuffer *inBuf); + + // Call by CommunicatorAggregator directly + void OnConnectChange(const std::string &target, bool isConnect); + + // Call by CommunicatorAggregator directly + void OnSendAvailable(); + + // Call by CommunicatorAggregator directly + LabelType GetCommunicatorLabel() const; + +private: + void TriggerVersionNegotiation(const std::string &dstTarget); + void TriggerUnknownMessageFeedback(const std::string &dstTarget, Message* &oriMsg); + + DECLARE_OBJECT_TAG(Communicator); + + CommunicatorAggregator *commAggrHandle_ = nullptr; + LabelType commLabel_; + + std::set onlineTargets_; // Actually protected by connectHandleMutex_ + + OnMessageCallback onMessageHandle_; + OnConnectCallback onConnectHandle_; + std::function onSendableHandle_; + Finalizer onMessageFinalizer_; + Finalizer onConnectFinalizer_; + Finalizer onSendableFinalizer_; + std::mutex messageHandleMutex_; + std::mutex connectHandleMutex_; + std::mutex sendableHandleMutex_; +}; +} // namespace DistributedDB + +#endif // COMMUNICATOR_H diff --git a/mock/distributeddb/communicator/src/communicator_aggregator.cpp b/mock/distributeddb/communicator/src/communicator_aggregator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ec0fa24a763b3d3b85b9e9d64a4abfc120ac9b7 --- /dev/null +++ b/mock/distributeddb/communicator/src/communicator_aggregator.cpp @@ -0,0 +1,888 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "communicator_aggregator.h" +#include +#include +#include +#include +#include "hash.h" +#include "log_print.h" +#include "db_common.h" +#include "communicator.h" +#include "endian_convert.h" +#include "protocol_proto.h" +#include "communicator_linker.h" + +namespace DistributedDB { +namespace { +inline std::string GetThreadId() +{ + std::stringstream stream; + stream << std::this_thread::get_id(); + return stream.str(); +} +} + +std::atomic CommunicatorAggregator::isCommunicatorNotFoundFeedbackEnable_{true}; + +CommunicatorAggregator::CommunicatorAggregator() + : shutdown_(false), + incFrameId_(0), + localSourceId_(0) +{ +} + +CommunicatorAggregator::~CommunicatorAggregator() +{ + scheduler_.Finalize(); // Clear residual frame dumped by linker after CommunicatorAggregator finalize + adapterHandle_ = nullptr; + commLinker_ = nullptr; +} + +int CommunicatorAggregator::Initialize(IAdapter *inAdapter) +{ + if (inAdapter == nullptr) { + return -E_INVALID_ARGS; + } + adapterHandle_ = inAdapter; + + combiner_.Initialize(); + retainer_.Initialize(); + scheduler_.Initialize(); + + int errCode; + commLinker_ = new (std::nothrow) CommunicatorLinker(this); + if (commLinker_ == nullptr) { + errCode = -E_OUT_OF_MEMORY; + goto ROLL_BACK; + } + commLinker_->Initialize(); + + errCode = RegCallbackToAdapter(); + if (errCode != E_OK) { + goto ROLL_BACK; + } + + errCode = adapterHandle_->StartAdapter(); + if (errCode != E_OK) { + LOGE("[CommAggr][Init] Start Adapter Fail, errCode=%d.", errCode); + goto ROLL_BACK; + } + GenerateLocalSourceId(); + + shutdown_ = false; + exclusiveThread_ = std::thread(&CommunicatorAggregator::SendDataRoutine, this); + return E_OK; +ROLL_BACK: + UnRegCallbackFromAdapter(); + if (commLinker_ != nullptr) { + RefObject::DecObjRef(commLinker_); // Refcount of linker is 1 when created, here to unref linker + commLinker_ = nullptr; + } + // Scheduler do not need to do finalize in this roll_back + retainer_.Finalize(); + combiner_.Finalize(); + return errCode; +} + +void CommunicatorAggregator::Finalize() +{ + shutdown_ = true; + retryCv_.notify_all(); + { + std::lock_guard wakingLockGuard(wakingMutex_); + wakingSignal_ = true; + wakingCv_.notify_one(); + } + exclusiveThread_.join(); // Waiting thread to thoroughly quit + LOGI("[CommAggr][Final] Sub Thread Exit."); + scheduler_.Finalize(); // scheduler_ must finalize here to make space for linker to dump residual frame + + adapterHandle_->StopAdapter(); + UnRegCallbackFromAdapter(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait 100 ms to make sure all callback thread quit + + // No callback now and later, so combiner, retainer and linker can finalize or delete safely + RefObject::DecObjRef(commLinker_); // Refcount of linker is 1 when created, here to unref linker + commLinker_ = nullptr; + retainer_.Finalize(); + combiner_.Finalize(); +} + +ICommunicator *CommunicatorAggregator::AllocCommunicator(uint64_t commLabel, int &outErrorNo) +{ + uint64_t netOrderLabel = HostToNet(commLabel); + uint8_t *eachByte = reinterpret_cast(&netOrderLabel); + std::vector realLabel(COMM_LABEL_LENGTH, 0); + for (int i = 0; i < static_cast(sizeof(uint64_t)); i++) { + realLabel[i] = eachByte[i]; + } + return AllocCommunicator(realLabel, outErrorNo); +} + +ICommunicator *CommunicatorAggregator::AllocCommunicator(const std::vector &commLabel, int &outErrorNo) +{ + std::lock_guard commMapLockGuard(commMapMutex_); + LOGI("[CommAggr][Alloc] Label=%.6s.", VEC_TO_STR(commLabel)); + if (commLabel.size() != COMM_LABEL_LENGTH) { + outErrorNo = -E_INVALID_ARGS; + return nullptr; + } + + if (commMap_.count(commLabel) != 0) { + outErrorNo = -E_ALREADY_ALLOC; + return nullptr; + } + + Communicator *commPtr = new (std::nothrow) Communicator(this, commLabel); + if (commPtr == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + commMap_[commLabel] = {commPtr, false}; // Communicator is not activated when allocated + return commPtr; +} + +void CommunicatorAggregator::ReleaseCommunicator(ICommunicator *inCommunicator) +{ + if (inCommunicator == nullptr) { + return; + } + Communicator *commPtr = static_cast(inCommunicator); + LabelType commLabel = commPtr->GetCommunicatorLabel(); + LOGI("[CommAggr][Release] Label=%.6s.", VEC_TO_STR(commLabel)); + + std::lock_guard commMapLockGuard(commMapMutex_); + if (commMap_.count(commLabel) == 0) { + LOGE("[CommAggr][Release] Not Found."); + return; + } + commMap_.erase(commLabel); + RefObject::DecObjRef(commPtr); // Refcount of Communicator is 1 when created, here to unref Communicator + + int errCode = commLinker_->DecreaseLocalLabel(commLabel); + if (errCode != E_OK) { + LOGE("[CommAggr][Release] DecreaseLocalLabel Fail, Just Log, errCode=%d.", errCode); + } +} + +int CommunicatorAggregator::RegCommunicatorLackCallback(const CommunicatorLackCallback &onCommLack, + const Finalizer &inOper) +{ + std::lock_guard onCommLackLockGuard(onCommLackMutex_); + return RegCallBack(onCommLack, onCommLackHandle_, inOper, onCommLackFinalizer_); +} + +int CommunicatorAggregator::RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) +{ + std::lock_guard onConnectLockGuard(onConnectMutex_); + int errCode = RegCallBack(onConnect, onConnectHandle_, inOper, onConnectFinalizer_); + if (onConnect && errCode == E_OK) { + // Register action and success + std::set onlineTargets = commLinker_->GetOnlineRemoteTarget(); + for (auto &entry : onlineTargets) { + LOGI("[CommAggr][RegConnect] Online target=%s{private}.", entry.c_str()); + onConnectHandle_(entry, true); + } + } + return errCode; +} + +uint32_t CommunicatorAggregator::GetCommunicatorAggregatorMtuSize() const +{ + return adapterHandle_->GetMtuSize() - ProtocolProto::GetLengthBeforeSerializedData(); +} + +uint32_t CommunicatorAggregator::GetCommunicatorAggregatorMtuSize(const std::string &target) const +{ + return adapterHandle_->GetMtuSize(target) - ProtocolProto::GetLengthBeforeSerializedData(); +} + +uint32_t CommunicatorAggregator::GetCommunicatorAggregatorTimeout() const +{ + return adapterHandle_->GetTimeout(); +} + +uint32_t CommunicatorAggregator::GetCommunicatorAggregatorTimeout(const std::string &target) const +{ + return adapterHandle_->GetTimeout(target); +} + +bool CommunicatorAggregator::IsDeviceOnline(const std::string &device) const +{ + return adapterHandle_->IsDeviceOnline(device); +} + +int CommunicatorAggregator::GetLocalIdentity(std::string &outTarget) const +{ + return adapterHandle_->GetLocalIdentity(outTarget); +} + +void CommunicatorAggregator::ActivateCommunicator(const LabelType &commLabel) +{ + std::lock_guard commMapLockGuard(commMapMutex_); + LOGI("[CommAggr][Activate] Label=%.6s.", VEC_TO_STR(commLabel)); + if (commMap_.count(commLabel) == 0) { + LOGW("[CommAggr][Activate] Communicator of this label not allocated."); + return; + } + if (commMap_.at(commLabel).second) { + LOGW("[CommAggr][Activate] Communicator of this label had been activated."); + return; + } + commMap_.at(commLabel).second = true; // Mark this communicator as activated + + // IncreaseLocalLabel below and DecreaseLocalLabel in ReleaseCommunicator should all be protected by commMapMutex_ + // To avoid disordering probably caused by concurrent call to ActivateCommunicator and ReleaseCommunicator + std::set onlineTargets; + int errCode = commLinker_->IncreaseLocalLabel(commLabel, onlineTargets); + if (errCode != E_OK) { + LOGE("[CommAggr][Activate] IncreaseLocalLabel Fail, Just Log, errCode=%d.", errCode); + // Do not return here + } + for (auto &entry : onlineTargets) { + LOGI("[CommAggr][Activate] Already Online Target=%s{private}.", entry.c_str()); + commMap_.at(commLabel).first->OnConnectChange(entry, true); + } + // Do Redeliver, the communicator is responsible to deal with the frame + std::list framesToRedeliver = retainer_.FetchFramesForSpecificCommunicator(commLabel); + for (auto &entry : framesToRedeliver) { + commMap_.at(commLabel).first->OnBufferReceive(entry.srcTarget, entry.buffer); + } +} + +namespace { +void DoOnSendEndByTaskIfNeed(const OnSendEnd &onEnd, int result) +{ + if (onEnd) { + TaskAction onSendEndTask = [onEnd, result]() { + LOGD("[CommAggr][SendEndTask] Before On Send End."); + onEnd(result); + LOGD("[CommAggr][SendEndTask] After On Send End."); + }; + int errCode = RuntimeContext::GetInstance()->ScheduleTask(onSendEndTask); + if (errCode != E_OK) { + LOGE("[CommAggr][SendEndTask] ScheduleTask failed, errCode = %d.", errCode); + } + } +} +} + +int CommunicatorAggregator::CreateSendTask(const std::string &dstTarget, SerialBuffer *inBuff, + FrameType inType, const TaskConfig &inConfig, const OnSendEnd &onEnd) +{ + if (inBuff == nullptr) { + return -E_INVALID_ARGS; + } + LOGI("[CommAggr][Create] Enter, thread=%s, target=%s{private}, type=%d, nonBlock=%d, timeout=%u, prio=%d.", + GetThreadId().c_str(), dstTarget.c_str(), static_cast(inType), inConfig.nonBlock, inConfig.timeout, + static_cast(inConfig.prio)); + + if (!ReGenerateLocalSourceIdIfNeed()) { + delete inBuff; + inBuff = nullptr; + DoOnSendEndByTaskIfNeed(onEnd, -E_PERIPHERAL_INTERFACE_FAIL); + LOGE("[CommAggr][Create] Exit ok but discard since localSourceId zero, thread=%s.", GetThreadId().c_str()); + return E_OK; // Returns E_OK here to indicate this buffer was accepted though discard immediately + } + PhyHeaderInfo info{localSourceId_, incFrameId_.fetch_add(1, std::memory_order_seq_cst), inType}; + int errCode = ProtocolProto::SetPhyHeader(inBuff, info); + if (errCode != E_OK) { + LOGE("[CommAggr][Create] Set phyHeader fail, thread=%s, errCode=%d", GetThreadId().c_str(), errCode); + return errCode; + } + + SendTask task{inBuff, dstTarget, onEnd}; + if (inConfig.nonBlock) { + errCode = scheduler_.AddSendTaskIntoSchedule(task, inConfig.prio); + } else { + errCode = RetryUntilTimeout(task, inConfig.timeout, inConfig.prio); + } + if (errCode != E_OK) { + LOGW("[CommAggr][Create] Exit failed, thread=%s, errCode=%d", GetThreadId().c_str(), errCode); + return errCode; + } + + std::lock_guard wakingLockGuard(wakingMutex_); + wakingSignal_ = true; + wakingCv_.notify_one(); + LOGI("[CommAggr][Create] Exit ok, thread=%s, frameId=%u", GetThreadId().c_str(), info.frameId); // Delete In Future + return E_OK; +} + +void CommunicatorAggregator::EnableCommunicatorNotFoundFeedback(bool isEnable) +{ + isCommunicatorNotFoundFeedbackEnable_ = isEnable; +} + +int CommunicatorAggregator::GetRemoteCommunicatorVersion(const std::string &target, uint16_t &outVersion) const +{ + std::lock_guard versionMapLockGuard(versionMapMutex_); + auto pair = versionMap_.find(target); + if (pair == versionMap_.end()) { + return -E_NOT_FOUND; + } + outVersion = pair->second; + return E_OK; +} + +void CommunicatorAggregator::SendDataRoutine() +{ + while (!shutdown_) { + if (scheduler_.GetNoDelayTaskCount() == 0) { + std::unique_lock wakingUniqueLock(wakingMutex_); + LOGI("[CommAggr][Routine] Send done and sleep."); // Delete In Future + wakingCv_.wait(wakingUniqueLock, [this] { return this->wakingSignal_; }); + LOGI("[CommAggr][Routine] Send continue."); // Delete In Future + wakingSignal_ = false; + continue; + } + + SendTask taskToSend; + int errCode = scheduler_.ScheduleOutSendTask(taskToSend); + if (errCode != E_OK) { + continue; // Not possible to happen + } + // + std::vector, uint32_t>> piecePackets; + errCode = ProtocolProto::SplitFrameIntoPacketsIfNeed(taskToSend.buffer, + adapterHandle_->GetMtuSize(taskToSend.dstTarget), piecePackets); + if (errCode != E_OK) { + LOGE("[CommAggr][Routine] Split frame fail, errCode=%d.", errCode); + TaskFinalizer(taskToSend, errCode); + continue; + } + // > + std::vector>> eachPacket; + if (piecePackets.size() == 0) { + // Case that no need to split a frame, just use original buffer as a packet + std::pair tmpEntry = taskToSend.buffer->GetReadOnlyBytesForEntireBuffer(); + std::pair> entry; + entry.first = tmpEntry.first - taskToSend.buffer->GetExtendHeadLength(); + entry.second.first = taskToSend.buffer->GetExtendHeadLength(); + entry.second.second = tmpEntry.second + entry.second.first; + eachPacket.push_back(entry); + } else { + for (auto &entry : piecePackets) { + std::pair> tmpEntry = {&(entry.first[0]), + {entry.second, entry.first.size()}}; + eachPacket.push_back(tmpEntry); + } + } + + SendPacketsAndDisposeTask(taskToSend, eachPacket); + } +} + +void CommunicatorAggregator::SendPacketsAndDisposeTask(const SendTask &inTask, + const std::vector>> &eachPacket) +{ + bool taskNeedFinalize = true; + int errCode = E_OK; + for (auto &entry : eachPacket) { + LOGI("[CommAggr][SendPackets] DoSendBytes, dstTarget=%s{private}, extendHeadLength=%u, totalLength=%u.", + inTask.dstTarget.c_str(), entry.second.first, entry.second.second); + ProtocolProto::DisplayPacketInformation(entry.first + entry.second.first, entry.second.second); + errCode = adapterHandle_->SendBytes(inTask.dstTarget, entry.first, entry.second.second); + if (errCode == -E_WAIT_RETRY) { + LOGE("[CommAggr][SendPackets] SendBytes temporally fail."); + scheduler_.DelayTaskByTarget(inTask.dstTarget); + taskNeedFinalize = false; + break; + } else if (errCode != E_OK) { + LOGE("[CommAggr][SendPackets] SendBytes totally fail, errCode=%d.", errCode); + break; + } + } + if (taskNeedFinalize) { + TaskFinalizer(inTask, errCode); + } +} + +int CommunicatorAggregator::RetryUntilTimeout(SendTask &inTask, uint32_t timeout, Priority inPrio) +{ + int errCode = scheduler_.AddSendTaskIntoSchedule(inTask, inPrio); + if (errCode != E_OK) { + bool notTimeout = true; + auto retryFunc = [this, inPrio, &inTask]()->bool { + if (this->shutdown_) { + delete inTask.buffer; + inTask.buffer = nullptr; + return true; + } + int retCode = scheduler_.AddSendTaskIntoSchedule(inTask, inPrio); + if (retCode != E_OK) { + return false; + } + return true; + }; + + if (timeout == 0) { // Unlimited retry + std::unique_lock retryUniqueLock(retryMutex_); + retryCv_.wait(retryUniqueLock, retryFunc); + } else { + std::unique_lock retryUniqueLock(retryMutex_); + notTimeout = retryCv_.wait_for(retryUniqueLock, std::chrono::milliseconds(timeout), retryFunc); + } + + if (shutdown_) { + return E_OK; + } + if (!notTimeout) { + return -E_TIMEOUT; + } + } + return E_OK; +} + +void CommunicatorAggregator::TaskFinalizer(const SendTask &inTask, int result) +{ + // Call the OnSendEnd if need + if (inTask.onEnd) { + LOGD("[CommAggr][TaskFinal] On Send End."); + inTask.onEnd(result); + } + // Finalize the task that just scheduled + int errCode = scheduler_.FinalizeLastScheduleTask(); + // Notify Sendable To All Communicator If Need + if (errCode == -E_CONTAINER_FULL_TO_NOTFULL) { + retryCv_.notify_all(); + } + if (errCode == -E_CONTAINER_NOTEMPTY_TO_EMPTY) { + NotifySendableToAllCommunicator(); + } +} + +void CommunicatorAggregator::NotifySendableToAllCommunicator() +{ + std::lock_guard commMapLockGuard(commMapMutex_); + for (auto &entry : commMap_) { + // Ignore nonactivated communicator + if (entry.second.second) { + entry.second.first->OnSendAvailable(); + } + } +} + +void CommunicatorAggregator::OnBytesReceive(const std::string &srcTarget, const uint8_t *bytes, uint32_t length, + const std::string &userId) +{ + ProtocolProto::DisplayPacketInformation(bytes, length); // For debug, delete in the future + ParseResult packetResult; + int errCode = ProtocolProto::CheckAndParsePacket(srcTarget, bytes, length, packetResult); + if (errCode != E_OK) { + LOGE("[CommAggr][Receive] Parse packet fail, errCode=%d.", errCode); + if (errCode == -E_VERSION_NOT_SUPPORT) { + TriggerVersionNegotiation(srcTarget); + } + return; + } + + // Update version of remote target + SetRemoteCommunicatorVersion(srcTarget, packetResult.GetDbVersion()); + if (packetResult.GetFrameTypeInfo() == FrameType::EMPTY) { // Empty frame will never be fragmented + LOGI("[CommAggr][Receive] Empty frame, just ignore in this version of distributeddb."); + return; + } + + if (packetResult.IsFragment()) { + OnFragmentReceive(srcTarget, bytes, length, packetResult, userId); + } else if (packetResult.GetFrameTypeInfo() != FrameType::APPLICATION_MESSAGE) { + errCode = OnCommLayerFrameReceive(srcTarget, packetResult); + if (errCode != E_OK) { + LOGE("[CommAggr][Receive] CommLayer receive fail, errCode=%d.", errCode); + } + } else { + errCode = OnAppLayerFrameReceive(srcTarget, bytes, length, packetResult, userId); + if (errCode != E_OK) { + LOGE("[CommAggr][Receive] AppLayer receive fail, errCode=%d.", errCode); + } + } +} + +void CommunicatorAggregator::OnTargetChange(const std::string &target, bool isConnect) +{ + if (target.empty()) { + LOGE("[CommAggr][OnTarget] Target empty string."); + return; + } + // For process level target change + { + std::lock_guard onConnectLockGuard(onConnectMutex_); + if (onConnectHandle_) { + onConnectHandle_(target, isConnect); + LOGI("[CommAggr][OnTarget] On Connect End."); // Log in case callback block this thread + } else { + LOGI("[CommAggr][OnTarget] ConnectHandle invalid currently."); + } + } + std::set relatedLabels; + // For communicator level target change + if (isConnect) { + int errCode = commLinker_->TargetOnline(target, relatedLabels); + if (errCode != E_OK) { + LOGE("[CommAggr][OnTarget] TargetOnline fail, target=%s{private}, errCode=%d.", target.c_str(), errCode); + } + } else { + int errCode = commLinker_->TargetOffline(target, relatedLabels); + if (errCode != E_OK) { + LOGE("[CommAggr][OnTarget] TargetOffline fail, target=%s{private}, errCode=%d.", target.c_str(), errCode); + } + } + // All related communicator online or offline this target, no matter TargetOnline or TargetOffline fail or not + std::lock_guard commMapLockGuard(commMapMutex_); + for (auto &entry : commMap_) { + // Ignore nonactivated communicator + if (entry.second.second && (!isConnect || (relatedLabels.count(entry.first) != 0))) { + entry.second.first->OnConnectChange(target, isConnect); + } + } +} + +void CommunicatorAggregator::OnSendable(const std::string &target) +{ + int errCode = scheduler_.NoDelayTaskByTarget(target); + if (errCode != E_OK) { + LOGE("[CommAggr][Sendable] NoDelay target=%s{private} fail, errCode=%d.", target.c_str(), errCode); + return; + } + std::lock_guard wakingLockGuard(wakingMutex_); + wakingSignal_ = true; + wakingCv_.notify_one(); +} + +void CommunicatorAggregator::OnFragmentReceive(const std::string &srcTarget, const uint8_t *bytes, uint32_t length, + const ParseResult &inResult, const std::string &userId) +{ + int errorNo = E_OK; + ParseResult frameResult; + SerialBuffer *frameBuffer = combiner_.AssembleFrameFragment(bytes, length, inResult, frameResult, errorNo); + if (errorNo != E_OK) { + LOGE("[CommAggr][Receive] Combine fail, errCode=%d.", errorNo); + return; + } + if (frameBuffer == nullptr) { + LOGW("[CommAggr][Receive] Combine undone."); + return; + } + + int errCode = ProtocolProto::CheckAndParseFrame(frameBuffer, frameResult); + if (errCode != E_OK) { + LOGE("[CommAggr][Receive] Parse frame fail, errCode=%d.", errCode); + delete frameBuffer; + frameBuffer = nullptr; + if (errCode == -E_VERSION_NOT_SUPPORT) { + TriggerVersionNegotiation(srcTarget); + } + return; + } + + if (frameResult.GetFrameTypeInfo() != FrameType::APPLICATION_MESSAGE) { + errCode = OnCommLayerFrameReceive(srcTarget, frameResult); + if (errCode != E_OK) { + LOGE("[CommAggr][Receive] CommLayer receive fail after combination, errCode=%d.", errCode); + } + delete frameBuffer; + frameBuffer = nullptr; + } else { + errCode = OnAppLayerFrameReceive(srcTarget, frameBuffer, frameResult, userId); + if (errCode != E_OK) { + LOGE("[CommAggr][Receive] AppLayer receive fail after combination, errCode=%d.", errCode); + } + } +} + +int CommunicatorAggregator::OnCommLayerFrameReceive(const std::string &srcTarget, const ParseResult &inResult) +{ + if (inResult.GetFrameTypeInfo() == FrameType::COMMUNICATION_LABEL_EXCHANGE_ACK) { + int errCode = commLinker_->ReceiveLabelExchangeAck(srcTarget, inResult.GetLabelExchangeDistinctValue(), + inResult.GetLabelExchangeSequenceId()); + if (errCode != E_OK) { + LOGE("[CommAggr][CommReceive] Receive LabelExchangeAck Fail."); + return errCode; + } + } else { + std::map changedLabels; + int errCode = commLinker_->ReceiveLabelExchange(srcTarget, inResult.GetLatestCommLabels(), + inResult.GetLabelExchangeDistinctValue(), inResult.GetLabelExchangeSequenceId(), changedLabels); + if (errCode != E_OK) { + LOGE("[CommAggr][CommReceive] Receive LabelExchange Fail."); + return errCode; + } + if (!commLinker_->IsRemoteTargetOnline(srcTarget)) { + LOGW("[CommAggr][CommReceive] Receive LabelExchange from offline target=%s{private}.", srcTarget.c_str()); + for (const auto &entry : changedLabels) { + LOGW("[CommAggr][CommReceive] REMEMBER: label=%s, inOnline=%d.", VEC_TO_STR(entry.first), entry.second); + } + return E_OK; + } + // Do target change notify + std::lock_guard commMapLockGuard(commMapMutex_); + for (auto &entry : changedLabels) { + // Ignore nonactivated communicator + if (commMap_.count(entry.first) != 0 && commMap_.at(entry.first).second) { + LOGI("[CommAggr][CommReceive] label=%s, srcTarget=%s{private}, isOnline=%d.", + VEC_TO_STR(entry.first), srcTarget.c_str(), entry.second); + commMap_.at(entry.first).first->OnConnectChange(srcTarget, entry.second); + } + } + } + return E_OK; +} + +int CommunicatorAggregator::OnAppLayerFrameReceive(const std::string &srcTarget, const uint8_t *bytes, + uint32_t length, const ParseResult &inResult, const std::string &userId) +{ + SerialBuffer *buffer = new (std::nothrow) SerialBuffer(); + if (buffer == nullptr) { + LOGE("[CommAggr][AppReceive] New SerialBuffer fail."); + return -E_OUT_OF_MEMORY; + } + int errCode = buffer->SetExternalBuff(bytes, length - inResult.GetPaddingLen(), + ProtocolProto::GetAppLayerFrameHeaderLength()); + if (errCode != E_OK) { + LOGE("[CommAggr][AppReceive] SetExternalBuff fail, errCode=%d.", errCode); + delete buffer; + buffer = nullptr; + return -E_INTERNAL_ERROR; + } + return OnAppLayerFrameReceive(srcTarget, buffer, inResult, userId); +} + +// In early time, we cover "OnAppLayerFrameReceive" totally by commMapMutex_, then search communicator, if not found, +// we call onCommLackHandle_ if exist to ask whether to retain this frame or not, if the answer is yes we retain this +// frame, otherwise we discard this frame and send out CommunicatorNotFound feedback. +// We design so(especially cover this function totally by commMapMutex_) to avoid current situation described below +// 1:This func find that target communicator not allocated or activated, so decide to retain this frame. +// 2:Thread switch out, the target communicator is allocated and activated, previous retained frame is fetched out. +// 3:Thread switch back, this frame is then retained into the retainer, no chance to be fetched out. +// In conclusion: the decision to retain a frame and the action to retain a frame should not be separated. +// Otherwise, at the action time, the retain decision may be obsolete and wrong. +// #### BUT #### since onCommLackHandle_ callback is go beyond DistributedDB and there is the risk that the final upper +// user may do something such as GetKvStore(we can prevent them to so) which could result in calling AllocCommunicator +// in the same callback thread finally causing DeadLock on commMapMutex_. +// #### SO #### we have to make a change described below +// 1:Search communicator under commMapMutex_, if found then deliver frame to that communicator and end. +// 2:Call onCommLackHandle_ if exist to ask whether to retain this frame or not, without commMapMutex_. +// Note: during this period, commMap_ maybe changed, and communicator not found before may exist now. +// 3:Search communicator under commMapMutex_ again, if found then deliver frame to that communicator and end. +// 4:If still not found, retain this frame if need or otherwise send CommunicatorNotFound feedback. +int CommunicatorAggregator::OnAppLayerFrameReceive(const std::string &srcTarget, SerialBuffer *&inFrameBuffer, + const ParseResult &inResult, const std::string &userId) +{ + LabelType toLabel = inResult.GetCommLabel(); + { + std::lock_guard commMapLockGuard(commMapMutex_); + int errCode = TryDeliverAppLayerFrameToCommunicatorNoMutex(srcTarget, inFrameBuffer, toLabel); + if (errCode == E_OK) { // Attention: Here is equal to E_OK + return E_OK; + } + } + LOGI("[CommAggr][AppReceive] Communicator of %s not found or nonactivated.", VEC_TO_STR(toLabel)); + int errCode = -E_NOT_FOUND; + { + std::lock_guard onCommLackLockGuard(onCommLackMutex_); + if (onCommLackHandle_) { + errCode = onCommLackHandle_(toLabel, userId); + LOGI("[CommAggr][AppReceive] On CommLack End."); // Log in case callback block this thread + } else { + LOGI("[CommAggr][AppReceive] CommLackHandle invalid currently."); + } + } + // Here we have to lock commMapMutex_ and search communicator again. + std::lock_guard commMapLockGuard(commMapMutex_); + int errCodeAgain = TryDeliverAppLayerFrameToCommunicatorNoMutex(srcTarget, inFrameBuffer, toLabel); + if (errCodeAgain == E_OK) { // Attention: Here is equal to E_OK. + LOGI("[CommAggr][AppReceive] Communicator of %s found after try again(rare case).", VEC_TO_STR(toLabel)); + return E_OK; + } + // Here, communicator is still not found, retain or discard according to the result of onCommLackHandle_ + if (errCode != E_OK) { + TryToFeedbackWhenCommunicatorNotFound(srcTarget, toLabel, inFrameBuffer); + delete inFrameBuffer; + inFrameBuffer = nullptr; + return errCode; // The caller will display errCode in log + } + // Do Retention, the retainer is responsible to deal with the frame + retainer_.RetainFrame(FrameInfo{inFrameBuffer, srcTarget, toLabel, inResult.GetFrameId()}); + inFrameBuffer = nullptr; + return E_OK; +} + +int CommunicatorAggregator::TryDeliverAppLayerFrameToCommunicatorNoMutex(const std::string &srcTarget, + SerialBuffer *&inFrameBuffer, const LabelType &toLabel) +{ + // Ignore nonactivated communicator, which is regarded as inexistent + if (commMap_.count(toLabel) != 0 && commMap_.at(toLabel).second) { + commMap_.at(toLabel).first->OnBufferReceive(srcTarget, inFrameBuffer); + // Frame handed over to communicator who is responsible to delete it. The frame is deleted here after return. + inFrameBuffer = nullptr; + return E_OK; + } + return -E_NOT_FOUND; +} + +int CommunicatorAggregator::RegCallbackToAdapter() +{ + RefObject::IncObjRef(this); // Reference to be hold by adapter + int errCode = adapterHandle_->RegBytesReceiveCallback( + std::bind(&CommunicatorAggregator::OnBytesReceive, this, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4), + [this]() { RefObject::DecObjRef(this); }); + if (errCode != E_OK) { + RefObject::DecObjRef(this); // Rollback in case reg failed + return errCode; + } + + RefObject::IncObjRef(this); // Reference to be hold by adapter + errCode = adapterHandle_->RegTargetChangeCallback( + std::bind(&CommunicatorAggregator::OnTargetChange, this, std::placeholders::_1, std::placeholders::_2), + [this]() { RefObject::DecObjRef(this); }); + if (errCode != E_OK) { + RefObject::DecObjRef(this); // Rollback in case reg failed + return errCode; + } + + RefObject::IncObjRef(this); // Reference to be hold by adapter + errCode = adapterHandle_->RegSendableCallback( + std::bind(&CommunicatorAggregator::OnSendable, this, std::placeholders::_1), + [this]() { RefObject::DecObjRef(this); }); + if (errCode != E_OK) { + RefObject::DecObjRef(this); // Rollback in case reg failed + return errCode; + } + + return E_OK; +} + +void CommunicatorAggregator::UnRegCallbackFromAdapter() +{ + adapterHandle_->RegBytesReceiveCallback(nullptr, nullptr); + adapterHandle_->RegTargetChangeCallback(nullptr, nullptr); + adapterHandle_->RegSendableCallback(nullptr, nullptr); +} + +void CommunicatorAggregator::GenerateLocalSourceId() +{ + std::string identity; + adapterHandle_->GetLocalIdentity(identity); + // When GetLocalIdentity fail, the identity be an empty string, the localSourceId be zero, need regenerate + // The localSourceId is std::atomic, so there is no concurrency risk + uint64_t identityHash = Hash::HashFunc(identity); + if (identityHash != localSourceId_) { + LOGI("[CommAggr][GenSrcId] identity=%s{private}, localSourceId=%llu.", identity.c_str(), ULL(identityHash)); + } + localSourceId_ = identityHash; +} + +bool CommunicatorAggregator::ReGenerateLocalSourceIdIfNeed() +{ + // The deviceId will change when switch user from A to B + // We can't listen to the user change, because it's hard to ensure the timing is correct. + // So we regenerate to make sure the deviceId and localSourceId is correct when we create send task. + // The localSourceId is std::atomic, so there is no concurrency risk, no need lockguard here. + GenerateLocalSourceId(); + return (localSourceId_ != 0); +} + +void CommunicatorAggregator::TriggerVersionNegotiation(const std::string &dstTarget) +{ + LOGI("[CommAggr][TrigVer] Do version negotiate with target=%s{private}.", dstTarget.c_str()); + int errCode = E_OK; + SerialBuffer *buffer = ProtocolProto::BuildEmptyFrameForVersionNegotiate(errCode); + if (errCode != E_OK) { + LOGE("[CommAggr][TrigVer] Build empty frame fail, errCode=%d", errCode); + return; + } + + TaskConfig config{true, 0, Priority::HIGH}; + errCode = CreateSendTask(dstTarget, buffer, FrameType::EMPTY, config); + if (errCode != E_OK) { + LOGE("[CommAggr][TrigVer] Send empty frame fail, errCode=%d", errCode); + // if send fails, free buffer, otherwise buffer will be taked over by SendTaskScheduler + delete buffer; + buffer = nullptr; + } +} + +void CommunicatorAggregator::TryToFeedbackWhenCommunicatorNotFound(const std::string &dstTarget, + const LabelType &dstLabel, const SerialBuffer *inOriFrame) +{ + if (!isCommunicatorNotFoundFeedbackEnable_ || dstTarget.empty() || inOriFrame == nullptr) { + return; + } + int errCode = E_OK; + Message *message = ProtocolProto::ToMessage(inOriFrame, errCode, true); + if (message == nullptr) { + if (errCode == -E_VERSION_NOT_SUPPORT) { + TriggerVersionNegotiation(dstTarget); + } + return; + } + // Message is release in TriggerCommunicatorNotFoundFeedback + TriggerCommunicatorNotFoundFeedback(dstTarget, dstLabel, message); +} + +void CommunicatorAggregator::TriggerCommunicatorNotFoundFeedback(const std::string &dstTarget, + const LabelType &dstLabel, Message* &oriMsg) +{ + if (oriMsg == nullptr || oriMsg->GetMessageType() != TYPE_REQUEST) { + LOGI("[CommAggr][TrigNotFound] Do nothing for message with type not request."); + // Do not have to do feedback if the message is not a request type message + delete oriMsg; + oriMsg = nullptr; + return; + } + + LOGI("[CommAggr][TrigNotFound] Do communicator not found feedback with target=%s{private}.", dstTarget.c_str()); + oriMsg->SetMessageType(TYPE_RESPONSE); + oriMsg->SetErrorNo(E_FEEDBACK_COMMUNICATOR_NOT_FOUND); + + int errCode = E_OK; + SerialBuffer *buffer = ProtocolProto::BuildFeedbackMessageFrame(oriMsg, dstLabel, errCode); + delete oriMsg; + oriMsg = nullptr; + if (errCode != E_OK) { + LOGE("[CommAggr][TrigNotFound] Build communicator not found feedback frame fail, errCode=%d", errCode); + return; + } + + TaskConfig config{true, 0, Priority::HIGH}; + errCode = CreateSendTask(dstTarget, buffer, FrameType::APPLICATION_MESSAGE, config); + if (errCode != E_OK) { + LOGE("[CommAggr][TrigNotFound] Send communicator not found feedback frame fail, errCode=%d", errCode); + // if send fails, free buffer, otherwise buffer will be taked over by CreateSendTask + delete buffer; + buffer = nullptr; + } +} + +void CommunicatorAggregator::SetRemoteCommunicatorVersion(const std::string &target, uint16_t version) +{ + std::lock_guard versionMapLockGuard(versionMapMutex_); + versionMap_[target] = version; +} + +std::shared_ptr CommunicatorAggregator::GetExtendHeaderHandle(const ExtendInfo ¶mInfo) +{ + if (adapterHandle_ == nullptr) { + return nullptr; + } + return adapterHandle_->GetExtendHeaderHandle(paramInfo); +} + +DEFINE_OBJECT_TAG_FACILITIES(CommunicatorAggregator) +} // namespace DistributedDB diff --git a/mock/distributeddb/communicator/src/communicator_linker.cpp b/mock/distributeddb/communicator/src/communicator_linker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bb9fde6f0d08035080291232f82464b341c19bd5 --- /dev/null +++ b/mock/distributeddb/communicator/src/communicator_linker.cpp @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "communicator_linker.h" +#include "hash.h" +#include "db_errno.h" +#include "log_print.h" +#include "protocol_proto.h" +#include "platform_specific.h" +#include "communicator_aggregator.h" + +namespace DistributedDB { +namespace { +constexpr uint32_t TIME_LAPSE_FOR_WAITING_ACK = 5000; // 5s +constexpr uint32_t TIME_LAPSE_FOR_RETRY_SEND = 1000; // 1s +constexpr uint32_t RETRANSMIT_LIMIT = 20; // Currently we do at most 20 retransmission if no ack received +constexpr uint32_t RETRANSMIT_LIMIT_EQUAL_INTERVAL = 5; // First 5 retransmission will be equal interval +} + +CommunicatorLinker::CommunicatorLinker(CommunicatorAggregator *inAggregator) + : incSequenceId_(0), incAckTriggerId_(0) +{ + aggregator_ = inAggregator; + RefObject::IncObjRef(aggregator_); // The linker rely on CommunicatorAggregator +} + +CommunicatorLinker::~CommunicatorLinker() +{ + RefObject::DecObjRef(aggregator_); // The linker no longer rely on CommunicatorAggregator + aggregator_ = nullptr; +} + +void CommunicatorLinker::Initialize() +{ + uint64_t curTime = 0; + int errCode = OS::GetCurrentSysTimeInMicrosecond(curTime); + if (errCode != E_OK) { + LOGW("[Linker][Init] Get systime fail, use default, errCode=%d.", errCode); + } + std::string curTimeStr = std::to_string(curTime); + localDistinctValue_ = Hash::HashFunc(curTimeStr); + LOGI("[Linker][Init] curTime=%llu, distinct=%llu.", ULL(curTime), ULL(localDistinctValue_)); +} + +// Create async task to send out label_exchange and waiting for label_exchange_ack. +// If waiting timeout, pass the send&wait task to overrall timing retry task. +int CommunicatorLinker::TargetOnline(const std::string &inTarget, std::set &outRelatedLabels) +{ + { + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + // if inTarget is offline before, use the remembered previous online labels to decide which communicator to be + // notified online. Such handling is in case for abnormal unilateral offline, which A and B is notified online + // mutually, then B is notified A offline and for a while B is notified A online again, but A feels no notify. + if (remoteOnlineTarget_.count(inTarget) == 0) { + outRelatedLabels = targetMapOnlineLabels_[inTarget]; + remoteOnlineTarget_.insert(inTarget); + } + } + return TriggerLabelExchangeEvent(inTarget); +} + +// Clear all labels related to this target. Let no longer waiting for ack of this target. +// The caller should notify all related communicator about this target offline. +int CommunicatorLinker::TargetOffline(const std::string &inTarget, std::set &outRelatedLabels) +{ + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + outRelatedLabels = targetMapOnlineLabels_[inTarget]; + // Do not erase the Labels of inTarget from targetMapOnlineLabels_, remember it for using when TargetOnline + remoteOnlineTarget_.erase(inTarget); + // Note: The process of remote target may quit, when remote target restart, + // the distinctValue of this remote target may be changed, and the sequenceId may start from zero + targetDistinctValue_.erase(inTarget); + topRecvLabelSeq_.erase(inTarget); + return E_OK; +} + +// Add local label. Create async task to send out label_exchange and waiting for label_exchange_ack. +// If waiting timeout, pass the send&wait task to overrall timing retry task. +// Find out targets for this label that is already online. +// The caller should notify communicator of this label about already online target. +int CommunicatorLinker::IncreaseLocalLabel(const LabelType &inLabel, std::set &outOnlineTarget) +{ + std::set totalOnlineTargets; + { + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + localOnlineLabels_.insert(inLabel); + totalOnlineTargets = remoteOnlineTarget_; + for (auto &entry : targetMapOnlineLabels_) { + if (remoteOnlineTarget_.count(entry.first) == 0) { // Ignore offline target + continue; + } + if (entry.second.count(inLabel) != 0) { // This online target had opened then same Label + outOnlineTarget.insert(entry.first); + } + } + } + bool everFail = false; + for (auto &entry : totalOnlineTargets) { + int errCode = TriggerLabelExchangeEvent(entry); + if (errCode != E_OK) { + everFail = true; + } + } + return everFail ? -E_INTERNAL_ERROR : E_OK; +} + +// Del local label. Create async task to send out label_exchange and waiting for label_exchange_ack. +// If waiting timeout, pass the send&wait task to overrall timing retry task. +int CommunicatorLinker::DecreaseLocalLabel(const LabelType &inLabel) +{ + std::set totalOnlineTargets; + { + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + localOnlineLabels_.erase(inLabel); + totalOnlineTargets = remoteOnlineTarget_; + } + bool everFail = false; + for (auto &entry : totalOnlineTargets) { + int errCode = TriggerLabelExchangeEvent(entry); + if (errCode != E_OK) { + everFail = true; + } + } + return everFail ? -E_INTERNAL_ERROR : E_OK; +} + +// Compare the latest labels with previous Label, find out label changes. +// The caller should notify the target changes according to label changes. +// Update the online labels of this target. Send out label_exchange_ack. +int CommunicatorLinker::ReceiveLabelExchange(const std::string &inTarget, const std::set &inLatestLabels, + uint64_t inDistinctValue, uint64_t inSequenceId, std::map &outChangeLabels) +{ + { + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + DetectDistinctValueChange(inTarget, inDistinctValue); + if (topRecvLabelSeq_.count(inTarget) == 0) { + // Firstly receive LabelExchange from this target + topRecvLabelSeq_[inTarget] = inSequenceId; + } else if (inSequenceId < topRecvLabelSeq_[inTarget]) { + // inSequenceId can be equal to topRecvLabelSeq, in this case, the ack of this sequence send to this target + // may be lost, this target resend LabelExchange, and we should resend ack to this target + LOGW("[Linker][RecvLabel] inSequenceId=%llu smaller than topRecvLabelSeq=%llu. Frame Ignored.", + ULL(inSequenceId), ULL(topRecvLabelSeq_[inTarget])); + return -E_OUT_OF_DATE; + } else { + // Update top sequenceId of received LabelExchange + topRecvLabelSeq_[inTarget] = inSequenceId; + } + // Find out online labels by check difference + for (auto &entry : inLatestLabels) { + if (targetMapOnlineLabels_[inTarget].count(entry) == 0) { + outChangeLabels[entry] = true; + } + } + // Find out offline labels by check difference + for (auto &entry : targetMapOnlineLabels_[inTarget]) { + if (inLatestLabels.count(entry) == 0) { + outChangeLabels[entry] = false; + } + } + // Update target online labels + targetMapOnlineLabels_[inTarget] = inLatestLabels; + } + // Trigger sending ack + int errCode = TriggerLabelExchangeAckEvent(inTarget, inSequenceId); + if (errCode != E_OK) { + LOGE("[Linker][RecvLabel] TriggerAckEvent Fail, Just Log, errCode=%d.", errCode); + // Do not return error here + } + return E_OK; +} + +// Waiting finish if the ack is what linker wait by check inSequenceId +// Similarly, stop the retry task of this Target. +int CommunicatorLinker::ReceiveLabelExchangeAck(const std::string &inTarget, uint64_t inDistinctValue, + uint64_t inSequenceId) +{ + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + DetectDistinctValueChange(inTarget, inDistinctValue); + // This two judge is for detecting case that local device process restart so incSequenceId_ restart from 0 + // The remote device may send an ack cause by previous process, which may destroy the functionality of this process + if (waitAckSeq_.count(inTarget) == 0) { + LOGW("[Linker][RecvAck] Not waiting any ack now, inSequenceId=%llu", ULL(inSequenceId)); + return -E_NOT_FOUND; + } + if (waitAckSeq_[inTarget] < inSequenceId) { + LOGW("[Linker][RecvAck] Not waiting this ack now, inSequenceId=%llu, waitAckSeq_=%llu", + ULL(inSequenceId), ULL(waitAckSeq_[inTarget])); + return -E_NOT_FOUND; + } + // An valid ack received + if (recvAckSeq_.count(inTarget) == 0) { + // Firstly receive LabelExchangeAck from this target + recvAckSeq_[inTarget] = inSequenceId; + } else if (inSequenceId <= recvAckSeq_[inTarget]) { + LOGW("[Linker][RecvAck] inSequenceId=%llu not greater than recvAckSeq_=%llu. Frame Ignored.", + ULL(inSequenceId), ULL(recvAckSeq_[inTarget])); + return -E_OUT_OF_DATE; + } else { + // Update top sequenceId of received LabelExchangeAck + recvAckSeq_[inTarget] = inSequenceId; + } + return E_OK; +} + +std::set CommunicatorLinker::GetOnlineRemoteTarget() const +{ + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + return remoteOnlineTarget_; +} + +bool CommunicatorLinker::IsRemoteTargetOnline(const std::string &inTarget) const +{ + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + if (remoteOnlineTarget_.count(inTarget) != 0) { + return true; + } + return false; +} + +// inCountDown is in millisecond +void CommunicatorLinker::SuspendByOnceTimer(const std::function &inAction, uint32_t inCountDown) +{ + TimerId thisTimerId = 0; + RuntimeContext *context = RuntimeContext::GetInstance(); + int errCode = context->SetTimer(static_cast(inCountDown), [inAction](TimerId inTimerId)->int{ + // Note: inAction should be captured by value (must not by reference) + LOGI("[Linker][Suspend] Timer Due : inTimerId=%llu.", ULL(inTimerId)); + inAction(); + return -E_END_TIMER; + }, nullptr, thisTimerId); + if (errCode == E_OK) { + LOGI("[Linker][Suspend] SetTimer Success : thisTimerId=%llu, wait=%u(ms).", ULL(thisTimerId), inCountDown); + } else { + LOGI("[Linker][Suspend] SetTimer Fail Raise Thread Instead : errCode=%d, wait=%u(ms).", errCode, inCountDown); + std::thread timerThread([inAction, inCountDown]() { + // Note: inAction and inCountDown should be captured by value (must not by reference) + std::this_thread::sleep_for(std::chrono::milliseconds(inCountDown)); + inAction(); + }); + timerThread.detach(); + } +} + +// This function should be called under protection of entireInfoMutex_ +void CommunicatorLinker::DetectDistinctValueChange(const std::string &inTarget, uint64_t inDistinctValue) +{ + // Firstly received distinctValue from this target ever or after offline + if (targetDistinctValue_.count(inTarget) == 0) { + targetDistinctValue_[inTarget] = inDistinctValue; + return; + } + + // DistinctValue is the same as before + if (targetDistinctValue_[inTarget] == inDistinctValue) { + return; + } + + // DistinctValue change detected !!! This must be caused by malfunctioning of underlayer communication component. + LOGE("[Linker][Detect] ######## DISTINCT VALUE CHANGE DETECTED : %llu VS %llu ########", + ULL(inDistinctValue), ULL(targetDistinctValue_[inTarget])); + targetDistinctValue_[inTarget] = inDistinctValue; + // The process of remote target must have undergone a quit and restart, the remote sequenceId will start from zero. + topRecvLabelSeq_.erase(inTarget); +} + +int CommunicatorLinker::TriggerLabelExchangeEvent(const std::string &toTarget) +{ + // Apply for a latest sequenceId + uint64_t sequenceId = incSequenceId_.fetch_add(1, std::memory_order_seq_cst); + // Get a snapshot of current online labels + std::set onlineLabels; + { + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + onlineLabels = localOnlineLabels_; + } + // Build LabelExchange Frame + int error = E_OK; + SerialBuffer *buffer = ProtocolProto::BuildLabelExchange(localDistinctValue_, sequenceId, onlineLabels, error); + if (error != E_OK) { + LOGE("[Linker][TriggerLabel] BuildLabel fail, error=%d", error); + return error; + } + // Update waitAckSeq, Check whether new event be triggered in other thread + { + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + if (waitAckSeq_.count(toTarget) == 0) { + // Firstly send LabelExchange to this target + waitAckSeq_[toTarget] = sequenceId; + } else if (waitAckSeq_[toTarget] > sequenceId) { + // New LabelExchangeEvent had been trigger for this target, so this event can be abort + LOGI("[Linker][TriggerLabel] Detect newSeqId=%llu than thisSeqId=%llu be triggered for target=%s{private}", + ULL(waitAckSeq_[toTarget]), ULL(sequenceId), toTarget.c_str()); + delete buffer; + buffer = nullptr; + return E_OK; + } else { + waitAckSeq_[toTarget] = sequenceId; + } + } + // Synchronously call SendLabelExchange and hand over buffer to it + RefObject::IncObjRef(this); // SendLabelExchange will only DecRef when total done if no need to send + SendLabelExchange(toTarget, buffer, sequenceId, 0); // Initially retransmitCount is 0 + return E_OK; +} + +int CommunicatorLinker::TriggerLabelExchangeAckEvent(const std::string &toTarget, uint64_t inSequenceId) +{ + // Build LabelExchangeAck Frame + int errCode = E_OK; + SerialBuffer *buffer = ProtocolProto::BuildLabelExchangeAck(localDistinctValue_, inSequenceId, errCode); + if (errCode != E_OK) { + LOGE("[Linker][TriggerAck] BuildAck fail, error=%d", errCode); + return errCode; + } + // Apply for a latest ackId and update ackTriggerId_ + uint64_t ackId; + { + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + ackId = incAckTriggerId_.fetch_add(1, std::memory_order_seq_cst); + ackTriggerId_[toTarget] = ackId; + } + // Synchronously call SendLabelExchangeAck and hand over buffer to it + RefObject::IncObjRef(this); // SendLabelExchangeAck will only DecRef when total done if no need to send + SendLabelExchangeAck(toTarget, buffer, inSequenceId, ackId); + return E_OK; +} + +namespace { +inline uint32_t GetDynamicTimeLapseForWaitingAck(uint32_t inRetransmitCount) +{ + if (inRetransmitCount <= RETRANSMIT_LIMIT_EQUAL_INTERVAL) { + return TIME_LAPSE_FOR_WAITING_ACK; + } + uint32_t subsequentRetransmit = inRetransmitCount - RETRANSMIT_LIMIT_EQUAL_INTERVAL; + return subsequentRetransmit * subsequentRetransmit * TIME_LAPSE_FOR_WAITING_ACK; +} +} + +void CommunicatorLinker::SendLabelExchange(const std::string &toTarget, SerialBuffer *inBuff, uint64_t inSequenceId, + uint32_t inRetransmitCount) +{ + // Check whether have the need to send + bool noNeedToSend = ((inRetransmitCount <= RETRANSMIT_LIMIT) ? false : true); + { + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + if (remoteOnlineTarget_.count(toTarget) == 0) { + // Target offline + noNeedToSend = true; + } + if (waitAckSeq_[toTarget] > inSequenceId) { + // New LabelExchangeEvent had been trigger for this target, so this event can be abort + noNeedToSend = true; + } + if (recvAckSeq_.count(toTarget) != 0 && recvAckSeq_[toTarget] >= inSequenceId) { + // Ack of this sequenceId had been received or even later ack had been received + noNeedToSend = true; + } + if (noNeedToSend) { // ATTENTION: This Log should be inside the protection of entireInfoLockGuard!!! + LOGI("[Linker][SendLabel] NoNeedSend:target=%s{private}, thisSeqId=%llu, waitAckSeq=%llu, recvAckSeq=%llu," + "retrans=%u.", toTarget.c_str(), ULL(inSequenceId), ULL(waitAckSeq_[toTarget]), + ULL((recvAckSeq_.count(toTarget) != 0) ? recvAckSeq_[toTarget] : ~ULL(0)), inRetransmitCount); + } // ~0 indicate no ack ever recv + } + if (noNeedToSend) { + delete inBuff; + inBuff = nullptr; + RefObject::DecObjRef(this); // ATTENTION: The DecObjRef should be outside entireInfoLockGuard!!! + return; + } + + int error = E_OK; + SerialBuffer *cloneBuffer = inBuff->Clone(error); + TaskConfig config{true, 0, Priority::HIGH}; + int errCode = aggregator_->CreateSendTask(toTarget, inBuff, FrameType::COMMUNICATION_LABEL_EXCHANGE, config); + if (errCode == E_OK) { + // Send ok, go on to wait ack, and maybe resend + if (error == E_OK) { + SuspendByOnceTimer([this, toTarget, cloneBuffer, inSequenceId, inRetransmitCount]() { + // Note: toTarget and cloneBuffer and inSequenceId should be captured by value (must not by reference) + SendLabelExchange(toTarget, cloneBuffer, inSequenceId, inRetransmitCount + 1); // Do retransmission + }, GetDynamicTimeLapseForWaitingAck(inRetransmitCount)); + } else { + LOGE("[Linker][SendLabel] CloneFail: target=%s{private}, SeqId=%llu.", toTarget.c_str(), ULL(inSequenceId)); + } + } else { + // Send fail, go on to retry send + SuspendByOnceTimer([this, toTarget, inBuff, inSequenceId, inRetransmitCount]() { + // Note: toTarget and inBuff and inSequenceId should be captured by value (must not by reference) + SendLabelExchange(toTarget, inBuff, inSequenceId, inRetransmitCount); // Just do retry send + }, TIME_LAPSE_FOR_RETRY_SEND); + if (error == E_OK) { + delete cloneBuffer; + cloneBuffer = nullptr; + } + } +} + +void CommunicatorLinker::SendLabelExchangeAck(const std::string &toTarget, SerialBuffer *inBuff, + uint64_t inSequenceId, uint64_t inAckTriggerId) +{ + // Check whether have the need to send + bool noNeedToSend = false; + { + std::lock_guard entireInfoLockGuard(entireInfoMutex_); + // Now that LabelExchange is received, LabelExchangeAck should be send no matter target online or not + if (topRecvLabelSeq_.count(toTarget) != 0 && topRecvLabelSeq_[toTarget] > inSequenceId) { + // topRecvLabelSeq for this target may have been erased, detect it for avoid creating an entry + // New LabelExchange had been received for this target, so this event can be abort + noNeedToSend = true; + } + if (ackTriggerId_[toTarget] > inAckTriggerId) { + // New LabelExchangeAck had been trigger for this target, so this event can be abort + noNeedToSend = true; + } + if (noNeedToSend) { // ATTENTION: This Log should be inside the protection of entireInfoLockGuard!!! + LOGI("[Linker][SendAck] NoNeedSend:target=%s{private}, thisSeqId=%llu, topRecLabelSeq=%llu, thisAckId=%llu," + "ackTriggerId=%llu.", toTarget.c_str(), ULL(inSequenceId), // ~0 indacate no label ever recv + ULL((topRecvLabelSeq_.count(toTarget) != 0) ? topRecvLabelSeq_[toTarget] : ~ULL(0)), + ULL(inAckTriggerId), ULL(ackTriggerId_[toTarget])); + } + } + if (noNeedToSend) { + delete inBuff; + inBuff = nullptr; + RefObject::DecObjRef(this); // ATTENTION: The DecObjRef should be outside entireInfoLockGuard!!! + return; + } + + TaskConfig config{true, 0, Priority::HIGH}; + int errCode = aggregator_->CreateSendTask(toTarget, inBuff, FrameType::COMMUNICATION_LABEL_EXCHANGE_ACK, config); + if (errCode == E_OK) { + // Send ok, finish event + RefObject::DecObjRef(this); // ATTENTION: The DecObjRef should be outside entireInfoLockGuard!!! + } else { + // Send fail, go on to retry send + SuspendByOnceTimer([this, toTarget, inBuff, inSequenceId, inAckTriggerId]() { + // Note: toTarget, inBuff, inSequenceId, inAckTriggerId should be captured by value (must not by reference) + SendLabelExchangeAck(toTarget, inBuff, inSequenceId, inAckTriggerId); + }, TIME_LAPSE_FOR_RETRY_SEND); + } +} + +DEFINE_OBJECT_TAG_FACILITIES(CommunicatorLinker) +} // namespace DistributedDB diff --git a/mock/distributeddb/communicator/src/communicator_linker.h b/mock/distributeddb/communicator/src/communicator_linker.h new file mode 100644 index 0000000000000000000000000000000000000000..d8d0740f5b14b2f63b0192b7cf587a2cb092d6a2 --- /dev/null +++ b/mock/distributeddb/communicator/src/communicator_linker.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATOR_LINKER_H +#define COMMUNICATOR_LINKER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ref_object.h" +#include "serial_buffer.h" +#include "communicator_type_define.h" + +namespace DistributedDB { +class CommunicatorAggregator; // Forward Declaration + +class CommunicatorLinker : public virtual RefObject { +public: + explicit CommunicatorLinker(CommunicatorAggregator *inAggregator); + ~CommunicatorLinker(); + + DISABLE_COPY_ASSIGN_MOVE(CommunicatorLinker); + + void Initialize(); + + // Create async task to send out label_exchange and waiting for label_exchange_ack. + // If waiting timeout, pass the send&wait task to overrall timing retry task. + int TargetOnline(const std::string &inTarget, std::set &outRelatedLabels); + + // Clear all labels related to this target. Let no longer waiting for ack of this target. + // The caller should notify all related communicator about this target offline. + int TargetOffline(const std::string &inTarget, std::set &outRelatedLabels); + + // Add local label. Create async task to send out label_exchange and waiting for label_exchange_ack. + // If waiting timeout, pass the send&wait task to overrall timing retry task. + // Find out targets for this label that is already online. + // The caller should notify communicator of this label about already online target. + int IncreaseLocalLabel(const LabelType &inLabel, std::set &outOnlineTarget); + + // Del local label. Create async task to send out label_exchange and waiting for label_exchange_ack. + // If waiting timeout, pass the send&wait task to overrall timing retry task. + int DecreaseLocalLabel(const LabelType &inLabel); + + // Compare the latest labels with previous Label, find out label changes. + // The caller should notify the target changes according to label changes. + // Update the online labels of this target. Send out label_exchange_ack. + int ReceiveLabelExchange(const std::string &inTarget, const std::set &inLatestLabels, + uint64_t inDistinctValue, uint64_t inSequenceId, std::map &outChangeLabels); + + // Waiting finish if the ack is what linker wait by check inSequenceId + // Similarly, stop the retry task of this Target. + int ReceiveLabelExchangeAck(const std::string &inTarget, uint64_t inDistinctValue, uint64_t inSequenceId); + + std::set GetOnlineRemoteTarget() const; + + bool IsRemoteTargetOnline(const std::string &inTarget) const; +private: + DECLARE_OBJECT_TAG(CommunicatorLinker); + + // inCountDown is in millisecond + void SuspendByOnceTimer(const std::function &inAction, uint32_t inCountDown); + + // This function should be called under protection of entireInfoMutex_ + void DetectDistinctValueChange(const std::string &inTarget, uint64_t inDistinctValue); + + int TriggerLabelExchangeEvent(const std::string &toTarget); + int TriggerLabelExchangeAckEvent(const std::string &toTarget, uint64_t inSequenceId); + + void SendLabelExchange(const std::string &toTarget, SerialBuffer *inBuff, uint64_t inSequenceId, + uint32_t inRetransmitCount); + void SendLabelExchangeAck(const std::string &toTarget, SerialBuffer *inBuff, uint64_t inSequenceId, + uint64_t inAckTriggerId); + + uint64_t localDistinctValue_ = 0; + std::atomic incSequenceId_; + std::atomic incAckTriggerId_; + CommunicatorAggregator *aggregator_ = nullptr; + + mutable std::mutex entireInfoMutex_; + + // Point out the distinctValue for each target in order to detect malfunctioning "target offline" + std::map targetDistinctValue_; + + // Point out the largest sequenceId of LabelExchange that ever received for each target + std::map topRecvLabelSeq_; + + // Point out currently which sequenceId of ack is being waited for each target + std::map waitAckSeq_; + + // Point out the largest sequenceId of LabelExchangeAck that ever received for each target + std::map recvAckSeq_; + + // Point out the latest ackTriggerId for each target in order to abort outdated triggered event + std::map ackTriggerId_; + + // Core Info : Online Labels + std::set localOnlineLabels_; + std::set remoteOnlineTarget_; + + // remember the opened labels no matter target now online or offline + std::map> targetMapOnlineLabels_; +}; +} + +#endif \ No newline at end of file diff --git a/mock/distributeddb/communicator/src/frame_combiner.cpp b/mock/distributeddb/communicator/src/frame_combiner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bbca2164878b6ae8c06f9edef4b61cfce7d02bda --- /dev/null +++ b/mock/distributeddb/communicator/src/frame_combiner.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "frame_combiner.h" +#include +#include "log_print.h" +#include "protocol_proto.h" + +namespace DistributedDB { +static const uint32_t MAX_WORK_PER_SRC_TARGET = 1; // Only allow 1 CombineWork for each target +static const int SURVAIL_PERIOD_IN_MILLISECOND = 10000; // Period is 10 s + +void FrameCombiner::Initialize() +{ + RuntimeContext *context = RuntimeContext::GetInstance(); + TimerAction action = [this](TimerId inTimerId)->int{ + PeriodicalSurveillance(); + return E_OK; + }; + TimerFinalizer finalizer = [this]() { + timerRemovedIndicator_.SendSemaphore(); + }; + int errCode = context->SetTimer(SURVAIL_PERIOD_IN_MILLISECOND, action, finalizer, timerId_); + if (errCode != E_OK) { + LOGE("[Combiner][Init] Set timer fail, errCode=%d.", errCode); + return; + } + isTimerWork_ = true; +} + +void FrameCombiner::Finalize() +{ + // First: Stop the timer + if (isTimerWork_) { + RuntimeContext *context = RuntimeContext::GetInstance(); + context->RemoveTimer(timerId_); + timerRemovedIndicator_.WaitSemaphore(); + } + + // Second: Clear the combineWorkPool_ + for (auto &eachSource : combineWorkPool_) { + for (auto &eachFrame : eachSource.second) { + delete eachFrame.second.buffer; + eachFrame.second.buffer = nullptr; + } + } +} + +SerialBuffer *FrameCombiner::AssembleFrameFragment(const uint8_t *bytes, uint32_t length, + const ParseResult &inPacketInfo, ParseResult &outFrameInfo, int &outErrorNo) +{ + uint64_t sourceId = inPacketInfo.GetSourceId(); + uint32_t frameId = inPacketInfo.GetFrameId(); + std::lock_guard overallLockGuard(overallMutex_); + if (combineWorkPool_[sourceId].count(frameId) != 0) { + // CombineWork already exist + int errCode = ContinueExistCombineWork(bytes, length, inPacketInfo); + if (errCode != E_OK) { + LOGE("[Combiner][Assemble] Continue work fail, errCode=%d.", errCode); + outErrorNo = errCode; + return nullptr; + } + + if (combineWorkPool_[sourceId][frameId].status.IsCombineDone()) { + // We can parse the combined frame here, or outside this class. + LOGI("[Combiner][Assemble] Combine done, sourceId=%llu, frameId=%u.", ULL(sourceId), frameId); + SerialBuffer *outFrame = combineWorkPool_[sourceId][frameId].buffer; + outFrameInfo = combineWorkPool_[sourceId][frameId].frameInfo; + outErrorNo = E_OK; + combineWorkPool_[sourceId].erase(frameId); + return outFrame; // The caller is responsible for release the outFrame + } + } else { + // CombineWork not exist and even existing work number reaches the limitation. Try create work first. + int errCode = CreateNewCombineWork(bytes, length, inPacketInfo); + if (errCode != E_OK) { + LOGE("[Combiner][Assemble] Create work fail, errCode=%d.", errCode); + outErrorNo = errCode; + return nullptr; + } + // After successfully create work, the existing work number may exceed the limitation + // If so, choose one from works of this target with lowest progressId and abort it + if (combineWorkPool_[sourceId].size() > MAX_WORK_PER_SRC_TARGET) { + AbortCombineWorkBySource(sourceId); + } + } + outErrorNo = E_OK; + return nullptr; +} + +void FrameCombiner::PeriodicalSurveillance() +{ + std::lock_guard overallLockGuard(overallMutex_); + for (auto &eachSource : combineWorkPool_) { + std::set frameToAbort; + for (auto &eachFrame : eachSource.second) { + if (!eachFrame.second.status.CheckProgress()) { + LOGW("[Combiner][Surveil] Source=%llu, frame=%u has no progress, this combine work will be aborted.", + ULL(eachSource.first), eachFrame.first); + // Free this combine work first + delete eachFrame.second.buffer; + eachFrame.second.buffer = nullptr; + // Record this frame in abort list + frameToAbort.insert(eachFrame.first); + } + } + // Remove the combine work from map + for (auto &entry : frameToAbort) { + eachSource.second.erase(entry); + } + } +} + +int FrameCombiner::ContinueExistCombineWork(const uint8_t *bytes, uint32_t length, const ParseResult &inPacketInfo) +{ + uint64_t sourceId = inPacketInfo.GetSourceId(); + uint32_t frameId = inPacketInfo.GetFrameId(); + CombineWork &oriWork = combineWorkPool_[sourceId][frameId]; // Be care here must be reference + if (!CheckPacketWithOriWork(inPacketInfo, oriWork)) { + LOGE("[Combiner][ContinueWork] Check packet fail, sourceId=%" PRIu64 ", frameId=%" PRIu32, sourceId, frameId); + return -E_COMBINE_FAIL; + } + + uint32_t fragOffset = oriWork.status.GetThisFragmentOffset(inPacketInfo.GetFragNo()); + uint32_t fragLength = oriWork.status.GetThisFragmentLength(inPacketInfo.GetFragNo()); + int errCode = ProtocolProto::CombinePacketIntoFrame(oriWork.buffer, bytes, length, fragOffset, fragLength); + if (errCode != E_OK) { + // We can consider abort this work, but here we choose not to affect it + LOGE("[Combiner][ContinueWork] Combine packet fail, sourceId=%" PRIu64 ", frameId=%" PRIu32, sourceId, frameId); + return -E_COMBINE_FAIL; + } + + oriWork.status.UpdateProgressId(incProgressId_++); + oriWork.status.CheckInFragmentNo(inPacketInfo.GetFragNo()); + return E_OK; +} + +int FrameCombiner::CreateNewCombineWork(const uint8_t *bytes, uint32_t length, const ParseResult &inPacketInfo) +{ + uint32_t fragLen = 0; + uint32_t lastFragLen = 0; + int errCode = ProtocolProto::AnalyzeSplitStructure(inPacketInfo, fragLen, lastFragLen); + if (errCode != E_OK) { + LOGE("[Combiner][CreateWork] Analyze fail, errCode=%d.", errCode); + return errCode; + } + + CombineWork work; + + work.frameInfo.SetPacketLen(inPacketInfo.GetFrameLen()); + work.frameInfo.SetSourceId(inPacketInfo.GetSourceId()); + work.frameInfo.SetFrameId(inPacketInfo.GetFrameId()); + work.frameInfo.SetFrameTypeInfo(inPacketInfo.GetFrameTypeInfo()); + work.frameInfo.SetFrameLen(inPacketInfo.GetFrameLen()); + work.frameInfo.SetFragCount(inPacketInfo.GetFragCount()); + + work.status.SetFragmentLen(fragLen); + work.status.SetLastFragmentLen(lastFragLen); + work.status.SetFragmentCount(inPacketInfo.GetFragCount()); + + work.buffer = CreateNewFrameBuffer(inPacketInfo); + if (work.buffer == nullptr) { + return -E_OUT_OF_MEMORY; + } + + uint32_t fragOffset = work.status.GetThisFragmentOffset(inPacketInfo.GetFragNo()); + uint32_t fragLength = work.status.GetThisFragmentLength(inPacketInfo.GetFragNo()); + errCode = ProtocolProto::CombinePacketIntoFrame(work.buffer, bytes, length, fragOffset, fragLength); + if (errCode != E_OK) { + delete work.buffer; + work.buffer = nullptr; + return errCode; + } + + totalSizeByByte_ += work.buffer->GetSize(); + work.status.UpdateProgressId(incProgressId_++); + work.status.CheckInFragmentNo(inPacketInfo.GetFragNo()); + combineWorkPool_[inPacketInfo.GetSourceId()][inPacketInfo.GetFrameId()] = work; + return E_OK; +} + +void FrameCombiner::AbortCombineWorkBySource(uint64_t inSourceId) +{ + if (combineWorkPool_[inSourceId].size() == 0) { + return; + } + uint32_t toBeAbortFrameId = 0; + uint64_t toBeAbortProgressId = UINT64_MAX; + for (auto &entry : combineWorkPool_[inSourceId]) { + if (entry.second.status.GetProgressId() < toBeAbortProgressId) { + toBeAbortProgressId = entry.second.status.GetProgressId(); + toBeAbortFrameId = entry.first; + } + } + // Do Abort! + LOGW("[Combiner][AbortWork] Abort Incomplete CombineWork, sourceId=%llu, frameId=%u.", + ULL(inSourceId), toBeAbortFrameId); + delete combineWorkPool_[inSourceId][toBeAbortFrameId].buffer; + combineWorkPool_[inSourceId][toBeAbortFrameId].buffer = nullptr; + combineWorkPool_[inSourceId].erase(toBeAbortFrameId); +} + +bool FrameCombiner::CheckPacketWithOriWork(const ParseResult &inPacketInfo, const CombineWork &inWork) +{ + if (inPacketInfo.GetFrameLen() != inWork.frameInfo.GetFrameLen()) { + LOGE("[Combiner][CheckPacket] FrameLen mismatch %u vs %u.", inPacketInfo.GetFrameLen(), + inWork.frameInfo.GetFrameLen()); + return false; + } + if (inPacketInfo.GetFragCount() != inWork.frameInfo.GetFragCount()) { + LOGE("[Combiner][CheckPacket] FragCount mismatch %u vs %u.", inPacketInfo.GetFragCount(), + inWork.frameInfo.GetFragCount()); + return false; + } + if (inPacketInfo.GetFragNo() >= inPacketInfo.GetFragCount()) { + LOGE("[Combiner][CheckPacket] FragNo=%u illegal vs FragCount=%u.", inPacketInfo.GetFragNo(), + inPacketInfo.GetFragCount()); + return false; + } + if (inWork.status.IsFragNoAlreadyExist(inPacketInfo.GetFragNo())) { + LOGE("[Combiner][CheckPacket] FragNo=%u already exist.", inPacketInfo.GetFragNo()); + return false; + } + return true; +} + +SerialBuffer *FrameCombiner::CreateNewFrameBuffer(const ParseResult &inInfo) +{ + SerialBuffer *buffer = new (std::nothrow) SerialBuffer(); + if (buffer == nullptr) { + return nullptr; + } + uint32_t frameHeaderLength = (inInfo.GetFrameTypeInfo() != FrameType::APPLICATION_MESSAGE) ? + ProtocolProto::GetCommLayerFrameHeaderLength() : ProtocolProto::GetAppLayerFrameHeaderLength(); + int errCode = buffer->AllocBufferByTotalLength(inInfo.GetFrameLen(), frameHeaderLength); + if (errCode != E_OK) { + LOGE("[Combiner][CreateBuffer] Alloc Buffer Fail."); + delete buffer; + buffer = nullptr; + return nullptr; + } + return buffer; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/communicator/src/frame_header.h b/mock/distributeddb/communicator/src/frame_header.h new file mode 100644 index 0000000000000000000000000000000000000000..b861a3e14b5f1f45486ff93a311e85f6ead616e3 --- /dev/null +++ b/mock/distributeddb/communicator/src/frame_header.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FRAMEHEADER_H +#define FRAMEHEADER_H + +#include +#include "communicator_type_define.h" + +namespace DistributedDB { +/* + * packetType: Bit0: FragmentFlag: 1: Fragmented 0: Not Fragmented + * Bit1~3: Reserved + * Bit4~7: FrameType + */ +struct CommPhyHeader { + uint16_t magic = 0; // Magic code to discern byte stream + uint16_t version = 0; // Version to differentiate fields layout + uint32_t packetLen = 0; // Length of total packet, include CommHeader and Padding + uint64_t checkSum = 0; // Check sum of data that follows CommPhyHeader + uint64_t sourceId = 0; // Indicate where this packet from + uint32_t frameId = 0; // FrameId to identify frame + uint8_t packetType = 0; // Some bits works individually, the high four bits indicates frameType + uint8_t paddingLen = 0; // Unit byte, range from 0 to 7. + uint16_t dbIntVer = 0; // Auxiliary info to help recognize db version in the future +}; + +/* + * Whether a physical packet contains CommPhyOptHeader depend on FragmentFlag of packetType in CommPhyHeader + */ +struct CommPhyOptHeader { + uint32_t frameLen = 0; // Indicate length of frame before fragmentation. Frame include CommHeader no padding + uint16_t fragCount = 0; // Indicate how many fragments this frame is divided into + uint16_t fragNo = 0; // Indicate which fragment this packet is. start from 0. +}; + +/* + * Whether a physical packet contains CommDivergeHeader depend on FrameType of packetType in CommPhyHeader + */ +struct CommDivergeHeader { + uint16_t version = 0; // Version to differentiate fields layout + uint16_t reserved = 0; // Reserved for future usage + uint32_t payLoadLen = 0; // Indicate length of data that follows CommDivergeHeader + uint8_t commLabel[COMM_LABEL_LENGTH] = {0}; // Indicate which communicator to hand out this frame +}; + +/* + * MessageHeader used to describe a message + */ +struct MessageHeader { + uint16_t version = 0; // Version to differentiate fields layout + uint16_t messageType = 0; // Distinguish request/response/notify + uint32_t messageId = 0; // Indicate message command + uint32_t sessionId = 0; // For matching request and response + uint32_t sequenceId = 0; // Sequence of message + uint32_t errorNo = 0; // Indicate no error when zero + uint32_t dataLen = 0; // Indicate length of data that follows MessageHeader +}; +} // namespace DistributedDB + +#endif // FRAMEHEADER_H diff --git a/mock/distributeddb/communicator/src/frame_retainer.cpp b/mock/distributeddb/communicator/src/frame_retainer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8d6db64e777b6ca350026d25513b5562afe6702 --- /dev/null +++ b/mock/distributeddb/communicator/src/frame_retainer.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "frame_retainer.h" +#include "db_common.h" +#include "log_print.h" +#include "serial_buffer.h" + +namespace DistributedDB { +namespace { +const uint32_t MAX_CAPACITY = 67108864; // 64 M bytes +const uint32_t MAX_RETAIN_TIME = 10; // 10 s +const uint32_t MAX_RETAIN_FRAME_SIZE = 33554432; // 32 M bytes +const uint32_t MAX_RETAIN_FRAME_PER_LABEL_PER_TARGET = 5; // Allow 5 frame per communicator per source target +const int SURVAIL_PERIOD_IN_MILLISECOND = 1000; // Period is 1 s +inline void LogRetainInfo(const std::string &logPrefix, const LabelType &label, const std::string &target, + uint64_t order, const RetainWork &work) +{ + LOGI("%s : Label=%s, target=%s{private}, retainOrder=%llu, frameId=%u, remainTime=%u, frameSize=%u.", + logPrefix.c_str(), VEC_TO_STR(label), target.c_str(), ULL(order), + work.frameId, work.remainTime, work.buffer->GetSize()); +} +} + +void FrameRetainer::Initialize() +{ + RuntimeContext *context = RuntimeContext::GetInstance(); + if (context == nullptr) { + return; // Never gonna happen, context always be valid. + } + TimerAction action = [this](TimerId inTimerId)->int { + PeriodicalSurveillance(); + return E_OK; + }; + int errCode = context->SetTimer(SURVAIL_PERIOD_IN_MILLISECOND, action, nullptr, timerId_); + if (errCode != E_OK) { + LOGE("[Retainer][Init] Set timer fail, errCode=%d.", errCode); + return; + } + isTimerWork_ = true; +} + +void FrameRetainer::Finalize() +{ + RuntimeContext *context = RuntimeContext::GetInstance(); + if (context == nullptr) { + return; // Never gonna happen, context always be valid. + } + // First: Stop the timer + if (isTimerWork_) { + // After return, the timer rely no more on retainer. + context->RemoveTimer(timerId_, true); + isTimerWork_ = false; + } + // Second: Clear the retainWorkPool_ + for (auto &eachLabel : retainWorkPool_) { + for (auto &eachTarget : eachLabel.second) { + for (auto &eachFrame : eachTarget.second) { + LogRetainInfo("[Retainer][Final] DISCARD", eachLabel.first, eachTarget.first, eachFrame.first, + eachFrame.second); + delete eachFrame.second.buffer; + eachFrame.second.buffer = nullptr; + } + } + } + retainWorkPool_.clear(); + totalSizeByByte_ = 0; + totalRetainFrames_ = 0; +} + +void FrameRetainer::RetainFrame(const FrameInfo &inFrame) +{ + if (inFrame.buffer == nullptr) { + return; // Never gonna happen + } + RetainWork work{inFrame.buffer, inFrame.frameId, MAX_RETAIN_TIME}; + if (work.buffer->GetSize() > MAX_RETAIN_FRAME_SIZE) { + LOGE("[Retainer][Retain] Frame size=%u over limit=%u.", work.buffer->GetSize(), MAX_RETAIN_FRAME_SIZE); + delete work.buffer; + work.buffer = nullptr; + return; + } + int errCode = work.buffer->ConvertForCrossThread(); + if (errCode != E_OK) { + LOGE("[Retainer][Retain] ConvertForCrossThread fail, errCode=%d.", errCode); + delete work.buffer; + work.buffer = nullptr; + return; + } + + std::lock_guard overallLockGuard(overallMutex_); + std::map &perLabelPerTarget = retainWorkPool_[inFrame.commLabel][inFrame.srcTarget]; + if (perLabelPerTarget.size() >= MAX_RETAIN_FRAME_PER_LABEL_PER_TARGET) { + // Discard the oldest and obsolete one, update the statistics, free the buffer and remove from the map + auto iter = perLabelPerTarget.begin(); + LogRetainInfo("[Retainer][Retain] DISCARD", inFrame.commLabel, inFrame.srcTarget, iter->first, iter->second); + totalSizeByByte_ -= iter->second.buffer->GetSize(); + totalRetainFrames_--; + delete iter->second.buffer; + iter->second.buffer = nullptr; + perLabelPerTarget.erase(iter); + } + // Retain the new frame, update the statistics + perLabelPerTarget[incRetainOrder_++] = work; + totalSizeByByte_ += inFrame.buffer->GetSize(); + totalRetainFrames_++; + // Discard obsolete frames until totalSize under capacity. + DiscardObsoleteFramesIfNeed(); + // Display the final statistics + LOGI("[Retainer][Retain] Order=%llu. Statistics: TOTAL_BYTE=%u, TOTAL_FRAME=%u.", ULL(incRetainOrder_ - 1), + totalSizeByByte_, totalRetainFrames_); +} + +std::list FrameRetainer::FetchFramesForSpecificCommunicator(const LabelType &inCommLabel) +{ + std::lock_guard overallLockGuard(overallMutex_); + std::list outFrameList; + if (retainWorkPool_.count(inCommLabel) == 0) { + return outFrameList; + } + auto &perLabel = retainWorkPool_[inCommLabel]; + std::map fetchOrder; + for (auto &eachTarget : perLabel) { + for (auto &eachFrame : eachTarget.second) { + fetchOrder[eachFrame.first] = eachTarget.first; + } + } + for (auto &entry : fetchOrder) { + RetainWork &work = perLabel[entry.second][entry.first]; + LogRetainInfo("[Retainer][Fetch] FETCH-OUT", inCommLabel, entry.second, entry.first, work); + outFrameList.emplace_back(FrameInfo{work.buffer, entry.second, inCommLabel, work.frameId}); + // Update statistics + totalSizeByByte_ -= work.buffer->GetSize(); + totalRetainFrames_--; + } + retainWorkPool_.erase(inCommLabel); + return outFrameList; +} + +void FrameRetainer::PeriodicalSurveillance() +{ + std::lock_guard overallLockGuard(overallMutex_); + // First: Discard overtime frames. + for (auto &eachLabel : retainWorkPool_) { + for (auto &eachTarget : eachLabel.second) { + std::set frameToDiscard; + for (auto &eachFrame : eachTarget.second) { + // Decrease remainTime and discard if need. The remainTime will not be zero before decrease. + eachFrame.second.remainTime--; + if (eachFrame.second.remainTime == 0) { + LogRetainInfo("[Retainer][Surveil] DISCARD", eachLabel.first, eachTarget.first, eachFrame.first, + eachFrame.second); + totalSizeByByte_ -= eachFrame.second.buffer->GetSize(); + totalRetainFrames_--; + // Free this retain work first + delete eachFrame.second.buffer; + eachFrame.second.buffer = nullptr; + // Record this frame in discard list + frameToDiscard.insert(eachFrame.first); + } + } + // Remove the retain work from frameMap. + for (auto &entry : frameToDiscard) { + eachTarget.second.erase(entry); + } + } + } + // Second: Shrink the retainWorkPool_ + ShrinkRetainWorkPool(); +} + +void FrameRetainer::DiscardObsoleteFramesIfNeed() +{ + if (totalSizeByByte_ <= MAX_CAPACITY) { + return; + } + std::map> discardOrder; + // Sort all the frames by their retain order ascendingly + for (auto &eachLabel : retainWorkPool_) { + for (auto &eachTarget : eachLabel.second) { + for (auto &eachFrame : eachTarget.second) { + discardOrder[eachFrame.first] = {eachLabel.first, eachTarget.first}; + } + } + } + // Discard obsolete frames until totalSize under capacity. + while (totalSizeByByte_ > MAX_CAPACITY) { + if (discardOrder.empty()) { // Unlikely to happen + LOGE("[Retainer][Discard] Internal Error: Byte=%u, Frames=%u.", totalSizeByByte_, totalRetainFrames_); + return; + } + auto iter = discardOrder.begin(); + RetainWork &workRef = retainWorkPool_[iter->second.first][iter->second.second][iter->first]; + LogRetainInfo("[Retainer][Discard] DISCARD", iter->second.first, iter->second.second, iter->first, workRef); + // Discard the oldest and obsolete one, update the statistics, free the buffer and remove from the map + totalSizeByByte_ -= workRef.buffer->GetSize(); + totalRetainFrames_--; + delete workRef.buffer; + workRef.buffer = nullptr; + retainWorkPool_[iter->second.first][iter->second.second].erase(iter->first); + // Remove from the discardOrder + discardOrder.erase(iter); + } + // Shrink the retainWorkPool_ to remove out empty node on the map + ShrinkRetainWorkPool(); +} + +void FrameRetainer::ShrinkRetainWorkPool() +{ + std::set emptyLabel; + for (auto &eachLabel : retainWorkPool_) { + std::set emptyTarget; + for (auto &eachTarget : eachLabel.second) { + // Record corresponding target if its frameMap empty. + if (eachTarget.second.empty()) { + emptyTarget.insert(eachTarget.first); + } + } + // Remove the empty frameMap from the targetMap. Record corresponding label if its targetMap empty. + for (auto &entry : emptyTarget) { + eachLabel.second.erase(entry); + } + if (eachLabel.second.empty()) { + emptyLabel.insert(eachLabel.first); + } + } + // Remove the empty targetMap from retainWorkPool_ + for (auto &entry : emptyLabel) { + retainWorkPool_.erase(entry); + } +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/communicator/src/header_converter.cpp b/mock/distributeddb/communicator/src/header_converter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b8fe88eb35e8ef511689fabc7d611eb54121109 --- /dev/null +++ b/mock/distributeddb/communicator/src/header_converter.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "header_converter.h" +#include "endian_convert.h" +#include "communicator_type_define.h" + +namespace DistributedDB { +void HeaderConverter::ConvertHostToNet(const CommPhyHeader &headerOriginal, CommPhyHeader &headerConverted) +{ + headerConverted.magic = HostToNet(headerOriginal.magic); + headerConverted.version = HostToNet(headerOriginal.version); + headerConverted.packetLen = HostToNet(headerOriginal.packetLen); + headerConverted.checkSum = HostToNet(headerOriginal.checkSum); + headerConverted.sourceId = HostToNet(headerOriginal.sourceId); + headerConverted.frameId = HostToNet(headerOriginal.frameId); + headerConverted.packetType = HostToNet(headerOriginal.packetType); + headerConverted.paddingLen = HostToNet(headerOriginal.paddingLen); + headerConverted.dbIntVer = HostToNet(headerOriginal.dbIntVer); +} + +void HeaderConverter::ConvertHostToNet(const CommPhyOptHeader &headerOriginal, CommPhyOptHeader &headerConverted) +{ + headerConverted.frameLen = HostToNet(headerOriginal.frameLen); + headerConverted.fragCount = HostToNet(headerOriginal.fragCount); + headerConverted.fragNo = HostToNet(headerOriginal.fragNo); +} + +void HeaderConverter::ConvertHostToNet(const CommDivergeHeader &headerOriginal, CommDivergeHeader &headerConverted) +{ + ConvertNetToHost(headerOriginal, headerConverted); +} + +void HeaderConverter::ConvertHostToNet(const MessageHeader &headerOriginal, MessageHeader &headerConverted) +{ + ConvertNetToHost(headerOriginal, headerConverted); +} + +void HeaderConverter::ConvertNetToHost(const CommPhyHeader &headerOriginal, CommPhyHeader &headerConverted) +{ + ConvertHostToNet(headerOriginal, headerConverted); +} + +void HeaderConverter::ConvertNetToHost(const CommPhyOptHeader &headerOriginal, CommPhyOptHeader &headerConverted) +{ + ConvertHostToNet(headerOriginal, headerConverted); +} + +void HeaderConverter::ConvertNetToHost(const CommDivergeHeader &headerOriginal, CommDivergeHeader &headerConverted) +{ + headerConverted.version = NetToHost(headerOriginal.version); + headerConverted.reserved = NetToHost(headerOriginal.reserved); + headerConverted.payLoadLen = NetToHost(headerOriginal.payLoadLen); + // commLabel now is array of uint8_t, so no need to do endian convert, but we need to copy it here + for (unsigned int i = 0; i < COMM_LABEL_LENGTH; i++) { + headerConverted.commLabel[i] = headerOriginal.commLabel[i]; + } +} + +void HeaderConverter::ConvertNetToHost(const MessageHeader &headerOriginal, MessageHeader &headerConverted) +{ + headerConverted.version = NetToHost(headerOriginal.version); + headerConverted.messageType = NetToHost(headerOriginal.messageType); + headerConverted.messageId = NetToHost(headerOriginal.messageId); + headerConverted.sessionId = NetToHost(headerOriginal.sessionId); + headerConverted.sequenceId = NetToHost(headerOriginal.sequenceId); + headerConverted.errorNo = NetToHost(headerOriginal.errorNo); + headerConverted.dataLen = NetToHost(headerOriginal.dataLen); +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/communicator/src/header_converter.h b/mock/distributeddb/communicator/src/header_converter.h new file mode 100644 index 0000000000000000000000000000000000000000..7cccfde4ce651f4bca88b7e4343911c9f1c1601b --- /dev/null +++ b/mock/distributeddb/communicator/src/header_converter.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HEADER_CONVERTER_H +#define HEADER_CONVERTER_H + +#include "frame_header.h" + +namespace DistributedDB { +class HeaderConverter { +public: + static void ConvertHostToNet(const CommPhyHeader &headerOriginal, CommPhyHeader &headerConverted); + static void ConvertHostToNet(const CommPhyOptHeader &headerOriginal, CommPhyOptHeader &headerConverted); + static void ConvertHostToNet(const CommDivergeHeader &headerOriginal, CommDivergeHeader &headerConverted); + static void ConvertHostToNet(const MessageHeader &headerOriginal, MessageHeader &headerConverted); + + static void ConvertNetToHost(const CommPhyHeader &headerOriginal, CommPhyHeader &headerConverted); + static void ConvertNetToHost(const CommPhyOptHeader &headerOriginal, CommPhyOptHeader &headerConverted); + static void ConvertNetToHost(const CommDivergeHeader &headerOriginal, CommDivergeHeader &headerConverted); + static void ConvertNetToHost(const MessageHeader &headerOriginal, MessageHeader &headerConverted); +}; +} // namespace DistributedDB + +#endif // HEADER_CONVERTER_H \ No newline at end of file diff --git a/mock/distributeddb/communicator/src/message_transform.cpp b/mock/distributeddb/communicator/src/message_transform.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dfcfc5162e86d38824d2eaf38988935b8f7406c9 --- /dev/null +++ b/mock/distributeddb/communicator/src/message_transform.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "message_transform.h" +#include "protocol_proto.h" + +namespace DistributedDB { +int MessageTransform::RegTransformFunction(uint32_t msgId, const TransformFunc &inFunc) +{ + return ProtocolProto::RegTransformFunction(msgId, inFunc); +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/communicator/src/network_adapter.cpp b/mock/distributeddb/communicator/src/network_adapter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..061fe1a521df216e676e2d7e36ef3adf60680d82 --- /dev/null +++ b/mock/distributeddb/communicator/src/network_adapter.cpp @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "network_adapter.h" +#include "db_constant.h" +#include "db_errno.h" +#include "log_print.h" +#include "runtime_context.h" + +namespace DistributedDB { +namespace { +const std::string DEFAULT_PROCESS_LABEL = "Distributeddb_Anonymous_Process"; +const std::string SCHEDULE_QUEUE_TAG = "NetworkAdapter"; +} + +NetworkAdapter::NetworkAdapter() + : processLabel_(DEFAULT_PROCESS_LABEL), processCommunicator_(nullptr) +{ +} + +NetworkAdapter::NetworkAdapter(const std::string &inProcessLabel) + : processLabel_(inProcessLabel), processCommunicator_(nullptr) +{ +} + +NetworkAdapter::NetworkAdapter(const std::string &inProcessLabel, + const std::shared_ptr &inCommunicator) + : processLabel_(inProcessLabel), processCommunicator_(inCommunicator) +{ +} + +NetworkAdapter::~NetworkAdapter() +{ +} + +int NetworkAdapter::StartAdapter() +{ + LOGI("[NAdapt][Start] Enter, ProcessLabel=%s.", processLabel_.c_str()); + if (processLabel_.empty()) { + return -E_INVALID_ARGS; + } + if (!processCommunicator_) { + LOGE("[NAdapt][Start] ProcessCommunicator not be designated yet."); + return -E_INVALID_ARGS; + } + DBStatus errCode = processCommunicator_->Start(processLabel_); + if (errCode != DBStatus::OK) { + LOGE("[NAdapt][Start] Start Fail, errCode=%d.", static_cast(errCode)); + return -E_PERIPHERAL_INTERFACE_FAIL; + } + errCode = processCommunicator_->RegOnDataReceive(std::bind(&NetworkAdapter::OnDataReceiveHandler, this, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + if (errCode != DBStatus::OK) { + LOGE("[NAdapt][Start] RegOnDataReceive Fail, errCode=%d.", static_cast(errCode)); + // DO ROLLBACK + errCode = processCommunicator_->Stop(); + LOGI("[NAdapt][Start] ROLLBACK: Stop errCode=%d.", static_cast(errCode)); + return -E_PERIPHERAL_INTERFACE_FAIL; + } + errCode = processCommunicator_->RegOnDeviceChange(std::bind(&NetworkAdapter::OnDeviceChangeHandler, this, + std::placeholders::_1, std::placeholders::_2)); + if (errCode != DBStatus::OK) { + LOGE("[NAdapt][Start] RegOnDeviceChange Fail, errCode=%d.", static_cast(errCode)); + // DO ROLLBACK + errCode = processCommunicator_->RegOnDataReceive(nullptr); + LOGI("[NAdapt][Start] ROLLBACK: UnRegOnDataReceive errCode=%d.", static_cast(errCode)); + errCode = processCommunicator_->Stop(); + LOGI("[NAdapt][Start] ROLLBACK: Stop errCode=%d.", static_cast(errCode)); + return -E_PERIPHERAL_INTERFACE_FAIL; + } + // These code is compensation for the probable defect of IProcessCommunicator implementation. + // As described in the agreement, for the missed online situation, we search for the online devices at beginning. + // OnDeviceChangeHandler is reused to check the existence of peer process. + // Since at this point, the CommunicatorAggregator had not been fully initialized, + // We need an async task which bring about dependency on the lifecycle of this NetworkAdapter Object. + SearchOnlineRemoteDeviceAtStartup(); + LOGI("[NAdapt][Start] Exit."); + return E_OK; +} + +// StartAdapter and StopAdapter are all innerly called by ICommunicatorAggregator +// If StopAdapter is called, the StartAdapter must have been called successfully before, +// so processCommunicator_ won't be null +void NetworkAdapter::StopAdapter() +{ + LOGI("[NAdapt][Stop] Enter, ProcessLabel=%s.", processLabel_.c_str()); + DBStatus errCode = processCommunicator_->RegOnDeviceChange(nullptr); + if (errCode != DBStatus::OK) { + LOGE("[NAdapt][Stop] UnRegOnDeviceChange Fail, errCode=%d.", static_cast(errCode)); + } + errCode = processCommunicator_->RegOnDataReceive(nullptr); + if (errCode != DBStatus::OK) { + LOGE("[NAdapt][Stop] UnRegOnDataReceive Fail, errCode=%d.", static_cast(errCode)); + } + errCode = processCommunicator_->Stop(); + if (errCode != DBStatus::OK) { + LOGE("[NAdapt][Stop] Stop Fail, errCode=%d.", static_cast(errCode)); + } + // We don't reset the shared_ptr of commProvider here, the release of commProvider is done by deconstruct of adapter + // In this way, the adapter can be start again after stop it, since it still hold the an valid commProvider + // The async task is dependent on this Object. we have to wait until all async task finished. + LOGI("[NAdapt][Stop] Wait all async task done."); + std::unique_lock asyncTaskDoneLock(asyncTaskDoneMutex_); + asyncTaskDoneCv_.wait(asyncTaskDoneLock, [this]{ return pendingAsyncTaskCount_ <= 0; }); + LOGI("[NAdapt][Stop] Exit."); +} + +namespace { +uint32_t CheckAndAdjustMtuSize(uint32_t inMtuSize) +{ + if (inMtuSize < DBConstant::MIN_MTU_SIZE) { + return DBConstant::MIN_MTU_SIZE; + } else if (inMtuSize > DBConstant::MAX_MTU_SIZE) { + return DBConstant::MAX_MTU_SIZE; + } else { + return (inMtuSize - (inMtuSize % sizeof(uint64_t))); // Octet alignment + } +} + +uint32_t CheckAndAdjustTimeout(uint32_t inTimeout) +{ + if (inTimeout < DBConstant::MIN_TIMEOUT) { + return DBConstant::MIN_TIMEOUT; + } else if (inTimeout > DBConstant::MAX_TIMEOUT) { + return DBConstant::MAX_TIMEOUT; + } else { + return inTimeout; + } +} +} + +uint32_t NetworkAdapter::GetMtuSize() +{ + std::lock_guard mtuSizeLockGuard(mtuSizeMutex_); + if (!isMtuSizeValid_) { + mtuSize_ = processCommunicator_->GetMtuSize(); + LOGI("[NAdapt][GetMtu] mtuSize=%u.", mtuSize_); + mtuSize_ = CheckAndAdjustMtuSize(mtuSize_); + isMtuSizeValid_ = true; + } + return mtuSize_; +} + +uint32_t NetworkAdapter::GetMtuSize(const std::string &target) +{ +#ifndef OMIT_MTU_CACHE + DeviceInfos devInfo; + devInfo.identifier = target; + uint32_t oriMtuSize = processCommunicator_->GetMtuSize(devInfo); + return CheckAndAdjustMtuSize(oriMtuSize); +#else + std::lock_guard mtuSizeLockGuard(mtuSizeMutex_); + if (devMapMtuSize_.count(target) == 0) { + DeviceInfos devInfo; + devInfo.identifier = target; + uint32_t oriMtuSize = processCommunicator_->GetMtuSize(devInfo); + LOGI("[NAdapt][GetMtu] mtuSize=%u of target=%s{private}.", oriMtuSize, target.c_str()); + devMapMtuSize_[target] = CheckAndAdjustMtuSize(oriMtuSize); + } + return devMapMtuSize_[target]; +#endif +} + +uint32_t NetworkAdapter::GetTimeout() +{ + uint32_t timeout = processCommunicator_->GetTimeout(); + LOGI("[NAdapt][GetTimeout] timeout_=%u ms.", timeout); + return CheckAndAdjustTimeout(timeout); +} + +uint32_t NetworkAdapter::GetTimeout(const std::string &target) +{ + DeviceInfos devInfos; + devInfos.identifier = target; + uint32_t timeout = processCommunicator_->GetTimeout(devInfos); + LOGI("[NAdapt][GetTimeout] timeout=%u ms of target=%s{private}.", timeout, target.c_str()); + return CheckAndAdjustTimeout(timeout); +} + +int NetworkAdapter::GetLocalIdentity(std::string &outTarget) +{ + std::lock_guard identityLockGuard(identityMutex_); + DeviceInfos devInfo = processCommunicator_->GetLocalDeviceInfos(); + if (devInfo.identifier.empty()) { + return -E_PERIPHERAL_INTERFACE_FAIL; + } + if (devInfo.identifier != localIdentity_) { + LOGI("[NAdapt][GetLocal] localIdentity=%s{private}.", devInfo.identifier.c_str()); + } + localIdentity_ = devInfo.identifier; + outTarget = localIdentity_; + return E_OK; +} + +int NetworkAdapter::SendBytes(const std::string &dstTarget, const uint8_t *bytes, uint32_t length) +{ + if (bytes == nullptr || length == 0) { + return -E_INVALID_ARGS; + } + LOGI("[NAdapt][SendBytes] Enter, to=%s{private}, length=%u", dstTarget.c_str(), length); + DeviceInfos dstDevInfo; + dstDevInfo.identifier = dstTarget; + DBStatus errCode = processCommunicator_->SendData(dstDevInfo, bytes, length); + if (errCode != DBStatus::OK) { + LOGE("[NAdapt][SendBytes] SendData Fail, errCode=%d.", static_cast(errCode)); + // These code is compensation for the probable defect of IProcessCommunicator implementation. + // As described in the agreement, for the missed offline situation, we check if still online at send fail. + // OnDeviceChangeHandler is reused but check the existence of peer process is done outerly. + // Since this thread is the sending_thread of the CommunicatorAggregator, + // We need an async task which bring about dependency on the lifecycle of this NetworkAdapter Object. + CheckDeviceOfflineAfterSendFail(dstDevInfo); + return -E_PERIPHERAL_INTERFACE_FAIL; + } + return E_OK; +} + +int NetworkAdapter::RegBytesReceiveCallback(const BytesReceiveCallback &onReceive, const Finalizer &inOper) +{ + std::lock_guard onReceiveLockGard(onReceiveMutex_); + return RegCallBack(onReceive, onReceiveHandle_, inOper, onReceiveFinalizer_); +} + +int NetworkAdapter::RegTargetChangeCallback(const TargetChangeCallback &onChange, const Finalizer &inOper) +{ + std::lock_guard onChangeLockGard(onChangeMutex_); + return RegCallBack(onChange, onChangeHandle_, inOper, onChangeFinalizer_); +} + +int NetworkAdapter::RegSendableCallback(const SendableCallback &onSendable, const Finalizer &inOper) +{ + std::lock_guard onSendableLockGard(onSendableMutex_); + return RegCallBack(onSendable, onSendableHandle_, inOper, onSendableFinalizer_); +} + +void NetworkAdapter::OnDataReceiveHandler(const DeviceInfos &srcDevInfo, const uint8_t *data, uint32_t length) +{ + if (data == nullptr || length == 0) { + LOGE("[NAdapt][OnDataRecv] data nullptr or length = %u.", length); + return; + } + uint32_t headLength = 0; + std::vector userId; + std::string currentUserId; + DBStatus errCode = processCommunicator_->CheckAndGetDataHeadInfo(data, length, headLength, userId); + LOGI("[NAdapt][OnDataRecv] Enter, from=%s{private}, extendHeadLength=%u, totalLength=%u", + srcDevInfo.identifier.c_str(), headLength, length); + if (errCode == NO_PERMISSION) { + LOGI("[NAdapt][OnDataRecv] userId dismatched, drop packet"); + return; + } + { + std::lock_guard onReceiveLockGard(onReceiveMutex_); + if (!onReceiveHandle_) { + LOGE("[NAdapt][OnDataRecv] onReceiveHandle invalid."); + return; + } + if (userId.size() >= 1) { + currentUserId = userId[0]; + } + onReceiveHandle_(srcDevInfo.identifier, data + headLength, length - headLength, currentUserId); + } + // These code is compensation for the probable defect of IProcessCommunicator implementation. + // As described in the agreement, for the missed online situation, we check the source dev when received. + // OnDeviceChangeHandler is reused to check the existence of peer process. + // Since this thread is the callback_thread of IProcessCommunicator, we do this check task directly in this thread. + CheckDeviceOnlineAfterReception(srcDevInfo); +} + +void NetworkAdapter::OnDeviceChangeHandler(const DeviceInfos &devInfo, bool isOnline) +{ + LOGI("[NAdapt][OnDeviceChange] Enter, dev=%s{private}, isOnline=%d", devInfo.identifier.c_str(), isOnline); + // These code is compensation for the probable defect of IProcessCommunicator implementation. + // As described in the agreement, for the mistake online situation, we check the existence of peer process. + // The IProcessCommunicator implementation guarantee that no mistake offline will happen. + if (isOnline) { + if (!processCommunicator_->IsSameProcessLabelStartedOnPeerDevice(devInfo)) { + LOGI("[NAdapt][OnDeviceChange] ######## Detect Not Really Online ########."); + std::lock_guard onlineRemoteDevLockGuard(onlineRemoteDevMutex_); + onlineRemoteDev_.erase(devInfo.identifier); + return; + } + std::lock_guard onlineRemoteDevLockGuard(onlineRemoteDevMutex_); + onlineRemoteDev_.insert(devInfo.identifier); + } else { + std::lock_guard onlineRemoteDevLockGuard(onlineRemoteDevMutex_); + onlineRemoteDev_.erase(devInfo.identifier); + } + // End compensation, do callback. + std::lock_guard onChangeLockGard(onChangeMutex_); + if (!onChangeHandle_) { + LOGE("[NAdapt][OnDeviceChange] onChangeHandle_ invalid."); + return; + } + onChangeHandle_(devInfo.identifier, isOnline); +} + +void NetworkAdapter::SearchOnlineRemoteDeviceAtStartup() +{ + std::vector onlineDev = processCommunicator_->GetRemoteOnlineDeviceInfosList(); + LOGE("[NAdapt][SearchOnline] onlineDev count = %zu.", onlineDev.size()); + if (!onlineDev.empty()) { + pendingAsyncTaskCount_.fetch_add(1); + // Note: onlineDev should be captured by value (must not by reference) + TaskAction callbackTask = [onlineDev, this]() { + LOGI("[NAdapt][SearchOnline] Begin Callback In Async Task."); + std::string localIdentity; + GetLocalIdentity(localIdentity); // It doesn't matter if getlocal fail and localIdentity be an empty string + for (auto &entry : onlineDev) { + if (entry.identifier == localIdentity) { + LOGW("[NAdapt][SearchOnline] ######## Detect Local Device in Remote Device List ########."); + continue; + } + OnDeviceChangeHandler(entry, true); + } + pendingAsyncTaskCount_.fetch_sub(1); + asyncTaskDoneCv_.notify_all(); + LOGI("[NAdapt][SearchOnline] End Callback In Async Task."); + }; + // Use ScheduleQueuedTask to keep order + int errCode = RuntimeContext::GetInstance()->ScheduleQueuedTask(SCHEDULE_QUEUE_TAG, callbackTask); + if (errCode != E_OK) { + LOGE("[NAdapt][SearchOnline] ScheduleQueuedTask failed, errCode = %d.", errCode); + pendingAsyncTaskCount_.fetch_sub(1); + asyncTaskDoneCv_.notify_all(); + } + } +} + +void NetworkAdapter::CheckDeviceOnlineAfterReception(const DeviceInfos &devInfo) +{ + bool isAlreadyOnline = true; + { + std::lock_guard onlineRemoteDevLockGuard(onlineRemoteDevMutex_); + if (onlineRemoteDev_.count(devInfo.identifier) == 0) { + isAlreadyOnline = false; + } + } + + // Seem offline but receive data from it, let OnDeviceChangeHandler check whether it is really online + if (!isAlreadyOnline) { + OnDeviceChangeHandler(devInfo, true); + } +} + +void NetworkAdapter::CheckDeviceOfflineAfterSendFail(const DeviceInfos &devInfo) +{ + // Note: only the identifier field of devInfo is valid, enough to call IsSameProcessLabelStartedOnPeerDevice + bool isAlreadyOffline = true; + { + std::lock_guard onlineRemoteDevLockGuard(onlineRemoteDevMutex_); + if (onlineRemoteDev_.count(devInfo.identifier) != 0) { + isAlreadyOffline = false; + } + } + + // Seem online but send fail, we have to check whether still online + if (!isAlreadyOffline) { + if (!processCommunicator_->IsSameProcessLabelStartedOnPeerDevice(devInfo)) { + LOGW("[NAdapt][CheckAfterSend] ######## Missed Offline Detected ########."); + { + // Mark this device not online immediately to avoid repeatedly miss-offline detect when send continually + std::lock_guard onlineRemoteDevLockGuard(onlineRemoteDevMutex_); + onlineRemoteDev_.erase(devInfo.identifier); + } + pendingAsyncTaskCount_.fetch_add(1); + // Note: devInfo should be captured by value (must not by reference) + TaskAction callbackTask = [devInfo, this]() { + LOGI("[NAdapt][CheckAfterSend] In Async Task, devInfo=%s{private}.", devInfo.identifier.c_str()); + OnDeviceChangeHandler(devInfo, false); + pendingAsyncTaskCount_.fetch_sub(1); + asyncTaskDoneCv_.notify_all(); + }; + // Use ScheduleQueuedTask to keep order + int errCode = RuntimeContext::GetInstance()->ScheduleQueuedTask(SCHEDULE_QUEUE_TAG, callbackTask); + if (errCode != E_OK) { + LOGE("[NAdapt][CheckAfterSend] ScheduleQueuedTask failed, errCode = %d.", errCode); + pendingAsyncTaskCount_.fetch_sub(1); + asyncTaskDoneCv_.notify_all(); + } + } + } +} + +bool NetworkAdapter::IsDeviceOnline(const std::string &device) +{ + std::lock_guard onlineRemoteDevLockGuard(onlineRemoteDevMutex_); + return (onlineRemoteDev_.find(device) != onlineRemoteDev_.end()); +} + +std::shared_ptr NetworkAdapter::GetExtendHeaderHandle(const ExtendInfo ¶mInfo) +{ + return processCommunicator_->GetExtendHeaderHandle(paramInfo); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/communicator/src/protocol_proto.cpp b/mock/distributeddb/communicator/src/protocol_proto.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d27be2af6cc7e1559b78a609f3b3fb660f14d26 --- /dev/null +++ b/mock/distributeddb/communicator/src/protocol_proto.cpp @@ -0,0 +1,1083 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "protocol_proto.h" +#include +#include +#include "hash.h" +#include "securec.h" +#include "version.h" +#include "db_common.h" +#include "log_print.h" +#include "macro_utils.h" +#include "endian_convert.h" +#include "header_converter.h" + +namespace DistributedDB { +namespace { +const uint16_t MAGIC_CODE = 0xAAAA; +const uint16_t PROTOCOL_VERSION = 0; +// Compatibility Final Method. 3 Correspond To Version 1.1.4(104) +const uint16_t DB_GLOBAL_VERSION = SOFTWARE_VERSION_CURRENT - SOFTWARE_VERSION_EARLIEST; +const uint8_t PACKET_TYPE_FRAGMENTED = BITX(0); // Use bit 0 +const uint8_t PACKET_TYPE_NOT_FRAGMENTED = 0; +const uint8_t MAX_PADDING_LEN = 7; +const uint32_t LENGTH_BEFORE_SUM_RANGE = sizeof(uint64_t) + sizeof(uint64_t); +const uint32_t MAX_FRAME_LEN = 32 * 1024 * 1024; // Max 32 MB, 1024 is scale +const uint16_t MIN_FRAGMENT_COUNT = 2; // At least a frame will be splited into 2 parts +// LabelExchange(Ack) Frame Field Length +const uint32_t LABEL_VER_LEN = sizeof(uint64_t); +const uint32_t DISTINCT_VALUE_LEN = sizeof(uint64_t); +const uint32_t SEQUENCE_ID_LEN = sizeof(uint64_t); +// Note: COMM_LABEL_LENGTH is defined in communicator_type_define.h +const uint32_t COMM_LABEL_COUNT_LEN = sizeof(uint64_t); +// Local func to set and get frame Type from packet Type field +void SetFrameType(uint8_t &inPacketType, FrameType inFrameType) +{ + inPacketType &= 0x0F; // Use 0x0F to clear high for bits + inPacketType |= (static_cast(inFrameType) << 4); // frame type is on high 4 bits +} +FrameType GetFrameType(uint8_t inPacketType) +{ + uint8_t frameType = ((inPacketType & 0xF0) >> 4); // Use 0xF0 to get high 4 bits + if (frameType >= static_cast(FrameType::INVALID_MAX_FRAME_TYPE)) { + return FrameType::INVALID_MAX_FRAME_TYPE; + } + return static_cast(frameType); +} +} + +std::map ProtocolProto::msgIdMapFunc_; + +uint32_t ProtocolProto::GetAppLayerFrameHeaderLength() +{ + uint32_t length = sizeof(CommPhyHeader) + sizeof(CommDivergeHeader); + return length; +} + +uint32_t ProtocolProto::GetLengthBeforeSerializedData() +{ + uint32_t length = sizeof(CommPhyHeader) + sizeof(CommDivergeHeader) + sizeof(MessageHeader); + return length; +} + +uint32_t ProtocolProto::GetCommLayerFrameHeaderLength() +{ + uint32_t length = sizeof(CommPhyHeader); + return length; +} + +SerialBuffer *ProtocolProto::ToSerialBuffer(const Message *inMsg, int &outErrorNo, + std::shared_ptr &extendHandle, bool onlyMsgHeader) +{ + if (inMsg == nullptr) { + outErrorNo = -E_INVALID_ARGS; + return nullptr; + } + + uint32_t serializeLen = 0; + if (!onlyMsgHeader) { + int errCode = CalculateDataSerializeLength(inMsg, serializeLen); + if (errCode != E_OK) { + outErrorNo = errCode; + return nullptr; + } + } + uint32_t headSize = 0; + int errCode = GetExtendHeadDataSize(extendHandle, headSize); + if (errCode != E_OK) { + outErrorNo = errCode; + return nullptr; + } + + SerialBuffer *buffer = new (std::nothrow) SerialBuffer(); + if (buffer == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + if (headSize > 0) { + buffer->SetExtendHeadLength(headSize); + } + // serializeLen maybe not 8-bytes aligned, let SerialBuffer deal with the padding. + uint32_t payLoadLength = serializeLen + sizeof(MessageHeader); + errCode = buffer->AllocBufferByPayloadLength(payLoadLength, GetAppLayerFrameHeaderLength()); + if (errCode != E_OK) { + LOGE("[Proto][ToSerial] Alloc Fail, errCode=%d.", errCode); + goto ERROR_HANDLE; + } + errCode = FillExtendHeadDataIfNeed(extendHandle, buffer, headSize); + if (errCode != E_OK) { + goto ERROR_HANDLE; + } + + // Serialize the MessageHeader and data if need + errCode = SerializeMessage(buffer, inMsg); + if (errCode != E_OK) { + LOGE("[Proto][ToSerial] Serialize Fail, errCode=%d.", errCode); + goto ERROR_HANDLE; + } + outErrorNo = E_OK; + return buffer; +ERROR_HANDLE: + outErrorNo = errCode; + delete buffer; + buffer = nullptr; + return nullptr; +} + +Message *ProtocolProto::ToMessage(const SerialBuffer *inBuff, int &outErrorNo, bool onlyMsgHeader) +{ + if (inBuff == nullptr) { + outErrorNo = -E_INVALID_ARGS; + return nullptr; + } + Message *outMsg = new (std::nothrow) Message(); + if (outMsg == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + int errCode = DeSerializeMessage(inBuff, outMsg, onlyMsgHeader); + if (errCode != E_OK && errCode != -E_NOT_REGISTER) { + LOGE("[Proto][ToMessage] DeSerialize Fail, errCode=%d.", errCode); + outErrorNo = errCode; + delete outMsg; + outMsg = nullptr; + return nullptr; + } + // If messageId not register in this software version, we return errCode and the Message without an object. + outErrorNo = errCode; + return outMsg; +} + +SerialBuffer *ProtocolProto::BuildEmptyFrameForVersionNegotiate(int &outErrorNo) +{ + SerialBuffer *buffer = new (std::nothrow) SerialBuffer(); + if (buffer == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + + // Empty frame has no payload, only header + int errCode = buffer->AllocBufferByPayloadLength(0, GetCommLayerFrameHeaderLength()); + if (errCode != E_OK) { + LOGE("[Proto][BuildEmpty] Alloc Fail, errCode=%d.", errCode); + outErrorNo = errCode; + delete buffer; + buffer = nullptr; + return nullptr; + } + outErrorNo = E_OK; + return buffer; +} + +SerialBuffer *ProtocolProto::BuildFeedbackMessageFrame(const Message *inMsg, const LabelType &inLabel, + int &outErrorNo) +{ + std::shared_ptr extendHandle = nullptr; + SerialBuffer *buffer = ToSerialBuffer(inMsg, outErrorNo, extendHandle, true); + if (buffer == nullptr) { + // outErrorNo had already been set in ToSerialBuffer + return nullptr; + } + int errCode = ProtocolProto::SetDivergeHeader(buffer, inLabel); + if (errCode != E_OK) { + LOGE("[Proto][BuildFeedback] Set DivergeHeader fail, label=%s, errCode=%d.", VEC_TO_STR(inLabel), errCode); + outErrorNo = errCode; + delete buffer; + buffer = nullptr; + return nullptr; + } + outErrorNo = E_OK; + return buffer; +} + +SerialBuffer *ProtocolProto::BuildLabelExchange(uint64_t inDistinctValue, uint64_t inSequenceId, + const std::set &inLabels, int &outErrorNo) +{ + // Size of inLabels won't be too large. + // The upper layer code(inside this communicator module) guarantee that size of each Label equals COMM_LABEL_LENGTH + uint32_t payloadLen = LABEL_VER_LEN + DISTINCT_VALUE_LEN + SEQUENCE_ID_LEN + COMM_LABEL_COUNT_LEN + + inLabels.size() * COMM_LABEL_LENGTH; + SerialBuffer *buffer = new (std::nothrow) SerialBuffer(); + if (buffer == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + int errCode = buffer->AllocBufferByPayloadLength(payloadLen, GetCommLayerFrameHeaderLength()); + if (errCode != E_OK) { + LOGE("[Proto][BuildLabel] Alloc Fail, errCode=%d.", errCode); + outErrorNo = errCode; + delete buffer; + buffer = nullptr; + return nullptr; + } + + auto payloadByteLen = buffer->GetWritableBytesForPayload(); + auto fieldPtr = reinterpret_cast(payloadByteLen.first); + *fieldPtr++ = HostToNet(static_cast(PROTOCOL_VERSION)); + *fieldPtr++ = HostToNet(inDistinctValue); + *fieldPtr++ = HostToNet(inSequenceId); + *fieldPtr++ = HostToNet(static_cast(inLabels.size())); + // Note: don't worry, memory length had been carefully calculated above + auto bytePtr = reinterpret_cast(fieldPtr); + for (auto &eachLabel : inLabels) { + for (auto &eachByte : eachLabel) { + *bytePtr++ = eachByte; + } + } + outErrorNo = E_OK; + return buffer; +} + +SerialBuffer *ProtocolProto::BuildLabelExchangeAck(uint64_t inDistinctValue, uint64_t inSequenceId, int &outErrorNo) +{ + uint32_t payloadLen = LABEL_VER_LEN + DISTINCT_VALUE_LEN + SEQUENCE_ID_LEN; + SerialBuffer *buffer = new (std::nothrow) SerialBuffer(); + if (buffer == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + int errCode = buffer->AllocBufferByPayloadLength(payloadLen, GetCommLayerFrameHeaderLength()); + if (errCode != E_OK) { + LOGE("[Proto][BuildLabelAck] Alloc Fail, errCode=%d.", errCode); + outErrorNo = errCode; + delete buffer; + buffer = nullptr; + return nullptr; + } + + auto payloadByteLen = buffer->GetWritableBytesForPayload(); + auto fieldPtr = reinterpret_cast(payloadByteLen.first); + *fieldPtr++ = HostToNet(static_cast(PROTOCOL_VERSION)); + *fieldPtr++ = HostToNet(inDistinctValue); + *fieldPtr++ = HostToNet(inSequenceId); + outErrorNo = E_OK; + return buffer; +} + +int ProtocolProto::SplitFrameIntoPacketsIfNeed(const SerialBuffer *inBuff, uint32_t inMtuSize, + std::vector, uint32_t>> &outPieces) +{ + auto bufferBytesLen = inBuff->GetReadOnlyBytesForEntireBuffer(); + if ((bufferBytesLen.second + inBuff->GetExtendHeadLength()) <= inMtuSize) { + return E_OK; + } + uint32_t modifyMtuSize = inMtuSize - inBuff->GetExtendHeadLength(); + // Do Fragmentaion! This function aims at calculate how many fragments to be split into. + auto frameBytesLen = inBuff->GetReadOnlyBytesForEntireFrame(); // Padding not in the range of fragmentation. + uint32_t lengthToSplit = frameBytesLen.second - sizeof(CommPhyHeader); // The former is always larger than latter. + // The inMtuSize pass from CommunicatorAggregator is large enough to be subtract by the latter two. + uint32_t maxFragmentLen = modifyMtuSize - sizeof(CommPhyHeader) - sizeof(CommPhyOptHeader); + // It can be proved that lengthToSplit is always larger than maxFragmentLen, so quotient won't be zero. + // The maxFragmentLen won't be zero and in fact large enough to make sure no precision loss during division + uint16_t quotient = lengthToSplit / maxFragmentLen; + uint32_t remainder = lengthToSplit % maxFragmentLen; + // Finally we get the fragCount for this frame + uint16_t fragCount = ((remainder == 0) ? quotient : (quotient + 1)); + // Get CommPhyHeader of this frame to be modified for each packets (Header in network endian) + auto oriPhyHeader = reinterpret_cast(frameBytesLen.first); + FrameFragmentInfo fragInfo = {inBuff->GetOringinalAddr(), inBuff->GetExtendHeadLength(), lengthToSplit, fragCount}; + return FrameFragmentation(frameBytesLen.first + sizeof(CommPhyHeader), fragInfo, *oriPhyHeader, outPieces); +} + +int ProtocolProto::AnalyzeSplitStructure(const ParseResult &inResult, uint32_t &outFragLen, uint32_t &outLastFragLen) +{ + uint32_t frameLen = inResult.GetFrameLen(); + uint16_t fragCount = inResult.GetFragCount(); + uint16_t fragNo = inResult.GetFragNo(); + + // Firstly: Check frameLen + if (frameLen <= sizeof(CommPhyHeader) || frameLen > MAX_FRAME_LEN) { + LOGE("[Proto][ParsePhyOpt] FrameLen=%u illegal.", frameLen); + return -E_PARSE_FAIL; + } + + // Secondly: Check fragCount and fragNo + uint32_t lengthBeSplit = frameLen - sizeof(CommPhyHeader); + if (fragCount == 0 || fragCount < MIN_FRAGMENT_COUNT || fragCount > lengthBeSplit || fragNo >= fragCount) { + LOGE("[Proto][ParsePhyOpt] FragCount=%u or fragNo=%u illegal.", fragCount, fragNo); + return -E_PARSE_FAIL; + } + + // Finally: Check length relation deeply + uint32_t quotient = lengthBeSplit / fragCount; + uint16_t remainder = lengthBeSplit % fragCount; + outFragLen = quotient; + outLastFragLen = quotient + remainder; + uint32_t thisFragLen = ((fragNo != fragCount - 1) ? outFragLen : outLastFragLen); // subtract by 1 for index + if (sizeof(CommPhyHeader) + sizeof(CommPhyOptHeader) + thisFragLen + + inResult.GetPaddingLen() != inResult.GetPacketLen()) { + LOGE("[Proto][ParsePhyOpt] Length Error: FrameLen=%u, FragCount=%u, fragNo=%u, PaddingLen=%u, PacketLen=%u", + frameLen, fragCount, fragNo, inResult.GetPaddingLen(), inResult.GetPacketLen()); + return -E_PARSE_FAIL; + } + + return E_OK; +} + +int ProtocolProto::CombinePacketIntoFrame(SerialBuffer *inFrame, const uint8_t *pktBytes, uint32_t pktLength, + uint32_t fragOffset, uint32_t fragLength) +{ + // inFrame is the destination, pktBytes and pktLength are the source, fragOffset and fragLength give the boundary + // Firstly: Check the length relation of source, even this check is not supposed to fail + if (sizeof(CommPhyHeader) + sizeof(CommPhyOptHeader) + fragLength > pktLength) { + return -E_LENGTH_ERROR; + } + // Secondly: Check the length relation of destination, even this check is not supposed to fail + auto frameByteLen = inFrame->GetWritableBytesForEntireFrame(); + if (sizeof(CommPhyHeader) + fragOffset + fragLength > frameByteLen.second) { + return -E_LENGTH_ERROR; + } + // Finally: Do Combination! + const uint8_t *srcByteHead = pktBytes + sizeof(CommPhyHeader) + sizeof(CommPhyOptHeader); + uint8_t *dstByteHead = frameByteLen.first + sizeof(CommPhyHeader) + fragOffset; + uint32_t dstLeftLen = frameByteLen.second - sizeof(CommPhyHeader) - fragOffset; + errno_t errCode = memcpy_s(dstByteHead, dstLeftLen, srcByteHead, fragLength); + if (errCode != EOK) { + return -E_SECUREC_ERROR; + } + return E_OK; +} + +int ProtocolProto::RegTransformFunction(uint32_t msgId, const TransformFunc &inFunc) +{ + if (msgIdMapFunc_.count(msgId) != 0) { + return -E_ALREADY_REGISTER; + } + if (!inFunc.computeFunc || !inFunc.serializeFunc || !inFunc.deserializeFunc) { + return -E_INVALID_ARGS; + } + msgIdMapFunc_[msgId] = inFunc; + return E_OK; +} + +void ProtocolProto::UnRegTransformFunction(uint32_t msgId) +{ + if (msgIdMapFunc_.count(msgId) != 0) { + msgIdMapFunc_.erase(msgId); + } +} + +int ProtocolProto::SetDivergeHeader(SerialBuffer *inBuff, const LabelType &inCommLabel) +{ + if (inBuff == nullptr) { + return -E_INVALID_ARGS; + } + auto headerByteLen = inBuff->GetWritableBytesForHeader(); + if (headerByteLen.second != GetAppLayerFrameHeaderLength()) { + return -E_INVALID_ARGS; + } + auto payloadByteLen = inBuff->GetReadOnlyBytesForPayload(); + + CommDivergeHeader divergeHeader; + divergeHeader.version = PROTOCOL_VERSION; + divergeHeader.reserved = 0; + divergeHeader.payLoadLen = payloadByteLen.second; + // The upper layer code(inside this communicator module) guarantee that size of inCommLabel equal COMM_LABEL_LENGTH + for (unsigned int i = 0; i < COMM_LABEL_LENGTH; i++) { + divergeHeader.commLabel[i] = inCommLabel[i]; + } + HeaderConverter::ConvertHostToNet(divergeHeader, divergeHeader); + + errno_t errCode = memcpy_s(headerByteLen.first + sizeof(CommPhyHeader), + headerByteLen.second - sizeof(CommPhyHeader), &divergeHeader, sizeof(CommDivergeHeader)); + if (errCode != EOK) { + return -E_SECUREC_ERROR; + } + return E_OK; +} + +namespace { +void FillPhyHeaderLenInfo(CommPhyHeader &header, uint32_t packetLen, uint64_t sum, uint8_t type, uint8_t paddingLen) +{ + header.packetLen = packetLen; + header.checkSum = sum; + header.packetType |= type; + header.paddingLen = paddingLen; +} +} + +int ProtocolProto::SetPhyHeader(SerialBuffer *inBuff, const PhyHeaderInfo &inInfo) +{ + if (inBuff == nullptr) { + return -E_INVALID_ARGS; + } + auto headerByteLen = inBuff->GetWritableBytesForHeader(); + if (headerByteLen.second < sizeof(CommPhyHeader)) { + return -E_INVALID_ARGS; + } + auto bufferByteLen = inBuff->GetReadOnlyBytesForEntireBuffer(); + auto frameByteLen = inBuff->GetReadOnlyBytesForEntireFrame(); + + uint32_t packetLen = bufferByteLen.second; + uint8_t paddingLen = static_cast(bufferByteLen.second - frameByteLen.second); + uint8_t packetType = PACKET_TYPE_NOT_FRAGMENTED; + if (inInfo.frameType != FrameType::INVALID_MAX_FRAME_TYPE) { + SetFrameType(packetType, inInfo.frameType); + } else { + return -E_INVALID_ARGS; + } + + CommPhyHeader phyHeader; + phyHeader.magic = MAGIC_CODE; + phyHeader.version = PROTOCOL_VERSION; + phyHeader.sourceId = inInfo.sourceId; + phyHeader.frameId = inInfo.frameId; + phyHeader.packetType = 0; + phyHeader.dbIntVer = DB_GLOBAL_VERSION; + FillPhyHeaderLenInfo(phyHeader, packetLen, 0, packetType, paddingLen); // Sum is calculated afterwards + HeaderConverter::ConvertHostToNet(phyHeader, phyHeader); + + errno_t retCode = memcpy_s(headerByteLen.first, headerByteLen.second, &phyHeader, sizeof(CommPhyHeader)); + if (retCode != EOK) { + return -E_SECUREC_ERROR; + } + + uint64_t sumResult = 0; + int errCode = CalculateXorSum(bufferByteLen.first + LENGTH_BEFORE_SUM_RANGE, + bufferByteLen.second - LENGTH_BEFORE_SUM_RANGE, sumResult); + if (errCode != E_OK) { + return -E_SUM_CALCULATE_FAIL; + } + + auto ptrPhyHeader = reinterpret_cast(headerByteLen.first); + ptrPhyHeader->checkSum = HostToNet(sumResult); + + return E_OK; +} + +int ProtocolProto::CheckAndParsePacket(const std::string &srcTarget, const uint8_t *bytes, uint32_t length, + ParseResult &outResult) +{ + if (bytes == nullptr || length > MAX_TOTAL_LEN) { + return -E_INVALID_ARGS; + } + int errCode = ParseCommPhyHeader(srcTarget, bytes, length, outResult); + if (errCode != E_OK) { + LOGE("[Proto][ParsePacket] Parse PhyHeader Fail, errCode=%d.", errCode); + return errCode; + } + + if (outResult.GetFrameTypeInfo() == FrameType::EMPTY) { + return E_OK; // Do nothing more for empty frame + } + + if (outResult.IsFragment()) { + errCode = ParseCommPhyOptHeader(bytes, length, outResult); + if (errCode != E_OK) { + LOGE("[Proto][ParsePacket] Parse CommPhyOptHeader Fail, errCode=%d.", errCode); + return errCode; + } + } else if (outResult.GetFrameTypeInfo() != FrameType::APPLICATION_MESSAGE) { + errCode = ParseCommLayerPayload(bytes, length, outResult); + if (errCode != E_OK) { + LOGE("[Proto][ParsePacket] Parse CommLayerPayload Fail, errCode=%d.", errCode); + return errCode; + } + } else { + errCode = ParseCommDivergeHeader(bytes, length, outResult); + if (errCode != E_OK) { + LOGE("[Proto][ParsePacket] Parse DivergeHeader Fail, errCode=%d.", errCode); + return errCode; + } + } + return E_OK; +} + +int ProtocolProto::CheckAndParseFrame(const SerialBuffer *inBuff, ParseResult &outResult) +{ + if (inBuff == nullptr || outResult.IsFragment()) { + return -E_INTERNAL_ERROR; + } + auto frameBytesLen = inBuff->GetReadOnlyBytesForEntireFrame(); + if (outResult.GetFrameTypeInfo() != FrameType::APPLICATION_MESSAGE) { + int errCode = ParseCommLayerPayload(frameBytesLen.first, frameBytesLen.second, outResult); + if (errCode != E_OK) { + LOGE("[Proto][ParseFrame] Parse CommLayerPayload Fail, errCode=%d.", errCode); + return errCode; + } + } else { + int errCode = ParseCommDivergeHeader(frameBytesLen.first, frameBytesLen.second, outResult); + if (errCode != E_OK) { + LOGE("[Proto][ParseFrame] Parse DivergeHeader Fail, errCode=%d.", errCode); + return errCode; + } + } + return E_OK; +} + +void ProtocolProto::DisplayPacketInformation(const uint8_t *bytes, uint32_t length) +{ + static std::map frameTypeStr{ + {FrameType::EMPTY, "EmptyFrame"}, + {FrameType::APPLICATION_MESSAGE, "AppLayerFrame"}, + {FrameType::COMMUNICATION_LABEL_EXCHANGE, "CommLayerFrame_LabelExchange"}, + {FrameType::COMMUNICATION_LABEL_EXCHANGE_ACK, "CommLayerFrame_LabelExchangeAck"}}; + + if (length < sizeof(CommPhyHeader)) { + return; + } + auto phyHeader = reinterpret_cast(bytes); + uint32_t frameId = NetToHost(phyHeader->frameId); + uint8_t pktType = NetToHost(phyHeader->packetType); + bool isFragment = ((pktType & PACKET_TYPE_FRAGMENTED) != 0); + FrameType frameType = GetFrameType(pktType); + if (frameType == FrameType::INVALID_MAX_FRAME_TYPE) { + LOGW("[Proto][Display] This is unrecognized frame, pktType=%" PRIu8 ".", pktType); + return; + } + if (isFragment) { + if (length < sizeof(CommPhyHeader) + sizeof(CommPhyOptHeader)) { + return; + } + auto phyOpt = reinterpret_cast(bytes + sizeof(CommPhyHeader)); + LOGI("[Proto][Display] This is %s, frameId=%u, frameLen=%u, fragCount=%u, fragNo=%u.", + frameTypeStr[frameType].c_str(), frameId, NetToHost(phyOpt->frameLen), + NetToHost(phyOpt->fragCount), NetToHost(phyOpt->fragNo)); + } else { + LOGI("[Proto][Display] This is %s, frameId=%u.", frameTypeStr[frameType].c_str(), frameId); + } +} + +int ProtocolProto::CalculateXorSum(const uint8_t *bytes, uint32_t length, uint64_t &outSum) +{ + if (length % sizeof(uint64_t) != 0) { + LOGE("[Proto][CalcuXorSum] Length=%d not multiple of eight.", length); + return -E_LENGTH_ERROR; + } + int count = length / sizeof(uint64_t); + auto array = reinterpret_cast(bytes); + outSum = 0; + for (int i = 0; i < count; i++) { + outSum ^= array[i]; + } + return E_OK; +} + +int ProtocolProto::CalculateDataSerializeLength(const Message *inMsg, uint32_t &outLength) +{ + uint32_t messageId = inMsg->GetMessageId(); + if (msgIdMapFunc_.count(messageId) == 0) { + LOGE("[Proto][CalcuDataSerialLen] Not registered for messageId=%u.", messageId); + return -E_NOT_REGISTER; + } + + TransformFunc function = msgIdMapFunc_[messageId]; + uint32_t serializeLen = function.computeFunc(inMsg); + uint32_t alignedLen = BYTE_8_ALIGN(serializeLen); + // Currently not allowed the upper module to send a message without data. Regard serializeLen zero as abnormal. + if (serializeLen == 0 || alignedLen > MAX_FRAME_LEN - GetLengthBeforeSerializedData()) { + LOGE("[Proto][CalcuDataSerialLen] Length too large, msgId=%u, serializeLen=%u, alignedLen=%u.", + messageId, serializeLen, alignedLen); + return -E_LENGTH_ERROR; + } + // Attention: return the serializeLen nor the alignedLen. Let SerialBuffer to deal with the padding + outLength = serializeLen; + return E_OK; +} + +int ProtocolProto::SerializeMessage(SerialBuffer *inBuff, const Message *inMsg) +{ + auto payloadByteLen = inBuff->GetWritableBytesForPayload(); + if (payloadByteLen.second < sizeof(MessageHeader)) { // For equal, only msgHeader case + LOGE("[Proto][Serialize] Length error, payload length=%u.", payloadByteLen.second); + return -E_LENGTH_ERROR; + } + uint32_t dataLen = payloadByteLen.second - sizeof(MessageHeader); + + auto messageHdr = reinterpret_cast(payloadByteLen.first); + messageHdr->version = inMsg->GetVersion(); + messageHdr->messageType = inMsg->GetMessageType(); + messageHdr->messageId = inMsg->GetMessageId(); + messageHdr->sessionId = inMsg->GetSessionId(); + messageHdr->sequenceId = inMsg->GetSequenceId(); + messageHdr->errorNo = inMsg->GetErrorNo(); + messageHdr->dataLen = dataLen; + HeaderConverter::ConvertHostToNet(*messageHdr, *messageHdr); + + if (dataLen == 0) { + // For zero dataLen, we don't need to serialize data part + return E_OK; + } + // If dataLen not zero, the TransformFunc of this messageId must exist, the caller's logic guarantee it + uint32_t messageId = inMsg->GetMessageId(); + TransformFunc function = msgIdMapFunc_[messageId]; + int result = function.serializeFunc(payloadByteLen.first + sizeof(MessageHeader), dataLen, inMsg); + if (result != E_OK) { + LOGE("[Proto][Serialize] SerializeFunc Fail, result=%d.", result); + return -E_SERIALIZE_ERROR; + } + return E_OK; +} + +int ProtocolProto::DeSerializeMessage(const SerialBuffer *inBuff, Message *inMsg, bool onlyMsgHeader) +{ + auto payloadByteLen = inBuff->GetReadOnlyBytesForPayload(); + // Check version before parse field + if (payloadByteLen.second < sizeof(uint16_t)) { + return -E_LENGTH_ERROR; + } + uint16_t version = NetToHost(*(reinterpret_cast(payloadByteLen.first))); + if (!IsSupportMessageVersion(version)) { + LOGE("[Proto][DeSerialize] Version=%u not support.", version); + return -E_VERSION_NOT_SUPPORT; + } + + if (payloadByteLen.second < sizeof(MessageHeader)) { + LOGE("[Proto][DeSerialize] Length error, payload length=%u.", payloadByteLen.second); + return -E_LENGTH_ERROR; + } + auto oriMsgHeader = reinterpret_cast(payloadByteLen.first); + MessageHeader messageHdr; + HeaderConverter::ConvertNetToHost(*oriMsgHeader, messageHdr); + inMsg->SetVersion(version); + inMsg->SetMessageType(messageHdr.messageType); + inMsg->SetMessageId(messageHdr.messageId); + inMsg->SetSessionId(messageHdr.sessionId); + inMsg->SetSequenceId(messageHdr.sequenceId); + inMsg->SetErrorNo(messageHdr.errorNo); + uint32_t dataLen = payloadByteLen.second - sizeof(MessageHeader); + if (dataLen != messageHdr.dataLen) { + LOGE("[Proto][DeSerialize] dataLen=%u, msgDataLen=%u.", dataLen, messageHdr.dataLen); + return -E_LENGTH_ERROR; + } + // It is better to check FeedbackMessage first and check onlyMsgHeader flag later + if (IsFeedbackErrorMessage(messageHdr.errorNo)) { + LOGI("[Proto][DeSerialize] Feedback Message with errorNo=%u.", messageHdr.errorNo); + return E_OK; + } + if (onlyMsgHeader || dataLen == 0) { // Do not need to deserialize data + return E_OK; + } + uint32_t messageId = inMsg->GetMessageId(); + if (msgIdMapFunc_.count(messageId) == 0) { + LOGE("[Proto][DeSerialize] Not register, messageId=%u.", messageId); + return -E_NOT_REGISTER; + } + TransformFunc function = msgIdMapFunc_[messageId]; + int result = function.deserializeFunc(payloadByteLen.first + sizeof(MessageHeader), dataLen, inMsg); + if (result != E_OK) { + LOGE("[Proto][DeSerialize] DeserializeFunc Fail, result=%d.", result); + return -E_DESERIALIZE_ERROR; + } + return E_OK; +} + +bool ProtocolProto::IsSupportMessageVersion(uint16_t version) +{ + return (version == MSG_VERSION_BASE || version == MSG_VERSION_EXT); +} + +bool ProtocolProto::IsFeedbackErrorMessage(uint32_t errorNo) +{ + return (errorNo == E_FEEDBACK_UNKNOWN_MESSAGE || errorNo == E_FEEDBACK_COMMUNICATOR_NOT_FOUND); +} + +int ProtocolProto::ParseCommPhyHeaderCheckMagicAndVersion(const uint8_t *bytes, uint32_t length) +{ + // At least magic and version should exist + if (length < sizeof(uint16_t) + sizeof(uint16_t)) { + LOGE("[Proto][ParsePhyCheckVer] Length of Bytes Error."); + return -E_LENGTH_ERROR; + } + auto fieldPtr = reinterpret_cast(bytes); + uint16_t magic = NetToHost(*fieldPtr++); + uint16_t version = NetToHost(*fieldPtr++); + + if (magic != MAGIC_CODE) { + LOGE("[Proto][ParsePhyCheckVer] MagicCode=%u Error.", magic); + return -E_PARSE_FAIL; + } + if (version != PROTOCOL_VERSION) { + LOGE("[Proto][ParsePhyCheckVer] Version=%u Error.", version); + return -E_VERSION_NOT_SUPPORT; + } + return E_OK; +} + +int ProtocolProto::ParseCommPhyHeaderCheckField(const std::string &srcTarget, const CommPhyHeader &phyHeader, + const uint8_t *bytes, uint32_t length) +{ + if (phyHeader.sourceId != Hash::HashFunc(srcTarget)) { + LOGE("[Proto][ParsePhyCheck] SourceId Error: inSourceId=%llu, srcTarget=%s{private}, hashId=%llu.", + ULL(phyHeader.sourceId), srcTarget.c_str(), ULL(Hash::HashFunc(srcTarget))); + return -E_PARSE_FAIL; + } + if (phyHeader.packetLen != length) { + LOGE("[Proto][ParsePhyCheck] PacketLen=%u Mismatch length=%u.", phyHeader.packetLen, length); + return -E_PARSE_FAIL; + } + if (phyHeader.paddingLen > MAX_PADDING_LEN) { + LOGE("[Proto][ParsePhyCheck] PaddingLen=%u Error.", phyHeader.paddingLen); + return -E_PARSE_FAIL; + } + if (sizeof(CommPhyHeader) + phyHeader.paddingLen > phyHeader.packetLen) { + LOGE("[Proto][ParsePhyCheck] PaddingLen Add PhyHeader Greater Than PacketLen."); + return -E_PARSE_FAIL; + } + uint64_t sumResult = 0; + int errCode = CalculateXorSum(bytes + LENGTH_BEFORE_SUM_RANGE, length - LENGTH_BEFORE_SUM_RANGE, sumResult); + if (errCode != E_OK) { + LOGE("[Proto][ParsePhyCheck] Calculate Sum Fail."); + return -E_SUM_CALCULATE_FAIL; + } + if (phyHeader.checkSum != sumResult) { + LOGE("[Proto][ParsePhyCheck] Sum Mismatch, checkSum=%llu, sumResult=%llu.", + ULL(phyHeader.checkSum), ULL(sumResult)); + return -E_SUM_MISMATCH; + } + return E_OK; +} + +int ProtocolProto::ParseCommPhyHeader(const std::string &srcTarget, const uint8_t *bytes, uint32_t length, + ParseResult &inResult) +{ + int errCode = ParseCommPhyHeaderCheckMagicAndVersion(bytes, length); + if (errCode != E_OK) { + LOGE("[Proto][ParsePhy] Check Magic And Version Fail."); + return errCode; + } + + if (length < sizeof(CommPhyHeader)) { + LOGE("[Proto][ParsePhy] Length of Bytes Error."); + return -E_PARSE_FAIL; + } + auto phyHeaderOri = reinterpret_cast(bytes); + CommPhyHeader phyHeader; + HeaderConverter::ConvertNetToHost(*phyHeaderOri, phyHeader); + errCode = ParseCommPhyHeaderCheckField(srcTarget, phyHeader, bytes, length); + if (errCode != E_OK) { + LOGE("[Proto][ParsePhy] Check Field Fail."); + return errCode; + } + + inResult.SetFrameId(phyHeader.frameId); + inResult.SetSourceId(phyHeader.sourceId); + inResult.SetPacketLen(phyHeader.packetLen); + inResult.SetPaddingLen(phyHeader.paddingLen); + inResult.SetDbVersion(phyHeader.dbIntVer); + if ((phyHeader.packetType & PACKET_TYPE_FRAGMENTED) != 0) { + inResult.SetFragmentFlag(true); + } // FragmentFlag default is false + FrameType frameType = GetFrameType(phyHeader.packetType); + if (frameType == FrameType::INVALID_MAX_FRAME_TYPE) { + LOGW("[Proto][ParsePhy] Unrecognized frame, pktType=%u.", phyHeader.packetType); + return -E_FRAME_TYPE_NOT_SUPPORT; + } + inResult.SetFrameTypeInfo(frameType); + return E_OK; +} + +int ProtocolProto::ParseCommPhyOptHeader(const uint8_t *bytes, uint32_t length, ParseResult &inResult) +{ + if (length < sizeof(CommPhyHeader) + sizeof(CommPhyOptHeader)) { + LOGE("[Proto][ParsePhyOpt] Length of Bytes Error."); + return -E_LENGTH_ERROR; + } + auto headerOri = reinterpret_cast(bytes + sizeof(CommPhyHeader)); + CommPhyOptHeader phyOptHeader; + HeaderConverter::ConvertNetToHost(*headerOri, phyOptHeader); + + // Check of CommPhyOptHeader field will be done in the procedure of FrameCombiner + inResult.SetFrameLen(phyOptHeader.frameLen); + inResult.SetFragCount(phyOptHeader.fragCount); + inResult.SetFragNo(phyOptHeader.fragNo); + return E_OK; +} + +int ProtocolProto::ParseCommDivergeHeader(const uint8_t *bytes, uint32_t length, ParseResult &inResult) +{ + // Check version before parse field + if (length < sizeof(CommPhyHeader) + sizeof(uint16_t)) { + return -E_LENGTH_ERROR; + } + uint16_t version = NetToHost(*(reinterpret_cast(bytes + sizeof(CommPhyHeader)))); + if (version != PROTOCOL_VERSION) { + LOGE("[Proto][ParseDiverge] Version=%" PRIu16 " not support.", version); + return -E_VERSION_NOT_SUPPORT; + } + + if (length < sizeof(CommPhyHeader) + sizeof(CommDivergeHeader)) { + LOGE("[Proto][ParseDiverge] Length of Bytes Error."); + return -E_PARSE_FAIL; + } + auto headerOri = reinterpret_cast(bytes + sizeof(CommPhyHeader)); + CommDivergeHeader divergeHeader; + HeaderConverter::ConvertNetToHost(*headerOri, divergeHeader); + if (sizeof(CommPhyHeader) + sizeof(CommDivergeHeader) + divergeHeader.payLoadLen + + inResult.GetPaddingLen() != inResult.GetPacketLen()) { + LOGE("[Proto][ParseDiverge] Total Length Mismatch."); + return -E_PARSE_FAIL; + } + inResult.SetPayloadLen(divergeHeader.payLoadLen); + inResult.SetCommLabel(LabelType(std::begin(divergeHeader.commLabel), std::end(divergeHeader.commLabel))); + return E_OK; +} + +int ProtocolProto::ParseCommLayerPayload(const uint8_t *bytes, uint32_t length, ParseResult &inResult) +{ + if (inResult.GetFrameTypeInfo() == FrameType::COMMUNICATION_LABEL_EXCHANGE_ACK) { + int errCode = ParseLabelExchangeAck(bytes, length, inResult); + if (errCode != E_OK) { + LOGE("[Proto][ParseCommPayload] Total Length Mismatch."); + return errCode; + } + } else { + int errCode = ParseLabelExchange(bytes, length, inResult); + if (errCode != E_OK) { + LOGE("[Proto][ParseCommPayload] Total Length Mismatch."); + return errCode; + } + } + return E_OK; +} + +int ProtocolProto::ParseLabelExchange(const uint8_t *bytes, uint32_t length, ParseResult &inResult) +{ + // Check version at very first + if (length < sizeof(CommPhyHeader) + LABEL_VER_LEN) { + return -E_LENGTH_ERROR; + } + auto fieldPtr = reinterpret_cast(bytes + sizeof(CommPhyHeader)); + uint64_t version = NetToHost(*fieldPtr++); + if (version != PROTOCOL_VERSION) { + LOGE("[Proto][ParseLabel] Version=%llu not support.", ULL(version)); + return -E_VERSION_NOT_SUPPORT; + } + + // Version, DistinctValue, SequenceId and CommLabelCount field must be exist. + if (length < sizeof(CommPhyHeader) + LABEL_VER_LEN + DISTINCT_VALUE_LEN + SEQUENCE_ID_LEN + COMM_LABEL_COUNT_LEN) { + LOGE("[Proto][ParseLabel] Length of Bytes Error."); + return -E_LENGTH_ERROR; + } + uint64_t distinctValue = NetToHost(*fieldPtr++); + inResult.SetLabelExchangeDistinctValue(distinctValue); + uint64_t sequenceId = NetToHost(*fieldPtr++); + inResult.SetLabelExchangeSequenceId(sequenceId); + uint64_t commLabelCount = NetToHost(*fieldPtr++); + if (length < commLabelCount || (UINT32_MAX / COMM_LABEL_LENGTH) < commLabelCount) { + LOGE("[Proto][ParseLabel] commLabelCount=%llu invalid.", ULL(commLabelCount)); + return -E_PARSE_FAIL; + } + // commLabelCount is expected to be not very large + if (length < sizeof(CommPhyHeader) + LABEL_VER_LEN + DISTINCT_VALUE_LEN + SEQUENCE_ID_LEN + COMM_LABEL_COUNT_LEN + + commLabelCount * COMM_LABEL_LENGTH) { + LOGE("[Proto][ParseLabel] Length of Bytes Error, commLabelCount=%llu", ULL(commLabelCount)); + return -E_LENGTH_ERROR; + } + + // Get each commLabel + std::set commLabels; + auto bytePtr = reinterpret_cast(fieldPtr); + for (uint64_t i = 0; i < commLabelCount; i++) { + // the length is checked just above + LabelType commLabel(bytePtr + i * COMM_LABEL_LENGTH, bytePtr + (i + 1) * COMM_LABEL_LENGTH); + if (commLabels.count(commLabel) != 0) { + LOGW("[Proto][ParseLabel] Duplicate Label Detected, commLabel=%s.", VEC_TO_STR(commLabel)); + } else { + commLabels.insert(commLabel); + } + } + inResult.SetLatestCommLabels(commLabels); + return E_OK; +} + +int ProtocolProto::ParseLabelExchangeAck(const uint8_t *bytes, uint32_t length, ParseResult &inResult) +{ + // Check version at very first + if (length < sizeof(CommPhyHeader) + LABEL_VER_LEN) { + return -E_LENGTH_ERROR; + } + auto fieldPtr = reinterpret_cast(bytes + sizeof(CommPhyHeader)); + uint64_t version = NetToHost(*fieldPtr++); + if (version != PROTOCOL_VERSION) { + LOGE("[Proto][ParseLabelAck] Version=%llu not support.", ULL(version)); + return -E_VERSION_NOT_SUPPORT; + } + + if (length < sizeof(CommPhyHeader) + LABEL_VER_LEN + DISTINCT_VALUE_LEN + SEQUENCE_ID_LEN) { + LOGE("[Proto][ParseLabelAck] Length of Bytes Error."); + return -E_LENGTH_ERROR; + } + uint64_t distinctValue = NetToHost(*fieldPtr++); + inResult.SetLabelExchangeDistinctValue(distinctValue); + uint64_t sequenceId = NetToHost(*fieldPtr++); + inResult.SetLabelExchangeSequenceId(sequenceId); + return E_OK; +} + +// Note: framePhyHeader is in network endian +// This function aims at calculating and preparing each part of each packets +int ProtocolProto::FrameFragmentation(const uint8_t *splitStartBytes, const FrameFragmentInfo &fragmentInfo, + const CommPhyHeader &framePhyHeader, std::vector, uint32_t>> &outPieces) +{ + // It can be guaranteed that fragCount >= 2 and also won't be too large + if (fragmentInfo.fragCount < MIN_FRAGMENT_COUNT) { + return -E_INVALID_ARGS; + } + outPieces.resize(fragmentInfo.fragCount); // Note: should use resize other than reserve + uint32_t quotient = fragmentInfo.splitLength / fragmentInfo.fragCount; + uint16_t remainder = fragmentInfo.splitLength % fragmentInfo.fragCount; + uint16_t fragNo = 0; // Fragment index start from 0 + uint32_t byteOffset = 0; + + for (auto &entry : outPieces) { + // subtract 1 for index + uint32_t pieceFragLen = (fragNo != fragmentInfo.fragCount - 1) ? quotient : (quotient + remainder); + uint32_t alignedFragLen = BYTE_8_ALIGN(pieceFragLen); // Add padding length + uint32_t pieceTotalLen = alignedFragLen + sizeof(CommPhyHeader) + sizeof(CommPhyOptHeader); + + // Since exception is disabled, we have to check the vector size to assure that memory is truly allocated + entry.first.resize(pieceTotalLen + fragmentInfo.extendHeadSize); // Note: should use resize other than reserve + if (entry.first.size() != (pieceTotalLen + fragmentInfo.extendHeadSize)) { + LOGE("[Proto][FrameFrag] Resize failed for length=%u", pieceTotalLen); + return -E_OUT_OF_MEMORY; + } + + CommPhyHeader pktPhyHeader; + HeaderConverter::ConvertNetToHost(framePhyHeader, pktPhyHeader); // Restore to host endian + + // The sum value need to be recalculated, and the packet is fragmented. + // The alignedFragLen is always larger than pieceFragLen + FillPhyHeaderLenInfo(pktPhyHeader, pieceTotalLen, 0, PACKET_TYPE_FRAGMENTED, alignedFragLen - pieceFragLen); + HeaderConverter::ConvertHostToNet(pktPhyHeader, pktPhyHeader); + + CommPhyOptHeader pktPhyOptHeader = {static_cast(fragmentInfo.splitLength + sizeof(CommPhyHeader)), + fragmentInfo.fragCount, fragNo}; + HeaderConverter::ConvertHostToNet(pktPhyOptHeader, pktPhyOptHeader); + int err; + FragmentPacket packet; + uint8_t *ptrPacket = &(entry.first[0]); + if (fragmentInfo.extendHeadSize > 0) { + packet = {ptrPacket, fragmentInfo.extendHeadSize}; + err = FillFragmentPacketExtendHead(fragmentInfo.oringinalBytesAddr, fragmentInfo.extendHeadSize, packet); + if (err != E_OK) { + return err; + } + ptrPacket += fragmentInfo.extendHeadSize; + } + packet = {ptrPacket, static_cast(entry.first.size()) - fragmentInfo.extendHeadSize}; + err = FillFragmentPacket(pktPhyHeader, pktPhyOptHeader, splitStartBytes + byteOffset, + pieceFragLen, packet); + entry.second = fragmentInfo.extendHeadSize; + if (err != E_OK) { + LOGE("[Proto][FrameFrag] Fill packet fail, fragCount=%" PRIu16 ", fragNo=%" PRIu16, fragmentInfo.fragCount, + fragNo); + return err; + } + + fragNo++; + byteOffset += pieceFragLen; + } + + return E_OK; +} + +int ProtocolProto::FillFragmentPacketExtendHead(uint8_t *headBytesAddr, uint32_t headLen, FragmentPacket &outPacket) +{ + if (headLen > outPacket.leftLength) { + LOGE("[Proto][FrameFrag] headLen less than leftLength"); + return -E_INVALID_ARGS; + } + errno_t retCode = memcpy_s(outPacket.ptrPacket, outPacket.leftLength, headBytesAddr, headLen); + if (retCode != EOK) { + LOGE("memcpy error:%d", retCode); + return -E_SECUREC_ERROR; + } + return E_OK; +} + +// Note: phyHeader and phyOptHeader is in network endian +int ProtocolProto::FillFragmentPacket(const CommPhyHeader &phyHeader, const CommPhyOptHeader &phyOptHeader, + const uint8_t *fragBytes, uint32_t fragLen, FragmentPacket &outPacket) +{ + if (outPacket.leftLength == 0) { + return -E_INVALID_ARGS; + } + uint8_t *ptrPacket = outPacket.ptrPacket; + uint32_t leftLength = outPacket.leftLength; + + // leftLength is guaranteed to be no smaller than the sum of phyHeaderLen + phyOptHeaderLen + fragLen + // So, there will be no redundant check during subtraction + errno_t retCode = memcpy_s(ptrPacket, leftLength, &phyHeader, sizeof(CommPhyHeader)); + if (retCode != EOK) { + return -E_SECUREC_ERROR; + } + ptrPacket += sizeof(CommPhyHeader); + leftLength -= sizeof(CommPhyHeader); + + retCode = memcpy_s(ptrPacket, leftLength, &phyOptHeader, sizeof(CommPhyOptHeader)); + if (retCode != EOK) { + return -E_SECUREC_ERROR; + } + ptrPacket += sizeof(CommPhyOptHeader); + leftLength -= sizeof(CommPhyOptHeader); + + retCode = memcpy_s(ptrPacket, leftLength, fragBytes, fragLen); + if (retCode != EOK) { + return -E_SECUREC_ERROR; + } + + // Calculate sum and set sum field + uint64_t sumResult = 0; + int errCode = CalculateXorSum(outPacket.ptrPacket + LENGTH_BEFORE_SUM_RANGE, + outPacket.leftLength - LENGTH_BEFORE_SUM_RANGE, sumResult); + if (errCode != E_OK) { + return -E_SUM_CALCULATE_FAIL; + } + auto ptrPhyHeader = reinterpret_cast(outPacket.ptrPacket); + if (ptrPhyHeader == nullptr) { + return -E_INVALID_ARGS; + } + ptrPhyHeader->checkSum = HostToNet(sumResult); + + return E_OK; +} + +int ProtocolProto::GetExtendHeadDataSize(std::shared_ptr &extendHandle, uint32_t &headSize) +{ + if (extendHandle != nullptr) { + DBStatus status = extendHandle->GetHeadDataSize(headSize); + if (status != DBStatus::OK) { + LOGI("[Proto][ToSerial] get head data size failed,not permit to send"); + return -E_FEEDBACK_COMMUNICATOR_NOT_FOUND; + } + if (headSize > SerialBuffer::MAX_EXTEND_HEAD_LENGTH || headSize != BYTE_8_ALIGN(headSize)) { + LOGI("[Proto][ToSerial] head data size is larger than 512 or not 8 byte align"); + return -E_FEEDBACK_COMMUNICATOR_NOT_FOUND; + } + return E_OK; + } + return E_OK; +} + +int ProtocolProto::FillExtendHeadDataIfNeed(std::shared_ptr &extendHandle, SerialBuffer *buffer, + uint32_t headSize) +{ + if (extendHandle != nullptr && headSize > 0) { + if (buffer == nullptr) { + return -E_INVALID_ARGS; + } + DBStatus status = extendHandle->FillHeadData(buffer->GetOringinalAddr(), headSize, + buffer->GetSize() + headSize); + if (status != DBStatus::OK) { + LOGI("[Proto][ToSerial] fill head data failed"); + return -E_FEEDBACK_COMMUNICATOR_NOT_FOUND; + } + return E_OK; + } + return E_OK; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/communicator/src/protocol_proto.h b/mock/distributeddb/communicator/src/protocol_proto.h new file mode 100644 index 0000000000000000000000000000000000000000..64ce126c1df57619749ab24e02457941731bc365 --- /dev/null +++ b/mock/distributeddb/communicator/src/protocol_proto.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PROTOCOLPROTO_H +#define PROTOCOLPROTO_H + +#include +#include +#include "message.h" +#include "frame_header.h" +#include "parse_result.h" +#include "serial_buffer.h" +#include "message_transform.h" +#include "communicator_type_define.h" +#include "iprocess_communicator.h" + +namespace DistributedDB { +struct PhyHeaderInfo { + uint64_t sourceId; + uint32_t frameId; + FrameType frameType; +}; + +struct FrameFragmentInfo { + uint8_t *oringinalBytesAddr; + uint32_t extendHeadSize; + uint32_t splitLength; + uint16_t fragCount; +}; + +struct FragmentPacket { + uint8_t *ptrPacket; + uint32_t leftLength; +}; + +class ProtocolProto { +public: + // For application layer frame + static uint32_t GetAppLayerFrameHeaderLength(); + static uint32_t GetLengthBeforeSerializedData(); + + // For communication layer frame + static uint32_t GetCommLayerFrameHeaderLength(); + + // For handling application layer message. Return a heap object. + static SerialBuffer *ToSerialBuffer(const Message *inMsg, int &outErrorNo, + std::shared_ptr &extendHandle, bool onlyMsgHeader = false); + static Message *ToMessage(const SerialBuffer *inBuff, int &outErrorNo, bool onlyMsgHeader = false); + + // For handling communication layer frame. Return a heap object. + static SerialBuffer *BuildEmptyFrameForVersionNegotiate(int &outErrorNo); + static SerialBuffer *BuildFeedbackMessageFrame(const Message *inMsg, const LabelType &inLabel, int &outErrorNo); + static SerialBuffer *BuildLabelExchange(uint64_t inDistinctValue, uint64_t inSequenceId, + const std::set &inLabels, int &outErrorNo); + static SerialBuffer *BuildLabelExchangeAck(uint64_t inDistinctValue, uint64_t inSequenceId, int &outErrorNo); + + // Return E_OK if no error happened. outPieces.size equal zero means not split, in this case, use ori buff. + static int SplitFrameIntoPacketsIfNeed(const SerialBuffer *inBuff, uint32_t inMtuSize, + std::vector, uint32_t>> &outPieces); + static int AnalyzeSplitStructure(const ParseResult &inResult, uint32_t &outFragLen, uint32_t &outLastFragLen); + + // inFrame is the destination, pktBytes and pktLength are the source, fragOffset and fragLength give the boundary + static int CombinePacketIntoFrame(SerialBuffer *inFrame, const uint8_t *pktBytes, uint32_t pktLength, + uint32_t fragOffset, uint32_t fragLength); + + // Must not be called in multi-thread + // Return E_ALREADY_REGISTER if msgId is already registered + // Return E_INVALID_ARGS if member of inFunc not all valid + static int RegTransformFunction(uint32_t msgId, const TransformFunc &inFunc); + + static void UnRegTransformFunction(uint32_t msgId); + + // For application layer frame. In send case. Focus on frame. + static int SetDivergeHeader(SerialBuffer *inBuff, const LabelType &inCommLabel); + + // For both application and communication layer frame. In send case. Focus on frame. + static int SetPhyHeader(SerialBuffer *inBuff, const PhyHeaderInfo &inInfo); + + // In receive case, return error if parse fail. + static int CheckAndParsePacket(const std::string &srcTarget, const uint8_t *bytes, uint32_t length, + ParseResult &outResult); + + // The CommPhyHeader had already been parsed into outResult + static int CheckAndParseFrame(const SerialBuffer *inBuff, ParseResult &outResult); + + // Dfx method for helping debugging + static void DisplayPacketInformation(const uint8_t *bytes, uint32_t length); + + ProtocolProto() = delete; + ~ProtocolProto() = delete; +private: + static int CalculateXorSum(const uint8_t *bytes, uint32_t length, uint64_t &outSum); + + // For handling application layer message + static int CalculateDataSerializeLength(const Message *inMsg, uint32_t &outLength); + static int SerializeMessage(SerialBuffer *inBuff, const Message *inMsg); + static int DeSerializeMessage(const SerialBuffer *inBuff, Message *inMsg, bool onlyMsgHeader); + static bool IsSupportMessageVersion(uint16_t version); + static bool IsFeedbackErrorMessage(uint32_t errorNo); + + static int ParseCommPhyHeader(const std::string &srcTarget, const uint8_t *bytes, uint32_t length, + ParseResult &inResult); + static int ParseCommPhyHeaderCheckMagicAndVersion(const uint8_t *bytes, uint32_t length); + static int ParseCommPhyHeaderCheckField(const std::string &srcTarget, const CommPhyHeader &phyHeader, + const uint8_t *bytes, uint32_t length); + static int ParseCommPhyOptHeader(const uint8_t *bytes, uint32_t length, ParseResult &inResult); + static int ParseCommDivergeHeader(const uint8_t *bytes, uint32_t length, ParseResult &inResult); + static int ParseCommLayerPayload(const uint8_t *bytes, uint32_t length, ParseResult &inResult); + static int ParseLabelExchange(const uint8_t *bytes, uint32_t length, ParseResult &inResult); + static int ParseLabelExchangeAck(const uint8_t *bytes, uint32_t length, ParseResult &inResult); + + static int FrameFragmentation(const uint8_t *splitStartBytes, const FrameFragmentInfo &fragmentInfo, + const CommPhyHeader &framePhyHeader, std::vector, uint32_t>> &outPieces); + static int FillFragmentPacket(const CommPhyHeader &phyHeader, const CommPhyOptHeader &phyOptHeader, + const uint8_t *fragBytes, uint32_t fragLen, FragmentPacket &outPacket); + static int FillFragmentPacketExtendHead(uint8_t *headBytesAddr, uint32_t headLen, FragmentPacket &outPacket); + static int GetExtendHeadDataSize(std::shared_ptr &extendHandle, uint32_t &headSize); + static int FillExtendHeadDataIfNeed(std::shared_ptr &extendHandle, SerialBuffer *buffer, + uint32_t headSize); + + static std::map msgIdMapFunc_; +}; +} // namespace DistributedDB + +#endif // PROTOCOLPROTO_H diff --git a/mock/distributeddb/communicator/src/send_task_scheduler.cpp b/mock/distributeddb/communicator/src/send_task_scheduler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25a60561e99502bc400d0c15b4cf03601d4a46f8 --- /dev/null +++ b/mock/distributeddb/communicator/src/send_task_scheduler.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "send_task_scheduler.h" +#include +#include "db_errno.h" +#include "log_print.h" +#include "serial_buffer.h" + +namespace DistributedDB { +// In current parameters, the scheduler will hold 160 MB in extreme situation. +// In actual runtime situation, the scheduler will hold no more than 100 MB. +static constexpr uint32_t MAX_CAPACITY = 67108864; // 64 M bytes +static constexpr uint32_t EXTRA_CAPACITY_FOR_NORMAL_PRIORITY = 33554432; // 32 M bytes +static constexpr uint32_t EXTRA_CAPACITY_FOR_HIGH_PRIORITY = 67108864; // 64 M bytes + +SendTaskScheduler::~SendTaskScheduler() +{ + Finalize(); +} + +void SendTaskScheduler::Initialize() +{ + priorityOrder_.clear(); + priorityOrder_.push_back(Priority::HIGH); + priorityOrder_.push_back(Priority::NORMAL); + priorityOrder_.push_back(Priority::LOW); + for (auto &prio : priorityOrder_) { + extraCapacityInByteByPrio_[prio] = 0; + taskCountByPrio_[prio] = 0; + taskDelayCountByPrio_[prio] = 0; + taskGroupByPrio_[prio] = TaskListByTarget(); + } + extraCapacityInByteByPrio_[Priority::NORMAL] = EXTRA_CAPACITY_FOR_NORMAL_PRIORITY; + extraCapacityInByteByPrio_[Priority::HIGH] = EXTRA_CAPACITY_FOR_HIGH_PRIORITY; +} + +void SendTaskScheduler::Finalize() +{ + while (GetTotalTaskCount() != 0) { + SendTask task; + SendTaskInfo taskInfo; + int errCode = ScheduleOutSendTask(task, taskInfo); + if (errCode != E_OK) { + LOGE("[Scheduler][Final] INTERNAL ERROR."); + break; // Not possible to happen + } + LOGW("[Scheduler][Finalize] dstTarget=%s{private}, delayFlag=%d, taskPrio=%d", task.dstTarget.c_str(), + taskInfo.delayFlag, static_cast(taskInfo.taskPrio)); + FinalizeLastScheduleTask(); + } +} + +int SendTaskScheduler::AddSendTaskIntoSchedule(const SendTask &inTask, Priority inPrio) +{ + std::lock_guard overallLockGuard(overallMutex_); + if (curTotalSizeByByte_ >= MAX_CAPACITY + extraCapacityInByteByPrio_[inPrio]) { + return -E_CONTAINER_FULL; + } + + uint32_t taskSizeByByte = inTask.buffer->GetSize(); + curTotalSizeByByte_ += taskSizeByByte; + curTotalSizeByTask_++; + if (policyMap_.count(inTask.dstTarget) == 0) { + policyMap_[inTask.dstTarget] = TargetPolicy::NO_DELAY; + } + if (policyMap_[inTask.dstTarget] == TargetPolicy::DELAY) { + delayTaskCount_++; + taskDelayCountByPrio_[inPrio]++; + } + + taskCountByPrio_[inPrio]++; + taskOrderByPrio_[inPrio].push_back(inTask.dstTarget); + taskGroupByPrio_[inPrio][inTask.dstTarget].push_back(inTask); + return E_OK; +} + +int SendTaskScheduler::ScheduleOutSendTask(SendTask &outTask) +{ + SendTaskInfo taskInfo; + int errCode = ScheduleOutSendTask(outTask, taskInfo); + if (errCode == E_OK) { + LOGI("[Scheduler][OutTask] dstTarget=%s{private}, delayFlag=%d, taskPrio=%d", outTask.dstTarget.c_str(), + taskInfo.delayFlag, static_cast(taskInfo.taskPrio)); + } + return errCode; +} + +int SendTaskScheduler::ScheduleOutSendTask(SendTask &outTask, SendTaskInfo &outTaskInfo) +{ + std::lock_guard overallLockGuard(overallMutex_); + if (curTotalSizeByTask_ == 0) { + return -E_CONTAINER_EMPTY; + } + + if (delayTaskCount_ == curTotalSizeByTask_) { + // Tasks are all in delay status + int errCode = ScheduleDelayTask(outTask, outTaskInfo); + if (errCode == E_OK) { + // Update last schedule location + lastScheduleTarget_ = outTask.dstTarget; + lastSchedulePriority_ = outTaskInfo.taskPrio; + scheduledFlag_ = true; + } + return errCode; + } else { + // There are some tasks not in delay status + int errCode = ScheduleNoDelayTask(outTask, outTaskInfo); + if (errCode == E_OK) { + // Update last schedule location + lastScheduleTarget_ = outTask.dstTarget; + lastSchedulePriority_ = outTaskInfo.taskPrio; + scheduledFlag_ = true; + } + return errCode; + } +} + +int SendTaskScheduler::FinalizeLastScheduleTask() +{ + std::lock_guard overallLockGuard(overallMutex_); + if (curTotalSizeByTask_ == 0) { + return -E_CONTAINER_EMPTY; + } + if (!scheduledFlag_) { + return -E_NOT_PERMIT; + } + + // Retrieve last scheduled task + SendTask task = taskGroupByPrio_[lastSchedulePriority_][lastScheduleTarget_].front(); + + bool isFullBefore = (curTotalSizeByByte_ >= MAX_CAPACITY); + uint32_t taskSize = task.buffer->GetSize(); + curTotalSizeByByte_ -= taskSize; + bool isFullAfter = (curTotalSizeByByte_ >= MAX_CAPACITY); + + curTotalSizeByTask_--; + taskCountByPrio_[lastSchedulePriority_]--; + if (policyMap_[lastScheduleTarget_] == TargetPolicy::DELAY) { + delayTaskCount_--; + taskDelayCountByPrio_[lastSchedulePriority_]--; + } + + for (auto iter = taskOrderByPrio_[lastSchedulePriority_].begin(); + iter != taskOrderByPrio_[lastSchedulePriority_].end(); ++iter) { + if (*iter == lastScheduleTarget_) { + taskOrderByPrio_[lastSchedulePriority_].erase(iter); + break; + } + } + + taskGroupByPrio_[lastSchedulePriority_][lastScheduleTarget_].pop_front(); + delete task.buffer; + task.buffer = nullptr; + scheduledFlag_ = false; + + if (isFullBefore && !isFullAfter) { + return -E_CONTAINER_FULL_TO_NOTFULL; + } + if (curTotalSizeByTask_ == 0) { + return -E_CONTAINER_NOTEMPTY_TO_EMPTY; + } + if (curTotalSizeByTask_ == delayTaskCount_) { + return -E_CONTAINER_ONLY_DELAY_TASK; + } + + return E_OK; +} + +int SendTaskScheduler::DelayTaskByTarget(const std::string &inTarget) +{ + std::lock_guard overallLockGuard(overallMutex_); + if (policyMap_.count(inTarget) == 0) { + LOGE("[Scheduler][DelayTask] Not found inTarget=%s{private}", inTarget.c_str()); + return -E_NOT_FOUND; + } + if (policyMap_[inTarget] == TargetPolicy::DELAY) { + return E_OK; + } + + policyMap_[inTarget] = TargetPolicy::DELAY; + for (auto &prio : priorityOrder_) { + size_t count = taskGroupByPrio_[prio][inTarget].size(); + taskDelayCountByPrio_[prio] += static_cast(count); + delayTaskCount_ += static_cast(count); + } + return E_OK; +} + +int SendTaskScheduler::NoDelayTaskByTarget(const std::string &inTarget) +{ + std::lock_guard overallLockGuard(overallMutex_); + if (policyMap_.count(inTarget) == 0) { + LOGE("[Scheduler][NoDelayTask] Not found inTarget=%s{private}", inTarget.c_str()); + return -E_NOT_FOUND; + } + if (policyMap_[inTarget] == TargetPolicy::NO_DELAY) { + return E_OK; + } + + policyMap_[inTarget] = TargetPolicy::NO_DELAY; + for (auto &prio : priorityOrder_) { + size_t count = taskGroupByPrio_[prio][inTarget].size(); + // Logic guarantee that former not smaller than latter + taskDelayCountByPrio_[prio] -= static_cast(count); + delayTaskCount_ -= static_cast(count); + } + return E_OK; +} + +uint32_t SendTaskScheduler::GetTotalTaskCount() const +{ + std::lock_guard overallLockGuard(overallMutex_); + return curTotalSizeByTask_; +} + +uint32_t SendTaskScheduler::GetNoDelayTaskCount() const +{ + std::lock_guard overallLockGuard(overallMutex_); + // delayTaskCount_ never greater than curTotalSizeByTask_ + return curTotalSizeByTask_ - delayTaskCount_; +} + +int SendTaskScheduler::ScheduleDelayTask(SendTask &outTask, SendTaskInfo &outTaskInfo) +{ + for (auto &prio : priorityOrder_) { + if (taskCountByPrio_[prio] == 0) { + // No task of this priority + continue; + } + // Logic guarantee that lists access below will not be empty + std::string dstTarget = taskOrderByPrio_[prio].front(); + outTask = taskGroupByPrio_[prio][dstTarget].front(); + outTaskInfo.delayFlag = true; + outTaskInfo.taskPrio = prio; + return E_OK; + } + LOGE("[Scheduler][ScheduleDelay] INTERNAL ERROR : NO TASK."); + return -E_INTERNAL_ERROR; +} + +int SendTaskScheduler::ScheduleNoDelayTask(SendTask &outTask, SendTaskInfo &outTaskInfo) +{ + for (auto &prio : priorityOrder_) { + if (taskCountByPrio_[prio] == 0 || taskCountByPrio_[prio] == taskDelayCountByPrio_[prio]) { + // No no_delay_task of this priority + continue; + } + // Logic guarantee that lists accessed below will not be empty + std::string dstTarget; + bool findFlag = false; // Not necessary in fact + for (auto iter = taskOrderByPrio_[prio].begin(); iter != taskOrderByPrio_[prio].end(); ++iter) { + // Logic guarantee that there is at least one target in orderList that is NO_DELAY + dstTarget = *iter; + if (policyMap_[dstTarget] == TargetPolicy::NO_DELAY) { + findFlag = true; + break; + } + } + if (!findFlag) { + LOGE("[Scheduler][ScheduleNoDelay] INTERNAL ERROR : NO_DELAY NOT FOUND."); + return -E_INTERNAL_ERROR; + } + + outTask = taskGroupByPrio_[prio][dstTarget].front(); + outTaskInfo.delayFlag = false; + outTaskInfo.taskPrio = prio; + return E_OK; + } + LOGE("[Scheduler][ScheduleNoDelay] INTERNAL ERROR : NO TASK."); + return -E_INTERNAL_ERROR; +} +} diff --git a/mock/distributeddb/communicator/src/serial_buffer.cpp b/mock/distributeddb/communicator/src/serial_buffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4fbcd89af9f8c34fb130c228cbfab78a5d9bc3f8 --- /dev/null +++ b/mock/distributeddb/communicator/src/serial_buffer.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "serial_buffer.h" +#include +#include "securec.h" +#include "db_errno.h" +#include "communicator_type_define.h" +#include "log_print.h" + +namespace DistributedDB { +SerialBuffer::~SerialBuffer() +{ + if (!isExternalStackMemory_ && oringinalBytes_ != nullptr) { + delete[] oringinalBytes_; + } + oringinalBytes_ = nullptr; + bytes_ = nullptr; + externalBytes_ = nullptr; +} + +void SerialBuffer::SetExtendHeadLength(uint32_t extendHeaderLen) +{ + extendHeadLen_ = extendHeaderLen; +} + +uint32_t SerialBuffer::GetExtendHeadLength() const +{ + return extendHeadLen_; +} + +// In case buffer be directly send out, so padding is needed +int SerialBuffer::AllocBufferByPayloadLength(uint32_t inPayloadLen, uint32_t inHeaderLen) +{ + if (oringinalBytes_ != nullptr || bytes_ != nullptr || externalBytes_ != nullptr) { + return -E_NOT_PERMIT; + } + + payloadLen_ = inPayloadLen; + headerLen_ = inHeaderLen; + totalLen_ = BYTE_8_ALIGN(payloadLen_ + headerLen_); + paddingLen_ = totalLen_ - payloadLen_ - headerLen_; + if (totalLen_ == 0 || totalLen_ > MAX_TOTAL_LEN) { + return -E_INVALID_ARGS; + } + oringinalBytes_ = new (std::nothrow) uint8_t[totalLen_ + extendHeadLen_]; + if (oringinalBytes_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + bytes_ = oringinalBytes_ + extendHeadLen_; + return E_OK; +} + +// In case assemble fragment to frame, so no padding is needed, using frameLen as inTotalLen +int SerialBuffer::AllocBufferByTotalLength(uint32_t inTotalLen, uint32_t inHeaderLen) +{ + if (bytes_ != nullptr || externalBytes_ != nullptr) { + return -E_NOT_PERMIT; + } + if (inTotalLen == 0 || inTotalLen > MAX_TOTAL_LEN || inTotalLen < inHeaderLen) { + return -E_INVALID_ARGS; + } + + totalLen_ = inTotalLen; + headerLen_ = inHeaderLen; + payloadLen_ = totalLen_ - headerLen_; + paddingLen_ = 0; + bytes_ = new (std::nothrow) uint8_t[inTotalLen]; + if (bytes_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + oringinalBytes_ = bytes_; + return E_OK; +} + +// In case directly received, inTotalLen not include the padding, using frameLen as inTotalLen +int SerialBuffer::SetExternalBuff(const uint8_t *buff, uint32_t inTotalLen, uint32_t inHeaderLen) +{ + if (bytes_ != nullptr || externalBytes_ != nullptr) { + return -E_NOT_PERMIT; + } + if (buff == nullptr || inTotalLen == 0 || inTotalLen > MAX_TOTAL_LEN || inTotalLen < inHeaderLen) { + return -E_INVALID_ARGS; + } + + totalLen_ = inTotalLen; + headerLen_ = inHeaderLen; + payloadLen_ = totalLen_ - headerLen_; + paddingLen_ = 0; + isExternalStackMemory_ = true; + externalBytes_ = buff; + return E_OK; +} + +SerialBuffer *SerialBuffer::Clone(int &outErrorNo) +{ + SerialBuffer *twinBuffer = new (std::nothrow) SerialBuffer(); + if (twinBuffer == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + if (bytes_ == nullptr) { + twinBuffer->bytes_ = nullptr; + } else { + twinBuffer->bytes_ = new (std::nothrow) uint8_t[totalLen_]; + if (twinBuffer->bytes_ == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + delete twinBuffer; + twinBuffer = nullptr; + return nullptr; + } + errno_t errCode = memcpy_s(twinBuffer->bytes_, totalLen_, bytes_, totalLen_); + if (errCode != EOK) { + outErrorNo = -E_SECUREC_ERROR; + delete twinBuffer; + twinBuffer = nullptr; + return nullptr; + } + } + twinBuffer->oringinalBytes_ = twinBuffer->bytes_; + twinBuffer->externalBytes_ = externalBytes_; + twinBuffer->totalLen_ = totalLen_; + twinBuffer->headerLen_ = headerLen_; + twinBuffer->payloadLen_ = payloadLen_; + twinBuffer->paddingLen_ = paddingLen_; + twinBuffer->isExternalStackMemory_ = isExternalStackMemory_; + twinBuffer->extendHeadLen_ = extendHeadLen_; + outErrorNo = E_OK; + return twinBuffer; +} + +int SerialBuffer::ConvertForCrossThread() +{ + if (externalBytes_ == nullptr) { + // No associated external stack memory. Do nothing and return E_OK. + return E_OK; + } + // Logic guarantee all the member value: isExternalStackMemory_ is true; bytes_ is nullptr; totalLen_ is correct. + bytes_ = new (std::nothrow) uint8_t[totalLen_]; + if (bytes_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + errno_t errCode = memcpy_s(bytes_, totalLen_, externalBytes_, totalLen_); + if (errCode != EOK) { + delete[] bytes_; + bytes_ = nullptr; + return -E_SECUREC_ERROR; + } + // Reset external related info + externalBytes_ = nullptr; + isExternalStackMemory_ = false; + oringinalBytes_ = bytes_; + extendHeadLen_ = 0; + return E_OK; +} + +uint32_t SerialBuffer::GetSize() const +{ + if (bytes_ == nullptr && externalBytes_ == nullptr) { + return 0; + } + return totalLen_; +} + +uint8_t *SerialBuffer::GetOringinalAddr() const +{ + return oringinalBytes_; +} + +std::pair SerialBuffer::GetWritableBytesForEntireBuffer() +{ + if (bytes_ == nullptr) { + return std::make_pair(nullptr, 0); + } else { + return std::make_pair(bytes_, totalLen_); + } +} + +std::pair SerialBuffer::GetWritableBytesForEntireFrame() +{ + if (bytes_ == nullptr) { + return std::make_pair(nullptr, 0); + } else { + return std::make_pair(bytes_, totalLen_ - paddingLen_); + } +} + +std::pair SerialBuffer::GetWritableBytesForHeader() +{ + if (bytes_ == nullptr) { + return std::make_pair(nullptr, 0); + } else { + return std::make_pair(bytes_, headerLen_); + } +} + +std::pair SerialBuffer::GetWritableBytesForPayload() +{ + if (bytes_ == nullptr) { + return std::make_pair(nullptr, 0); + } else { + return std::make_pair(bytes_ + headerLen_, payloadLen_); + } +} + +// For receive case, using Const Function +std::pair SerialBuffer::GetReadOnlyBytesForEntireBuffer() const +{ + if (isExternalStackMemory_) { + return std::make_pair(externalBytes_, totalLen_); + } else if (bytes_ != nullptr) { + return std::make_pair(bytes_, totalLen_); + } else { + return std::make_pair(nullptr, 0); + } +} + +std::pair SerialBuffer::GetReadOnlyBytesForEntireFrame() const +{ + if (isExternalStackMemory_) { + return std::make_pair(externalBytes_, totalLen_ - paddingLen_); + } else if (bytes_ != nullptr) { + return std::make_pair(bytes_, totalLen_ - paddingLen_); + } else { + return std::make_pair(nullptr, 0); + } +} + +std::pair SerialBuffer::GetReadOnlyBytesForHeader() const +{ + if (isExternalStackMemory_) { + return std::make_pair(externalBytes_, headerLen_); + } else if (bytes_ != nullptr) { + return std::make_pair(bytes_, headerLen_); + } else { + return std::make_pair(nullptr, 0); + } +} + +std::pair SerialBuffer::GetReadOnlyBytesForPayload() const +{ + if (isExternalStackMemory_) { + return std::make_pair(externalBytes_ + headerLen_, payloadLen_); + } else if (bytes_ != nullptr) { + return std::make_pair(bytes_ + headerLen_, payloadLen_); + } else { + return std::make_pair(nullptr, 0); + } +} +} // namespace DistributedDB diff --git a/mock/distributeddb/communicator/src/serial_buffer.h b/mock/distributeddb/communicator/src/serial_buffer.h new file mode 100644 index 0000000000000000000000000000000000000000..01fb39a62dcb1115fe8b1261cdb6cf16dcfad64b --- /dev/null +++ b/mock/distributeddb/communicator/src/serial_buffer.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SERIALBUFFER_H +#define SERIALBUFFER_H + +#include +#include +#include +#include "macro_utils.h" + +namespace DistributedDB { +class SerialBuffer { +public: + SerialBuffer() = default; // Default constructor must be explicitly provided due to DISABLE_COPY_ASSIGN_MOVE + ~SerialBuffer(); + + DISABLE_COPY_ASSIGN_MOVE(SerialBuffer); + + // will call the func before alloc buff, calculate the head len which is used for data service + void SetExtendHeadLength(uint32_t extendHeaderLen); + uint32_t GetExtendHeadLength() const; + // May be directly send out, so padding is needed + int AllocBufferByPayloadLength(uint32_t inPayloadLen, uint32_t inHeaderLen); + + // In case assemble fragment to frame, so no padding is needed, using frameLen as inTotalLen + int AllocBufferByTotalLength(uint32_t inTotalLen, uint32_t inHeaderLen); + + // In case directly received, inTotalLen not include the padding, using frameLen as inTotalLen + int SetExternalBuff(const uint8_t *buff, uint32_t inTotalLen, uint32_t inHeaderLen); + + // Create a SerialBuffer that has a independent bytes_ and point to the same externalBytes_ + SerialBuffer *Clone(int &outErrorNo); + + // After return E_OK, this SerialBuffer can cross thread. Do nothing indeed if it already able to cross thread. + int ConvertForCrossThread(); + + uint32_t GetSize() const; + + uint8_t *GetOringinalAddr() const; + + std::pair GetWritableBytesForEntireBuffer(); + std::pair GetWritableBytesForEntireFrame(); + std::pair GetWritableBytesForHeader(); + std::pair GetWritableBytesForPayload(); + + std::pair GetReadOnlyBytesForEntireBuffer() const; + std::pair GetReadOnlyBytesForEntireFrame() const; + std::pair GetReadOnlyBytesForHeader() const; + std::pair GetReadOnlyBytesForPayload() const; + + static const uint32_t MAX_EXTEND_HEAD_LENGTH = 512; +private: + uint8_t *oringinalBytes_ = nullptr; // all beytes start addr + uint8_t *bytes_ = nullptr; // distributeddb start addr + const uint8_t *externalBytes_ = nullptr; + uint32_t totalLen_ = 0; + uint32_t headerLen_ = 0; + uint32_t payloadLen_ = 0; + uint32_t paddingLen_ = 0; + // only apply message will use extend header + uint32_t extendHeadLen_ = 0; + bool isExternalStackMemory_ = false; +}; +} // namespace DistributedDB + +#endif // SERIALBUFFER_H diff --git a/mock/distributeddb/include/auto_launch_export.h b/mock/distributeddb/include/auto_launch_export.h new file mode 100644 index 0000000000000000000000000000000000000000..7a1b86ce760258ec9ab21b483dcc178a64ab9fc6 --- /dev/null +++ b/mock/distributeddb/include/auto_launch_export.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_AUTO_LAUNCH_EXPORT_H +#define DISTRIBUTEDDB_AUTO_LAUNCH_EXPORT_H + +#include "types_export.h" +#include "kv_store_observer.h" +#include "kv_store_nb_delegate.h" +#include "store_observer.h" + +namespace DistributedDB { +struct AutoLaunchOption { + bool createIfNecessary = true; + bool isEncryptedDb = false; + CipherType cipher = CipherType::DEFAULT; + CipherPassword passwd; + std::string schema; + bool createDirByStoreIdOnly = false; + std::string dataDir; + KvStoreObserver *observer = nullptr; + int conflictType = 0; + KvStoreNbConflictNotifier notifier; + SecurityOption secOption; + bool isNeedIntegrityCheck = false; + bool isNeedRmCorruptedDb = false; + bool isNeedCompressOnSync = false; + uint8_t compressionRate = 100; // valid in [1, 100]. + bool isAutoSync = true; + StoreObserver *storeObserver = nullptr; + bool syncDualTupleMode = false; // communicator label use dualTuple hash or not +}; + +struct AutoLaunchParam { + std::string userId; + std::string appId; + std::string storeId; + AutoLaunchOption option; + AutoLaunchNotifier notifier; + std::string path; +}; + +using AutoLaunchRequestCallback = std::function; +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_AUTO_LAUNCH_EXPORT_H diff --git a/mock/distributeddb/include/query.h b/mock/distributeddb/include/query.h new file mode 100644 index 0000000000000000000000000000000000000000..b12433166a5e6655e3b030b255dd447d658be81a --- /dev/null +++ b/mock/distributeddb/include/query.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_QUERY_H +#define DISTRIBUTEDDB_QUERY_H + +#include +#include +#include +#include + +#include "query_expression.h" +#include "types_export.h" + +namespace DistributedDB { +class GetQueryInfo; +class Query { +public: + + // Do not support concurrent use of query objects + DB_API static Query Select(); + DB_API static Query Select(const std::string &tableName); + + template + DB_API Query &EqualTo(const std::string &field, const T &value) + { + FieldValue fieldValue; + QueryValueType type = GetFieldTypeAndValue(value, fieldValue); + ExecuteCompareOperation(QueryObjType::EQUALTO, field, type, fieldValue); + return *this; + } + + template + DB_API Query &NotEqualTo(const std::string &field, const T &value) + { + FieldValue fieldValue; + QueryValueType type = GetFieldTypeAndValue(value, fieldValue); + ExecuteCompareOperation(QueryObjType::NOT_EQUALTO, field, type, fieldValue); + return *this; + } + + template + DB_API Query &GreaterThan(const std::string &field, const T &value) + { + FieldValue fieldValue; + QueryValueType type = GetFieldTypeAndValue(value, fieldValue); + ExecuteCompareOperation(QueryObjType::GREATER_THAN, field, type, fieldValue); + return *this; + } + + template + DB_API Query &LessThan(const std::string &field, const T &value) + { + FieldValue fieldValue; + QueryValueType type = GetFieldTypeAndValue(value, fieldValue); + ExecuteCompareOperation(QueryObjType::LESS_THAN, field, type, fieldValue); + return *this; + } + + template + DB_API Query &GreaterThanOrEqualTo(const std::string &field, const T &value) + { + FieldValue fieldValue; + QueryValueType type = GetFieldTypeAndValue(value, fieldValue); + ExecuteCompareOperation(QueryObjType::GREATER_THAN_OR_EQUALTO, field, type, fieldValue); + return *this; + } + + template + DB_API Query &LessThanOrEqualTo(const std::string &field, const T &value) + { + FieldValue fieldValue; + QueryValueType type = GetFieldTypeAndValue(value, fieldValue); + ExecuteCompareOperation(QueryObjType::LESS_THAN_OR_EQUALTO, field, type, fieldValue); + return *this; + } + + DB_API Query &OrderBy(const std::string &field, bool isAsc = true); + + DB_API Query &Limit(int number, int offset = 0); + + DB_API Query &Like(const std::string &field, const std::string &value); + + DB_API Query &NotLike(const std::string &field, const std::string &value); + + template + DB_API Query &In(const std::string &field, const std::vector &values) + { + std::vector fieldValues; + QueryValueType type = QueryValueType::VALUE_TYPE_NULL; + for (const auto &value : values) { + FieldValue fieldValue; + type = GetFieldTypeAndValue(value, fieldValue); + fieldValues.push_back(fieldValue); + } + + ExecuteCompareOperation(QueryObjType::IN, field, type, fieldValues); + return *this; + } + + template + DB_API Query &NotIn(const std::string &field, const std::vector &values) + { + std::vector fieldValues; + QueryValueType type = QueryValueType::VALUE_TYPE_NULL; + for (const auto &value : values) { + FieldValue fieldValue; + type = GetFieldTypeAndValue(value, fieldValue); + fieldValues.push_back(fieldValue); + } + + ExecuteCompareOperation(QueryObjType::NOT_IN, field, type, fieldValues); + return *this; + } + + DB_API Query &IsNull(const std::string &field); + + DB_API Query &And(); + + DB_API Query &Or(); + + DB_API Query &IsNotNull(const std::string &field); + + DB_API Query &BeginGroup(); + + DB_API Query &EndGroup(); + + DB_API Query &PrefixKey(const std::vector &key); + + DB_API Query &SuggestIndex(const std::string &indexName); + + DB_API Query &InKeys(const std::set &keys); + + friend class GetQueryInfo; + DB_API ~Query() = default; + DB_API Query() = default; +private: + explicit Query(const std::string &tableName); + + DB_SYMBOL void ExecuteCompareOperation(QueryObjType operType, const std::string &field, + const QueryValueType type, const FieldValue &fieldValue); + DB_SYMBOL void ExecuteCompareOperation(QueryObjType operType, const std::string &field, + const QueryValueType type, const std::vector &fieldValue); + + template + QueryValueType GetFieldTypeAndValue(const T &queryValue, FieldValue &fieldValue) + { + return GetQueryValueType::GetFieldTypeAndValue(queryValue, fieldValue); + } + + QueryExpression queryExpression_; +}; +} // namespace DistributedDB +#endif // DISTRIBUTEDDB_QUERY_H diff --git a/mock/distributeddb/include/query_expression.h b/mock/distributeddb/include/query_expression.h new file mode 100644 index 0000000000000000000000000000000000000000..0ee1467e2518ee81d5fc83341fd1655408415347 --- /dev/null +++ b/mock/distributeddb/include/query_expression.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_QUERY_EXPRESSION_H +#define DISTRIBUTEDDB_QUERY_EXPRESSION_H + +#include +#include +#include +#include + +#include "types_export.h" + +namespace DistributedDB { +enum class QueryValueType: int32_t { + VALUE_TYPE_INVALID = -1, + VALUE_TYPE_NULL, + VALUE_TYPE_BOOL, + VALUE_TYPE_INTEGER, + VALUE_TYPE_LONG, + VALUE_TYPE_DOUBLE, + VALUE_TYPE_STRING, +}; + +// value will const, it will influence query object id +// use high pos bit to distinguish operator type +enum class QueryObjType : uint32_t { + OPER_ILLEGAL = 0x0000, + EQUALTO = 0x0101, + NOT_EQUALTO, + GREATER_THAN, + LESS_THAN, + GREATER_THAN_OR_EQUALTO, + LESS_THAN_OR_EQUALTO, + LIKE = 0x0201, + NOT_LIKE, + IS_NULL, + IS_NOT_NULL, + IN = 0x0301, + NOT_IN, + QUERY_BY_KEY_PREFIX = 0x0401, + BEGIN_GROUP = 0x0501, + END_GROUP, + AND = 0x0601, + OR, + LIMIT = 0x0701, + ORDERBY, + SUGGEST_INDEX = 0x0801, + IN_KEYS = 0x0901, +}; + +struct QueryObjNode { + QueryObjType operFlag = QueryObjType::OPER_ILLEGAL; + std::string fieldName {}; + QueryValueType type = QueryValueType::VALUE_TYPE_INVALID; + std::vector fieldValue = {}; + bool IsValid() + { + return operFlag != QueryObjType::OPER_ILLEGAL && + type != QueryValueType::VALUE_TYPE_INVALID; + } +}; + +class QueryExpression final { +public: + DB_SYMBOL QueryExpression(); + DB_SYMBOL ~QueryExpression() {}; + + void EqualTo(const std::string &field, const QueryValueType type, const FieldValue &value); + + void NotEqualTo(const std::string &field, const QueryValueType type, const FieldValue &value); + + void GreaterThan(const std::string &field, const QueryValueType type, const FieldValue &value); + + void LessThan(const std::string &field, const QueryValueType type, const FieldValue &value); + + void GreaterThanOrEqualTo(const std::string &field, const QueryValueType type, const FieldValue &value); + + void LessThanOrEqualTo(const std::string &field, const QueryValueType type, const FieldValue &value); + + void OrderBy(const std::string &field, bool isAsc); + + void Limit(int number, int offset); + + void Like(const std::string &field, const std::string &value); + void NotLike(const std::string &field, const std::string &value); + + void In(const std::string &field, const QueryValueType type, const std::vector &values); + void NotIn(const std::string &field, const QueryValueType type, const std::vector &values); + + void IsNull(const std::string &field); + void IsNotNull(const std::string &field); + + void And(); + + void Or(); + + void BeginGroup(); + + void EndGroup(); + + void Reset(); + + void QueryByPrefixKey(const std::vector &key); + + void QueryBySuggestIndex(const std::string &indexName); + + std::vector GetPreFixKey() const; + + void SetTableName(const std::string &tableName); + const std::string &GetTableName(); + bool IsTableNameSpecified() const; + + std::string GetSuggestIndex() const; + + const std::set &GetKeys() const; + void InKeys(const std::set &keys); + + const std::list &GetQueryExpression(); + + void SetErrFlag(bool flag); + bool GetErrFlag(); + +private: + void AssemblyQueryInfo(const QueryObjType queryOperType, const std::string &field, + const QueryValueType type, const std::vector &value, bool isNeedFieldPath); + + std::list queryInfo_; + bool errFlag_ = true; + std::vector prefixKey_; + std::string suggestIndex_; + std::string tableName_; + bool isTableNameSpecified_; + std::set keys_; +}; + +// specialize for double +class GetQueryValueType { +public: + static QueryValueType GetFieldTypeAndValue(const double &queryValue, FieldValue &fieldValue) + { + fieldValue.doubleValue = queryValue; + return QueryValueType::VALUE_TYPE_DOUBLE; + } + static QueryValueType GetFieldTypeAndValue(const int &queryValue, FieldValue &fieldValue) + { + fieldValue.integerValue = queryValue; + return QueryValueType::VALUE_TYPE_INTEGER; + } + static QueryValueType GetFieldTypeAndValue(const int64_t &queryValue, FieldValue &fieldValue) + { + fieldValue.longValue = queryValue; + return QueryValueType::VALUE_TYPE_LONG; + } + static QueryValueType GetFieldTypeAndValue(const bool &queryValue, FieldValue &fieldValue) + { + fieldValue.boolValue = queryValue; + return QueryValueType::VALUE_TYPE_BOOL; + } + static QueryValueType GetFieldTypeAndValue(const std::string &queryValue, FieldValue &fieldValue) + { + fieldValue.stringValue = queryValue; + return QueryValueType::VALUE_TYPE_STRING; + } + static QueryValueType GetFieldTypeAndValue(const char *queryValue, FieldValue &fieldValue) + { + if (queryValue == nullptr) { + return QueryValueType::VALUE_TYPE_STRING; + } + fieldValue.stringValue = queryValue; + return QueryValueType::VALUE_TYPE_STRING; + } +}; +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/include/types_export.h b/mock/distributeddb/include/types_export.h new file mode 100644 index 0000000000000000000000000000000000000000..b084156c10698f787cf943a95423a6404e7edf62 --- /dev/null +++ b/mock/distributeddb/include/types_export.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_TYPES_EXPORT_H +#define DISTRIBUTEDDB_TYPES_EXPORT_H + +#include +#include +#include +#include +#include + +namespace DistributedDB { +#ifdef _WIN32 + #ifdef DB_DLL_EXPORT + #define DB_API __declspec(dllexport) + #else + #define DB_API + #endif +#else + #define DB_API __attribute__ ((visibility ("default"))) +#endif + +#define DB_SYMBOL DB_API + +using Key = std::vector; +using Value = std::vector; + +struct Entry { + Key key; + Value value; +}; + +enum class CipherType { + DEFAULT, + AES_256_GCM, // AES-256-GCM +}; + +class CipherPassword final { +public: + enum ErrorCode { + OK = 0, + OVERSIZE, + INVALID_INPUT, + SECUREC_ERROR, + }; + + DB_API CipherPassword(); + DB_API ~CipherPassword(); + + DB_API bool operator==(const CipherPassword &input) const; + DB_API bool operator!=(const CipherPassword &input) const; + + DB_API size_t GetSize() const; + DB_API const uint8_t *GetData() const; + DB_API int SetValue(const uint8_t *inputData, size_t inputSize); + DB_API int Clear(); + +private: + static const size_t MAX_PASSWORD_SIZE = 128; + uint8_t data_[MAX_PASSWORD_SIZE] = {UCHAR_MAX}; + size_t size_ = 0; +}; + +using PragmaData = void *; + +struct PragmaEntryDeviceIdentifier { + Key key; + bool origDevice = true; + std::string deviceIdentifier; +}; + +using KvStoreNbPublishAction = std::function; + +struct PragmaDeviceIdentifier { + std::string deviceID; + std::string deviceIdentifier; +}; + +enum WipePolicy { + RETAIN_STALE_DATA = 1, // remote stale data will be retained in syncing when remote db rebuiled. + WIPE_STALE_DATA // remote stale data will be wiped when in syncing remote db rebuiled. +}; + +// We don't parse, read or modify the array type, so there are not a corresponding array value +// The leaf object is empty, an internal object always composed by other type values. +struct FieldValue { + union { + bool boolValue; + int32_t integerValue; + int64_t longValue = 0; + double doubleValue; + }; + std::string stringValue; +}; + +enum PermissionCheckFlag { + CHECK_FLAG_SEND = 1, // send + CHECK_FLAG_RECEIVE = 2, // receive + CHECK_FLAG_AUTOSYNC = 4, // autosync flag + CHECK_FLAG_SPONSOR = 8, // sync sponsor +}; + +using PermissionCheckCallback = std::function; + +using PermissionCheckCallbackV2 = std::function; + +using StoreStatusNotifier = std::function; // status, 1: online, 0: offline + +using SyncActivationCheckCallback = std::function; + +enum AutoLaunchStatus { + WRITE_OPENED = 1, + WRITE_CLOSED = 2, + INVALID_PARAM = 3, // AutoLaunchRequestCallback, if param check failed +}; + +using AutoLaunchNotifier = std::function; + +enum SecurityLabel : int { + INVALID_SEC_LABEL = -1, + NOT_SET = 0, + S0, + S1, + S2, + S3, + S4 +}; + +// security flag type +enum SecurityFlag : int { + INVALID_SEC_FLAG = -1, + ECE = 0, + SECE +}; + +struct SecurityOption { + int securityLabel = 0; // the securityLabel is the class of data sensitive, see enum SecurityLabel + int securityFlag = 0; // the securityFlag is the encryption method of the file only used for S3 like 0:ECE, 1:SECE + bool operator==(const SecurityOption &rhs) const + { + return securityLabel == rhs.securityLabel && securityFlag == rhs.securityFlag; + } +}; + +enum class ResultSetCacheMode : int { + CACHE_FULL_ENTRY = 0, // Ordinary mode efficient when sequential access, the default mode + CACHE_ENTRY_ID_ONLY = 1, // Special mode efficient when random access +}; + +struct RemotePushNotifyInfo { + std::string deviceId; +}; +using RemotePushFinishedNotifier = std::function; +using RemotePushFinisheNotifier = RemotePushFinishedNotifier; // To correct spelling errors in the previous version + +enum class CompressAlgorithm : uint8_t { + NONE = 0, + ZLIB = 1 +}; +} // namespace DistributedDB +#endif // DISTRIBUTEDDB_TYPES_EXPORT_H diff --git a/mock/distributeddb/interfaces/include/get_query_info.h b/mock/distributeddb/interfaces/include/get_query_info.h new file mode 100644 index 0000000000000000000000000000000000000000..5adaf9d91b79eedc185e0713e2e9ea3062e11dd4 --- /dev/null +++ b/mock/distributeddb/interfaces/include/get_query_info.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_GET_QUERY_INFO_H +#define DISTRIBUTEDDB_GET_QUERY_INFO_H + +#include "query.h" + +namespace DistributedDB { +class GetQueryInfo { +public: + static QueryExpression GetQueryExpression(const Query &query) + { + return query.queryExpression_; + } +}; +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/intercepted_data.h b/mock/distributeddb/interfaces/include/intercepted_data.h new file mode 100644 index 0000000000000000000000000000000000000000..9d6fa2cfec81185c6927576aed95be52fc5fc46f --- /dev/null +++ b/mock/distributeddb/interfaces/include/intercepted_data.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERCEPTED_DATA_H +#define INTERCEPTED_DATA_H + +#include +#include "store_types.h" +#include "types_export.h" + +namespace DistributedDB { +struct KVEntry { + const Key &key; + const Value &value; +}; + +class InterceptedData { +public: + InterceptedData() {} + DB_API virtual ~InterceptedData() {} + + // Interface for getting the intercepted entries. + DB_API virtual std::vector GetEntries() = 0; + + // Interface for modifying key. Index is the index of GetEntries(). + DB_API virtual DBStatus ModifyKey(size_t index, const Key &newKey) = 0; + + // Interface for modifying value. Index is the index of GetEntries(). + DB_API virtual DBStatus ModifyValue(size_t index, const Value &newValue) = 0; +}; + +// The callback function works on the send data from device "sourceID" to device "targetID". +using PushDataInterceptor = std::function; +} // namespace DistributedDB +#endif // INTERCEPTED_DATA_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/iprocess_communicator.h b/mock/distributeddb/interfaces/include/iprocess_communicator.h new file mode 100644 index 0000000000000000000000000000000000000000..5abbd3069daf8782c99b85503198462c26fda049 --- /dev/null +++ b/mock/distributeddb/interfaces/include/iprocess_communicator.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IPROCESSCOMMUNICATOR_H +#define IPROCESSCOMMUNICATOR_H + +#include +#include +#include +#include +#include +#include "store_types.h" + +namespace DistributedDB { +// The DeviceInfos may contain other fields(Can only be auxiliary information) besides identifier field in the future. +struct DeviceInfos { + std::string identifier; // An unique and fixed identifier representing a device, such as UUID. +}; + +struct ExtendInfo { + std::string appId; + std::string storeId; + std::string userId; + std::string dstTarget; +}; + +class ExtendHeaderHandle { +public: + ExtendHeaderHandle() {}; + virtual ~ExtendHeaderHandle() {}; + // headSize should be 8 byte align + // return OK and headSize = 0 if no need to fill Head Data + // return OK and headSize > 0 if permit sync and will call FillHeadData + // return NO_PERMISSION if not permit sync + virtual DBStatus GetHeadDataSize(uint32_t &headSize) + { + headSize = 0; + return OK; + }; + // return OK if fill data ok + // return not OK if fill data failed + virtual DBStatus FillHeadData(uint8_t *data, uint32_t headSize, uint32_t totalLen) + { + (void)data; + (void)headSize; + (void)totalLen; + return OK; + }; +}; + +// In OnDeviceChange, all field of devInfo should be valid, isOnline true for online and false for offline. +// The concept of online or offline: +// 1: Can be at the physical device level, which means the remote device can be visible and communicable by local device +// 2: Can also be at the process level, which means the same ProcessCommunicator(with same processLabel) had been +// started on the remote device and thus visible and communicable by this local ProcessCommunicator. +using OnDeviceChange = std::function; + +// In OnDataReceive, all field of srcDevInfo should be valid +using OnDataReceive = std::function; + +// For all functions with returnType DBStatus: +// return DBStatus::OK if successful, otherwise DBStatus::DB_ERROR if anything wrong. +// Additional information of reason why failed can be present in the log by the implementation. +// For "Get" or "Is" functions, implementation should notice that concurrent call is possible. +class IProcessCommunicator { +public: + // The distributeddb in one process can only use one ProcessCommunicator at the same time + // The ProcessCommunicator can only Start one processLabel at the same time + // The ProcessCommunicator can Start again after stop + // The processLabel should not be an empty string + virtual DBStatus Start(const std::string &processLabel) = 0; + + // The Stop should only be called after Start successfully + virtual DBStatus Stop() = 0; + + // The register function can be called anytime regardless of whether started or stopped. + // There will only be one callback at the same time for each function + // If register again, the latter callback replace the former callback. + // Register nullptr as callback to do unregister semantic. + // For concurrency security of implementation, there should be lock between register_operation and callback_event. + virtual DBStatus RegOnDeviceChange(const OnDeviceChange &callback) = 0; + virtual DBStatus RegOnDataReceive(const OnDataReceive &callback) = 0; + + // The SendData function should only be called after Start successfully + // Only the identifier field of dstDevInfo must be valid, no requirement for other field. + virtual DBStatus SendData(const DeviceInfos &dstDevInfo, const uint8_t *data, uint32_t length) = 0; + + // The GetMtuSize function can be called anytime regardless of whether started or stopped. + // The mtuSize should not less than 1K otherwise it will be regard as 1K. + // For run on OHOS, there is agreement that the mtuSize should be nearly 5M. + virtual uint32_t GetMtuSize() = 0; + + // The GetLocalDeviceInfos function should only be called after Start successfully + // All field of returned DeviceInfos must be valid, the identifier must not be empty and changed between time. + virtual DeviceInfos GetLocalDeviceInfos() = 0; + + // The GetRemoteOnlineDeviceInfosList function should only be called after Start successfully + // All field of returned DeviceInfos must be valid, should not contain duplicate device or local device + virtual std::vector GetRemoteOnlineDeviceInfosList() = 0; + + // The IsSameProcessLabelStartedOnPeerDevice function should only be called after Start successfully + // Only the identifier field of peerDevInfo must be valid, no requirement for other field. + // If the peer device is offline, then return false. + // If the peer device is online but no ProcessCommunicator with same processLabel had started on it, return false. + // If the peer device is online and ProcessCommunicator with same processLabel had started on it, return true. + virtual bool IsSameProcessLabelStartedOnPeerDevice(const DeviceInfos &peerDevInfo) = 0; + + virtual ~IProcessCommunicator() {}; + + // For ABI compatibility reason, temporarily place this method at last and offer a fake implementation. + // The valid mtuSize range from 1K to 5M, value beyond this range will be set to the upper or lower limit. + virtual uint32_t GetMtuSize(const DeviceInfos &devInfo) + { + if (devInfo.identifier.empty()) { + // Error case(would never happen actually) to avoid "unused-parameter" warning. + return 0; + } + return GetMtuSize(); + } + + // The valid timeout range from 5s to 60s, value beyond this range will be set to the upper or lower limit. + virtual uint32_t GetTimeout() + { + return 5 * 1000; // 5 * 1000ms + }; + + // The valid timeout range from 5s to 60s, value beyond this range will be set to the upper or lower limit. + virtual uint32_t GetTimeout(const DeviceInfos &devInfo) + { + if (devInfo.identifier.empty()) { + // Error case(would never happen actually) to avoid "unused-parameter" warning. + return 5 * 1000; // 5 * 1000ms + } + return GetTimeout(); + } + + virtual std::shared_ptr GetExtendHeaderHandle(const ExtendInfo ¶mInfo) + { + (void)paramInfo; + return nullptr; + } + // called after OnDataReceive + // return NO_PERMISSION while no need to handle the dataBuff if remote device userId is not mate with local userId + // return INVALID_FORMAT and headLength = 0 if data service can not deSerialize the buff + // return OK if deSerialize ok and get HeadLength/localUserId successfully + virtual DBStatus CheckAndGetDataHeadInfo(const uint8_t *data, uint32_t totalLen, uint32_t &headLength, + std::vector &userId) + { + (void)data; + (void)totalLen; + (void)userId; + headLength = 0; + return OK; + } +}; +} // namespace DistributedDB + +#endif // IPROCESSCOMMUNICATOR_H diff --git a/mock/distributeddb/interfaces/include/iprocess_system_api_adapter.h b/mock/distributeddb/interfaces/include/iprocess_system_api_adapter.h new file mode 100644 index 0000000000000000000000000000000000000000..3a3b14de6dcdac3e5f8082f7fbd28d4c9905f366 --- /dev/null +++ b/mock/distributeddb/interfaces/include/iprocess_system_api_adapter.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IPROCESS_SYSTEM_API_ADAPTER_H +#define IPROCESS_SYSTEM_API_ADAPTER_H + +#include +#include +#include "store_types.h" + +namespace DistributedDB { +using OnAccessControlledEvent = std::function; + +// For all functions with returnType DBStatus: +// return DBStatus::OK if successful, otherwise DBStatus::DB_ERROR if anything wrong. +// Additional information of reason why failed can be present in the log by the implementation. +// For "Get" or "Is" functions, implementation should notice that concurrent call is possible. +// The distributeddb in one process can only use one ProcessSystemApiAdapter at the same time +class IProcessSystemApiAdapter { +public: + // Function used to register a AccessControlled listener, like screen locked. + // There will only be one callback at the same time for each function + // If register again, the latter callback replace the former callback. + // Register nullptr as callback to do unregister semantic. + // For concurrency security of implementation, there should be lock between register_operation and callback_event. + virtual DBStatus RegOnAccessControlledEvent(const OnAccessControlledEvent &callback) = 0; + + // Check is the access of this device in locked state + virtual bool IsAccessControlled() const = 0; + + // Set the SecurityOption to the targe filepath. + // If the filePath is a directory, All the files and directories in the filePath should be effective. + virtual DBStatus SetSecurityOption(const std::string &filePath, const SecurityOption &option) = 0; + + // Get the SecurityOption of the targe filepath. + virtual DBStatus GetSecurityOption(const std::string &filePath, SecurityOption &option) const = 0; + + // Check if the target device can save the data at the give sensitive class. + virtual bool CheckDeviceSecurityAbility(const std::string &devId, const SecurityOption &option) const = 0; + + virtual ~IProcessSystemApiAdapter() {}; +}; +} // namespace DistributedDB +#endif // IPROCESS_SYSTEM_API_ADAPTER_H diff --git a/mock/distributeddb/interfaces/include/kv_store_changed_data.h b/mock/distributeddb/interfaces/include/kv_store_changed_data.h new file mode 100644 index 0000000000000000000000000000000000000000..65f84a41965185653920a046ea4c9d62c42a92ac --- /dev/null +++ b/mock/distributeddb/interfaces/include/kv_store_changed_data.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_CHANGED_DATA_H +#define KV_STORE_CHANGED_DATA_H + +#include +#include "store_types.h" + +namespace DistributedDB { +class KvStoreChangedData { +public: + KvStoreChangedData() {} + DB_API virtual ~KvStoreChangedData() {} + + // Interface for Getting the inserted, updated, delete entries. + DB_API virtual const std::list &GetEntriesInserted() const = 0; + + DB_API virtual const std::list &GetEntriesUpdated() const = 0; + + DB_API virtual const std::list &GetEntriesDeleted() const = 0; + + DB_API virtual bool IsCleared() const = 0; +}; +} // namespace DistributedDB + +#endif // KV_STORE_CHANGED_DATA diff --git a/mock/distributeddb/interfaces/include/kv_store_delegate.h b/mock/distributeddb/interfaces/include/kv_store_delegate.h new file mode 100644 index 0000000000000000000000000000000000000000..5194ac455d4004c67a6e84e0ff8a00922e428934 --- /dev/null +++ b/mock/distributeddb/interfaces/include/kv_store_delegate.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_DELEGATE_H +#define KV_STORE_DELEGATE_H + +#include +#include + +#include "store_types.h" +#include "kv_store_observer.h" +#include "kv_store_snapshot_delegate.h" + +namespace DistributedDB { +class KvStoreDelegate { +public: + using ConflictResolution = std::function; + + struct Option { + bool createIfNecessary = true; + bool localOnly = false; + bool isEncryptedDb = false; + CipherType cipher = CipherType::DEFAULT; + CipherPassword passwd; + bool createDirByStoreIdOnly = false; + }; + + DB_API virtual ~KvStoreDelegate() {} + + // Used to Put a k-v pair to the kvstore. + // Return OK if operation is successful. + DB_API virtual DBStatus Put(const Key &key, const Value &value) = 0; + + // Used to Put a vector contains k-v pairs to the kvstore. + // Return OK if operation is successful.. + DB_API virtual DBStatus PutBatch(const std::vector &entries) = 0; + + // Delete a record with the given key. + // Return OK if operation is successful. + DB_API virtual DBStatus Delete(const Key &key) = 0; + + // Batch delete records with the given keys. + // Return OK if operation is successful. + DB_API virtual DBStatus DeleteBatch(const std::vector &keys) = 0; + + // Delete all record of the kvstore. + // Return OK if operation is successful. + DB_API virtual DBStatus Clear() = 0; + + // Return a storeId of the KvStore instance + DB_API virtual std::string GetStoreId() const = 0; + + // Get a snapshot of the kvstore. The observer is used to notify data changed, it can be null. + // Return value is DBStatus and KvStoreSnapshotDelegate*, these values will be passed to the callback. + DB_API virtual void GetKvStoreSnapshot(KvStoreObserver *observer, + const std::function &callback) = 0; + + // Release a snapshot, it will return OK if operation is successful. + DB_API virtual DBStatus ReleaseKvStoreSnapshot(KvStoreSnapshotDelegate *&snapshotDelegate) = 0; + + // Register a data change observer + DB_API virtual DBStatus RegisterObserver(KvStoreObserver *observer) = 0; + + // Unregister a data change observer + DB_API virtual DBStatus UnRegisterObserver(const KvStoreObserver *observer) = 0; + + // Start a transaction + DB_API virtual DBStatus StartTransaction() = 0; + + // Commit a transaction + DB_API virtual DBStatus Commit() = 0; + + // Rollback a transaction + DB_API virtual DBStatus Rollback() = 0; + + // Used to set the resolution policy for conflicts. + // Return OK if operation is successful. + DB_API virtual DBStatus SetConflictResolutionPolicy(ResolutionPolicyType type, + const ConflictResolution &resolution) = 0; + + // Used to rekey the database. + DB_API virtual DBStatus Rekey(const CipherPassword &password) = 0; + + // Special pragma interface, see PragmaCmd and PragmaData, + DB_API virtual DBStatus Pragma(PragmaCmd cmd, PragmaData ¶mData) = 0; + + // Empty passwords represent non-encrypted files. + // Export existing database files to a specified database file in the specified directory. + DB_API virtual DBStatus Export(const std::string &filePath, const CipherPassword &passwd) = 0; + + // Import the existing database files to the specified database file in the specified directory. + DB_API virtual DBStatus Import(const std::string &filePath, const CipherPassword &passwd) = 0; +}; +} // namespace DistributedDB + +#endif // KV_STORE_DELEGATE_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/kv_store_delegate_manager.h b/mock/distributeddb/interfaces/include/kv_store_delegate_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..69631d4d000758886f1b8385f8f62d0f4b846f0a --- /dev/null +++ b/mock/distributeddb/interfaces/include/kv_store_delegate_manager.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_DELEGATE_MANAGER_H +#define KV_STORE_DELEGATE_MANAGER_H + +#include +#include +#include +#include + +#ifndef OMIT_MULTI_VER +#include "kv_store_delegate.h" +#endif +#include "kv_store_nb_delegate.h" +#include "store_types.h" +#include "iprocess_communicator.h" +#include "iprocess_system_api_adapter.h" +#include "auto_launch_export.h" + +namespace DistributedDB { +class KvStoreDelegateManager final { +public: + DB_API KvStoreDelegateManager(const std::string &appId, const std::string &userId); + DB_API ~KvStoreDelegateManager(); + + KvStoreDelegateManager(const KvStoreDelegateManager &) = delete; + KvStoreDelegateManager(KvStoreDelegateManager &&) = delete; + KvStoreDelegateManager &operator=(const KvStoreDelegateManager &) = delete; + KvStoreDelegateManager &operator=(KvStoreDelegateManager &&) = delete; + + // Used to set global config of the KvStores, such dataDir, return OK if set config success. + DB_API DBStatus SetKvStoreConfig(const KvStoreConfig &kvStoreConfig); + +#ifndef OMIT_MULTI_VER + // Used to open or create a KvStore. + // Return OK and a KvStoreDelegate* if there is no error. else return ERROR and nullptr; + DB_API void GetKvStore(const std::string &storeId, const KvStoreDelegate::Option &option, + const std::function &callback); +#endif + // Used to open or create a KvStore(Natural store). + // Suggest: Not to use encrypted database in S3 SECE access controlled; + // Warning: Access controlled prevents access to files so cannot verify passwords, + // So that a cacheDb with incorrect passwd will be created or opened and lose these data. + DB_API void GetKvStore(const std::string &storeId, const KvStoreNbDelegate::Option &option, + const std::function &callback); + +#ifndef OMIT_MULTI_VER + // Close a KvStore, return OK if close success. + DB_API DBStatus CloseKvStore(KvStoreDelegate *kvStore); +#endif + + DB_API DBStatus CloseKvStore(KvStoreNbDelegate *kvStore); + + // Used to delete a KvStore, return OK if delete success. + DB_API DBStatus DeleteKvStore(const std::string &storeId); + + // Get the database size. + DB_API DBStatus GetKvStoreDiskSize(const std::string &storeId, uint64_t &size); + + // Used to set the process userid and appId + DB_API static DBStatus SetProcessLabel(const std::string &appId, const std::string &userId); + + // Set process communicator. + DB_API static DBStatus SetProcessCommunicator(const std::shared_ptr &inCommunicator); + + DB_API static void SetKvStoreCorruptionHandler(const KvStoreCorruptionHandler &handler); + + // Get database directory by storeId + appId + userId + DB_API static DBStatus GetDatabaseDir(const std::string &storeId, const std::string &appId, + const std::string &userId, std::string &directory); + + // Get database directory by storeId + DB_API static DBStatus GetDatabaseDir(const std::string &storeId, std::string &directory); + + DB_API static DBStatus SetPermissionCheckCallback(const PermissionCheckCallback &callback); + + DB_API static DBStatus SetPermissionCheckCallback(const PermissionCheckCallbackV2 &callback); + + DB_API static DBStatus EnableKvStoreAutoLaunch(const std::string &userId, const std::string &appId, + const std::string &storeId, const AutoLaunchOption &option, const AutoLaunchNotifier ¬ifier); + + DB_API static DBStatus DisableKvStoreAutoLaunch(const std::string &userId, const std::string &appId, + const std::string &storeId); + + DB_API static void SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback); + + DB_API static std::string GetKvStoreIdentifier(const std::string &userId, const std::string &appId, + const std::string &storeId, bool syncDualTupleMode = false); + + DB_API static DBStatus SetProcessSystemAPIAdapter(const std::shared_ptr &adapter); + + DB_API static void SetStoreStatusNotifier(const StoreStatusNotifier ¬ifier); + + DB_API static DBStatus SetSyncActivationCheckCallback(const SyncActivationCheckCallback &callback); + + DB_API static DBStatus NotifyUserChanged(); +private: + + // Check if the dataDir is safe arg. + bool IsDataDirSafe(const std::string &dataDir, std::string &canonicalDir) const; + bool GetKvStoreParamCheck(const std::string &storeId, const KvStoreNbDelegate::Option &option, + const std::function &callback) const; + DBStatus SetObserverNotifier(KvStoreNbDelegate *kvStore, const KvStoreNbDelegate::Option &option); + + const std::string &GetKvStorePath() const; + static const std::string DEFAULT_PROCESS_APP_ID; + static std::mutex communicatorMutex_; + static std::shared_ptr processCommunicator_; + static std::mutex multiUserMutex_; + + KvStoreConfig kvStoreConfig_; + std::string appId_; + std::string userId_; + + mutable std::mutex mutex_; +}; +} // namespace DistributedDB + +#endif // KV_STORE_DELEGATE_MANAGER_H diff --git a/mock/distributeddb/interfaces/include/kv_store_errno.h b/mock/distributeddb/interfaces/include/kv_store_errno.h new file mode 100644 index 0000000000000000000000000000000000000000..4cb150f59a06d3cdc8f5a3c01df5e249c1ed948c --- /dev/null +++ b/mock/distributeddb/interfaces/include/kv_store_errno.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_ERRNO_H +#define KV_STORE_ERRNO_H + +#include "store_types.h" + +namespace DistributedDB { +// Transfer the db error code to the DBStatus. +DBStatus TransferDBErrno(int err); +}; +#endif \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/kv_store_nb_conflict_data.h b/mock/distributeddb/interfaces/include/kv_store_nb_conflict_data.h new file mode 100644 index 0000000000000000000000000000000000000000..9ce4317d8f3a5f0ff9819a67d1eb4a1bfd3732f6 --- /dev/null +++ b/mock/distributeddb/interfaces/include/kv_store_nb_conflict_data.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_NB_CONFLICT_DATA_H +#define KV_STORE_NB_CONFLICT_DATA_H + +#include "store_types.h" + +namespace DistributedDB { +enum KvStoreNbConflictType { + CONFLICT_FOREIGN_KEY_ONLY = 0x01, // sync conflict for same origin dev + CONFIICT_FOREIGN_KEY_ONLY = CONFLICT_FOREIGN_KEY_ONLY, // sync conflict for same origin dev(compatible for mistake) + CONFLICT_FOREIGN_KEY_ORIG = 0x02, // sync conflict for different origin dev + CONFLICT_NATIVE_ALL = 0x0c, // native conflict. +}; + +class KvStoreNbConflictData { +public: + enum class ValueType { + OLD_VALUE = 0, + NEW_VALUE, + }; + + DB_API virtual ~KvStoreNbConflictData() {}; + + DB_API virtual KvStoreNbConflictType GetType() const = 0; + + DB_API virtual void GetKey(Key &key) const = 0; + + DB_API virtual DBStatus GetValue(ValueType type, Value &value) const = 0; + + DB_API virtual bool IsDeleted(ValueType type) const = 0; + + DB_API virtual bool IsNative(ValueType type) const = 0; +}; +} // namespace DistributedDB + +#endif // KV_STORE_NB_CONFLICT_DATA_H diff --git a/mock/distributeddb/interfaces/include/kv_store_nb_delegate.h b/mock/distributeddb/interfaces/include/kv_store_nb_delegate.h new file mode 100644 index 0000000000000000000000000000000000000000..5a15e46680c5aab25d838043d0a654fc3f07196b --- /dev/null +++ b/mock/distributeddb/interfaces/include/kv_store_nb_delegate.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_NB_DELEGATE_H +#define KV_STORE_NB_DELEGATE_H + +#include +#include +#include + +#include "store_types.h" +#include "kv_store_observer.h" +#include "kv_store_nb_conflict_data.h" +#include "kv_store_result_set.h" +#include "query.h" +#include "iprocess_system_api_adapter.h" +#include "intercepted_data.h" + +namespace DistributedDB { +using KvStoreNbPublishOnConflict = std::function; +using KvStoreNbConflictNotifier = std::function; + +class KvStoreNbDelegate { +public: + struct Option { + bool createIfNecessary = true; + bool isMemoryDb = false; + bool isEncryptedDb = false; + CipherType cipher = CipherType::DEFAULT; + CipherPassword passwd; + std::string schema = ""; + bool createDirByStoreIdOnly = false; + SecurityOption secOption; // Add data security level parameter + KvStoreObserver *observer = nullptr; + Key key; // The key that needs to be subscribed on obsever, empty means full subscription + unsigned int mode = 0; // obsever mode + int conflictType = 0; + KvStoreNbConflictNotifier notifier = nullptr; + int conflictResolvePolicy = LAST_WIN; + bool isNeedIntegrityCheck = false; + bool isNeedRmCorruptedDb = false; + bool isNeedCompressOnSync = false; + uint8_t compressionRate = 100; // Valid in [1, 100]. + bool syncDualTupleMode = false; // communicator label use dualTuple hash or not + }; + + DB_API virtual ~KvStoreNbDelegate() {} + + // Public zone interfaces + // Get value from the public zone of this store according to the key. + DB_API virtual DBStatus Get(const Key &key, Value &value) const = 0; + + // Get entries from the public zone of this store by key prefix. + // If 'keyPrefix' is empty, It would return all the entries in the zone. + DB_API virtual DBStatus GetEntries(const Key &keyPrefix, std::vector &entries) const = 0; + + // Get entries from the public zone of this store by key prefix. + // If 'keyPrefix' is empty, It would return all the entries in the zone. + DB_API virtual DBStatus GetEntries(const Key &keyPrefix, KvStoreResultSet *&resultSet) const = 0; + + // Get entries from the public zone of this store by query. + // If 'query' is empty, It would return all the entries in the zone. + DB_API virtual DBStatus GetEntries(const Query &query, std::vector &entries) const = 0; + + // Get entries from the public zone of this store by query. + // If query is empty, It would return all the entries in the zone. + DB_API virtual DBStatus GetEntries(const Query &query, KvStoreResultSet *&resultSet) const = 0; + + // Get count from the public zone of this store those meet conditions. + // If query is empty, It would return all the entries count in the zone. + DB_API virtual DBStatus GetCount(const Query &query, int &count) const = 0; + + // Close the result set returned by GetEntries(). + DB_API virtual DBStatus CloseResultSet(KvStoreResultSet *&resultSet) = 0; + + // Put one key-value entry into the public zone of this store. + DB_API virtual DBStatus Put(const Key &key, const Value &value) = 0; + + // Put a batch of entries into the public zone of this store. + DB_API virtual DBStatus PutBatch(const std::vector &entries) = 0; + + // Delete a batch of entries from the public zone of this store. + DB_API virtual DBStatus DeleteBatch(const std::vector &keys) = 0; + + // Delete one key-value entry from the public zone of this store according to the key. + DB_API virtual DBStatus Delete(const Key &key) = 0; + + // Local zone interfaces + // Get value from the local zone of this store according to the key. + DB_API virtual DBStatus GetLocal(const Key &key, Value &value) const = 0; + + // Get key-value entries from the local zone of this store by key prefix. + // If keyPrefix is empty, It would return all the entries in the local zone. + DB_API virtual DBStatus GetLocalEntries(const Key &keyPrefix, std::vector &entries) const = 0; + + // Put one key-value entry into the local zone of this store. + DB_API virtual DBStatus PutLocal(const Key &key, const Value &value) = 0; + + // Delete one key-value entry from the local zone of this store according to the key. + DB_API virtual DBStatus DeleteLocal(const Key &key) = 0; + + // Migrating(local zone <-> public zone) interfaces + // Publish a local key-value entry. + // Migrate the entry from the local zone to public zone. + DB_API virtual DBStatus PublishLocal(const Key &key, bool deleteLocal, bool updateTimestamp, + const KvStoreNbPublishOnConflict &onConflict) = 0; + + // Unpublish a public key-value entry. + // Migrate the entry from the public zone to local zone. + DB_API virtual DBStatus UnpublishToLocal(const Key &key, bool deletePublic, bool updateTimestamp) = 0; + + // Observer interfaces + // Register one observer which concerns the key and the changed data mode. + // If key is empty, observer would get all the changed data of the mode. + // There are three mode: native changes of nb syncable kv store, + // synced data changes from remote devices, + // local changes of local kv store. + DB_API virtual DBStatus RegisterObserver(const Key &key, unsigned int mode, KvStoreObserver *observer) = 0; + + // UnRegister the registered observer. + DB_API virtual DBStatus UnRegisterObserver(const KvStoreObserver *observer) = 0; + + // Remove the device data synced from remote. + DB_API virtual DBStatus RemoveDeviceData(const std::string &device) = 0; + + // Other interfaces + DB_API virtual std::string GetStoreId() const = 0; + + // Sync function interface, if wait set true, this function will be blocked until sync finished + DB_API virtual DBStatus Sync(const std::vector &devices, SyncMode mode, + const std::function &devicesMap)> &onComplete, + bool wait = false) = 0; + + // Special pragma interface, see PragmaCmd and PragmaData, + DB_API virtual DBStatus Pragma(PragmaCmd cmd, PragmaData ¶mData) = 0; + + // Set the conflict notifier for getting the specified type conflict data. + DB_API virtual DBStatus SetConflictNotifier(int conflictType, + const KvStoreNbConflictNotifier ¬ifier) = 0; + + // Used to rekey the database. + // Warning rekey may reopen database file, file handle may lose while locked + DB_API virtual DBStatus Rekey(const CipherPassword &password) = 0; + + // Empty passwords represent non-encrypted files. + // Export existing database files to a specified database file in the specified directory. + DB_API virtual DBStatus Export(const std::string &filePath, const CipherPassword &passwd) = 0; + + // Import the existing database files to the specified database file in the specified directory. + // Warning Import may reopen database file in locked state + DB_API virtual DBStatus Import(const std::string &filePath, const CipherPassword &passwd) = 0; + + // Start a transaction + DB_API virtual DBStatus StartTransaction() = 0; + + // Commit a transaction + DB_API virtual DBStatus Commit() = 0; + + // Rollback a transaction + DB_API virtual DBStatus Rollback() = 0; + + // Put a batch of entries into the local zone of this store. + DB_API virtual DBStatus PutLocalBatch(const std::vector &entries) = 0; + + // Delete a batch of entries from the local zone of this store according to the keys. + DB_API virtual DBStatus DeleteLocalBatch(const std::vector &keys) = 0; + + // Get the SecurityOption of this kvStore. + DB_API virtual DBStatus GetSecurityOption(SecurityOption &option) const = 0; + + // Set a notify callback, it will be called when remote push or push_pull finished. + // If Repeat set, subject to the last time. + // If set nullptr, means unregister the notify. + DB_API virtual DBStatus SetRemotePushFinishedNotify(const RemotePushFinishedNotifier ¬ifier) = 0; + + // Sync function interface, if wait set true, this function will be blocked until sync finished. + // Param query used to filter the records to be synchronized. + // Now just support push mode and query by prefixKey. + // If Query.limit is used, its query cache will not be recorded, In the same way below, + // the synchronization will still take the full amount. + DB_API virtual DBStatus Sync(const std::vector &devices, SyncMode mode, + const std::function &devicesMap)> &onComplete, + const Query &query, bool wait) = 0; + + // Check the integrity of this kvStore. + DB_API virtual DBStatus CheckIntegrity() const = 0; + + // Set an equal identifier for this database, After this called, send msg to the target will use this identifier + DB_API virtual DBStatus SetEqualIdentifier(const std::string &identifier, + const std::vector &targets) = 0; + + // This API is not recommended. Before using this API, you need to understand the API usage rules. + // Set pushdatainterceptor. The interceptor works when send data. + DB_API virtual DBStatus SetPushDataInterceptor(const PushDataInterceptor &interceptor) = 0; + + // Register a subscriber query on peer devices. The data in the peer device meets the subscriber query condition + // will automatically push to the local device when it's changed. + DB_API virtual DBStatus SubscribeRemoteQuery(const std::vector &devices, + const std::function &devicesMap)> &onComplete, + const Query &query, bool wait) = 0; + + // Unregister a subscriber query on peer devices. + DB_API virtual DBStatus UnSubscribeRemoteQuery(const std::vector &devices, + const std::function &devicesMap)> &onComplete, + const Query &query, bool wait) = 0; +}; +} // namespace DistributedDB + +#endif // KV_STORE_NB_DELEGATE_H diff --git a/mock/distributeddb/interfaces/include/kv_store_observer.h b/mock/distributeddb/interfaces/include/kv_store_observer.h new file mode 100644 index 0000000000000000000000000000000000000000..ea74c0c4b85439e4a79162274330aa3d8d441ec5 --- /dev/null +++ b/mock/distributeddb/interfaces/include/kv_store_observer.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_OBSERVER_H +#define KV_STORE_OBSERVER_H + +#include "kv_store_changed_data.h" + +namespace DistributedDB { +class KvStoreObserver { +public: + virtual ~KvStoreObserver() {} + + // Databa change callback + virtual void OnChange(const KvStoreChangedData &data) = 0; +}; +} // namespace DistributedDB + +#endif // KV_STORE_OBSERVER_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/kv_store_result_set.h b/mock/distributeddb/interfaces/include/kv_store_result_set.h new file mode 100644 index 0000000000000000000000000000000000000000..68a72de1d31dd002b2c75e7d8d4cd00104e4d4bc --- /dev/null +++ b/mock/distributeddb/interfaces/include/kv_store_result_set.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_RESULT_SET_H +#define KV_STORE_RESULT_SET_H + +#include "store_types.h" + +namespace DistributedDB { +class KvStoreResultSet { +public: + DB_API virtual ~KvStoreResultSet() {}; + + // Returns the count of rows in the result set. + DB_API virtual int GetCount() const = 0; + + // Returns the current read position of the result set. + DB_API virtual int GetPosition() const = 0; + + // Move the read position to the first row, return false if the result set is empty. + DB_API virtual bool MoveToFirst() = 0; + + // Move the read position to the last row, return false if the result set is empty. + DB_API virtual bool MoveToLast() = 0; + + // Move the read position to the next row, return false if the result set is empty + // or the read position is already past the last entry in the result set. + DB_API virtual bool MoveToNext() = 0; + + // Move the read position to the previous row, return false if the result set is empty + // or the read position is already before the first entry in the result set. + DB_API virtual bool MoveToPrevious() = 0; + + // Move the read position by a relative amount from the current position. + DB_API virtual bool Move(int offset) = 0; + + // Move the read position to an absolute position value. + DB_API virtual bool MoveToPosition(int position) = 0; + + // Returns whether the read position is pointing to the first row. + DB_API virtual bool IsFirst() const = 0; + + // Returns whether the read position is pointing to the last row. + DB_API virtual bool IsLast() const = 0; + + // Returns whether the read position is before the first row. + DB_API virtual bool IsBeforeFirst() const = 0; + + // Returns whether the read position is after the last row + DB_API virtual bool IsAfterLast() const = 0; + + // Get a key-value entry. + DB_API virtual DBStatus GetEntry(Entry &entry) const = 0; +}; +} // namespace DistributedDB + +#endif // KV_STORE_RESULT_SET_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/kv_store_snapshot_delegate.h b/mock/distributeddb/interfaces/include/kv_store_snapshot_delegate.h new file mode 100644 index 0000000000000000000000000000000000000000..3053e7ff5f6d08e60454e3b3cfcd1663a5c563a0 --- /dev/null +++ b/mock/distributeddb/interfaces/include/kv_store_snapshot_delegate.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_SNAPSHOT_DELEGATE_H +#define KV_STORE_SNAPSHOT_DELEGATE_H + +#include +#include + +#include "store_types.h" + +namespace DistributedDB { +class KvStoreSnapshotDelegate { +public: + DB_API virtual ~KvStoreSnapshotDelegate() {} + + // Get a value from the snapshot with the given key. + // The return value is DBStatus and Value, these values will be passed to the callback. + DB_API virtual void Get(const Key &key, const std::function &callback) const = 0; + + // Get entries from the snapshot which keys start with keyPrefix. + // The return value is DBStatus and Value, these values will be passed to the callback. + DB_API virtual void GetEntries(const Key &keyPrefix, + const std::function &)> &callback) const = 0; +}; +} // namespace DistributedDB + +#endif // KV_STORE_SNAPSHOT_DELEGATE_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/relational/relational_store_delegate.h b/mock/distributeddb/interfaces/include/relational/relational_store_delegate.h new file mode 100644 index 0000000000000000000000000000000000000000..448d22642e0ebc3dddd6c8b6bc77bab8cf4684f5 --- /dev/null +++ b/mock/distributeddb/interfaces/include/relational/relational_store_delegate.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RELATIONAL_STORE_DELEGATE_H +#define RELATIONAL_STORE_DELEGATE_H + +#include + +#include "query.h" +#include "store_types.h" +#include "store_observer.h" + +namespace DistributedDB { +class RelationalStoreDelegate { +public: + DB_API virtual ~RelationalStoreDelegate() = default; + + struct Option { + StoreObserver *observer = nullptr; + // split mode + }; + + DB_API virtual DBStatus CreateDistributedTable(const std::string &tableName) = 0; + + DB_API virtual DBStatus Sync(const std::vector &devices, SyncMode mode, + const Query &query, const SyncStatusCallback &onComplete, bool wait) = 0; + + DB_API virtual DBStatus RemoveDeviceData(const std::string &device) = 0; + + DB_API virtual DBStatus RemoveDeviceData(const std::string &device, const std::string &tableName) = 0; +}; +} // namespace DistributedDB +#endif // RELATIONAL_STORE_DELEGATE_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/relational/relational_store_manager.h b/mock/distributeddb/interfaces/include/relational/relational_store_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..9be7b6e69465a47be4f545ac0f1e87396318812b --- /dev/null +++ b/mock/distributeddb/interfaces/include/relational/relational_store_manager.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_STORE_MANAGER_H +#define RELATIONAL_STORE_MANAGER_H +#include +#include +#include + +#include "auto_launch_export.h" +#include "relational_store_delegate.h" +#include "store_types.h" + +namespace DistributedDB { +class RelationalStoreManager final { +public: + // Only calculate the table name with device hash, no guarantee for the table exists + DB_API static std::string GetDistributedTableName(const std::string &device, const std::string &tableName); + + DB_API RelationalStoreManager(const std::string &appId, const std::string &userId); + DB_API ~RelationalStoreManager() = default; + + RelationalStoreManager(const RelationalStoreManager &) = delete; + RelationalStoreManager(RelationalStoreManager &&) = delete; + RelationalStoreManager &operator=(const RelationalStoreManager &) = delete; + RelationalStoreManager &operator=(RelationalStoreManager &&) = delete; + + DB_API DBStatus OpenStore(const std::string &path, const std::string &storeId, + const RelationalStoreDelegate::Option &option, RelationalStoreDelegate *&delegate); + + DB_API DBStatus CloseStore(RelationalStoreDelegate *store); + + DB_API static void SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback); + + DB_API static std::string GetRelationalStoreIdentifier(const std::string &userId, const std::string &appId, + const std::string &storeId); + +private: + std::string appId_; + std::string userId_; +}; +} // namespace DistributedDB +#endif // RELATIONAL_STORE_MANAGER_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/relational/relational_store_sqlite_ext.h b/mock/distributeddb/interfaces/include/relational/relational_store_sqlite_ext.h new file mode 100644 index 0000000000000000000000000000000000000000..59f4d534236c2b4a1255f25991fa5b8c18585b49 --- /dev/null +++ b/mock/distributeddb/interfaces/include/relational/relational_store_sqlite_ext.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_STORE_EXT_H +#define RELATIONAL_STORE_EXT_H + +#define SQLITE3_HW_EXPORT_SYMBOLS + +// using the "sqlite3sym.h" in OHOS +#ifndef USE_SQLITE_SYMBOLS +#include "sqlite3.h" +#else +#include "sqlite3sym.h" +#endif + +// We extend the original purpose of the "sqlite3ext.h". +struct sqlite3_api_routines_relational { + int (*open)(const char *, sqlite3 **); + int (*open16)(const void *, sqlite3 **); + int (*open_v2)(const char *, sqlite3 **, int, const char *); +}; + +extern const struct sqlite3_api_routines_relational *sqlite3_export_relational_symbols; + +#ifdef sqlite3_open +#undef sqlite3_open +#endif +#define sqlite3_open sqlite3_export_relational_symbols->open + +#ifdef sqlite3_open16 +#undef sqlite3_open16 +#endif +#define sqlite3_open16 sqlite3_export_relational_symbols->open16 + +#ifdef sqlite3_open_v2 +#undef sqlite3_open_v2 +#endif +#define sqlite3_open_v2 sqlite3_export_relational_symbols->open_v2 + +#endif // RELATIONAL_STORE_EXT_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/relational/runtime_config.h b/mock/distributeddb/interfaces/include/relational/runtime_config.h new file mode 100644 index 0000000000000000000000000000000000000000..e6173f006b9e4d4d007602c51247d876daf9ac37 --- /dev/null +++ b/mock/distributeddb/interfaces/include/relational/runtime_config.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_CONFIG_H +#define RUNTIME_CONFIG_H + +#include +#include + +#include "iprocess_communicator.h" +#include "iprocess_system_api_adapter.h" +#include "store_types.h" +namespace DistributedDB { +class RuntimeConfig final { +public: + DB_API RuntimeConfig() = default; + DB_API ~RuntimeConfig() = default; + + DB_API static DBStatus SetProcessLabel(const std::string &appId, const std::string &userId); + + DB_API static DBStatus SetProcessCommunicator(const std::shared_ptr &inCommunicator); + + DB_API static DBStatus SetPermissionCheckCallback(const PermissionCheckCallbackV2 &callback); + + DB_API static DBStatus SetProcessSystemAPIAdapter(const std::shared_ptr &adapter); + + DB_API static void Dump(int fd, const std::vector &args); + +private: + static std::mutex communicatorMutex_; + static std::shared_ptr processCommunicator_; +}; +} // namespace DistributedDB + +#endif // RUNTIME_CONFIG_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/relational/store_changed_data.h b/mock/distributeddb/interfaces/include/relational/store_changed_data.h new file mode 100644 index 0000000000000000000000000000000000000000..3077d8d931f81dc723d5a2776e85cb3b75af845e --- /dev/null +++ b/mock/distributeddb/interfaces/include/relational/store_changed_data.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STORE_CHANGED_DATA_H +#define STORE_CHANGED_DATA_H + +#include +#include "store_types.h" + +namespace DistributedDB { +struct StoreProperty { + std::string userId; + std::string appId; + std::string storeId; +}; +class StoreChangedData { +public: + StoreChangedData() {} + DB_API virtual ~StoreChangedData() {} + + // Interface for Getting the device whose data changed. + DB_API virtual std::string GetDataChangeDevice() const = 0; + + // Interface for Getting the store whose data changed. + DB_API virtual void GetStoreProperty(StoreProperty &storeProperty) const = 0; +}; +} // namespace DistributedDB + +#endif // STORE_CHANGED_DATA_H diff --git a/mock/distributeddb/interfaces/include/relational/store_observer.h b/mock/distributeddb/interfaces/include/relational/store_observer.h new file mode 100644 index 0000000000000000000000000000000000000000..6072bbc694668ce585cee25cb21605691865c33e --- /dev/null +++ b/mock/distributeddb/interfaces/include/relational/store_observer.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STORE_OBSERVER_H +#define STORE_OBSERVER_H + +#include "store_changed_data.h" + +namespace DistributedDB { +class StoreObserver { +public: + virtual ~StoreObserver() {} + + // Databa change callback + virtual void OnChange(const StoreChangedData &data) = 0; +}; +} // namespace DistributedDB + +#endif // STORE_OBSERVER_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/include/store_types.h b/mock/distributeddb/interfaces/include/store_types.h new file mode 100644 index 0000000000000000000000000000000000000000..62fc4fa1989938cc04901dff2702b384f918d565 --- /dev/null +++ b/mock/distributeddb/interfaces/include/store_types.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_TYPE_H +#define KV_STORE_TYPE_H + +#include +#include +#include + +#include "types_export.h" + +namespace DistributedDB { +enum DBStatus { + DB_ERROR = -1, + OK = 0, + BUSY, + NOT_FOUND, + INVALID_ARGS, + TIME_OUT, + NOT_SUPPORT, + INVALID_PASSWD_OR_CORRUPTED_DB, + OVER_MAX_LIMITS, + INVALID_FILE, + NO_PERMISSION, + FILE_ALREADY_EXISTED, + SCHEMA_MISMATCH, + INVALID_SCHEMA, + READ_ONLY, + INVALID_VALUE_FIELDS, // invalid put value for json schema. + INVALID_FIELD_TYPE, // invalid put value field type for json schema. + CONSTRAIN_VIOLATION, // invalid put value constrain for json schema. + INVALID_FORMAT, // invalid put value format for json schema. + STALE, // new record is staler compared to the same key existed in db. + LOCAL_DELETED, // local data is deleted by the unpublish. + LOCAL_DEFEAT, // local data defeat the sync data while unpublish. + LOCAL_COVERED, // local data is covered by the sync data while unpublish. + INVALID_QUERY_FORMAT, + INVALID_QUERY_FIELD, + PERMISSION_CHECK_FORBID_SYNC, // permission check result , forbid sync. + ALREADY_SET, // already set. + COMM_FAILURE, // communicator may get some error. + EKEYREVOKED_ERROR, // EKEYREVOKED error when operating db file + SECURITY_OPTION_CHECK_ERROR, // such as remote device's SecurityOption not equal to local + SCHEMA_VIOLATE_VALUE, // Values already exist in dbFile do not match new schema + INTERCEPT_DATA_FAIL, // Interceptor push data failed. + LOG_OVER_LIMITS, // Log size is over the limits. + DISTRIBUTED_SCHEMA_NOT_FOUND, // the sync table is not a relational table + DISTRIBUTED_SCHEMA_CHANGED, // the schema was changed + MODE_MISMATCH, + NOT_ACTIVE, + USER_CHANGED, +}; + +struct KvStoreConfig { + std::string dataDir; +}; + +enum PragmaCmd { + AUTO_SYNC = 1, + SYNC_DEVICES = 2, + RM_DEVICE_DATA = 3, // remove the device data synced from remote by device name + PERFORMANCE_ANALYSIS_GET_REPORT, + PERFORMANCE_ANALYSIS_OPEN, + PERFORMANCE_ANALYSIS_CLOSE, + PERFORMANCE_ANALYSIS_SET_REPORTFILENAME, + GET_IDENTIFIER_OF_DEVICE, + GET_DEVICE_IDENTIFIER_OF_ENTRY, + GET_QUEUED_SYNC_SIZE, + SET_QUEUED_SYNC_LIMIT, + GET_QUEUED_SYNC_LIMIT, + SET_WIPE_POLICY, // set the policy of wipe remote stale data + RESULT_SET_CACHE_MODE, // Accept ResultSetCacheMode Type As PragmaData + RESULT_SET_CACHE_MAX_SIZE, // Allowed Int Type Range [1,16], Unit MB + SET_SYNC_RETRY, + SET_MAX_LOG_LIMIT, + EXEC_CHECKPOINT, +}; + +enum ResolutionPolicyType { + AUTO_LAST_WIN = 0, // resolve conflicts by timestamp(default value) + CUSTOMER_RESOLUTION = 1 // resolve conflicts by user +}; + +enum ObserverMode { + OBSERVER_CHANGES_NATIVE = 1, + OBSERVER_CHANGES_FOREIGN = 2, + OBSERVER_CHANGES_LOCAL_ONLY = 4, +}; + +enum SyncMode { + SYNC_MODE_PUSH_ONLY, + SYNC_MODE_PULL_ONLY, + SYNC_MODE_PUSH_PULL, +}; + +enum ConflictResolvePolicy { + LAST_WIN = 0, + DEVICE_COLLABORATION, +}; + +struct TableStatus { + std::string tableName; + DBStatus status; +}; +using KvStoreCorruptionHandler = std::function; +using StoreCorruptionHandler = std::function; +using SyncStatusCallback = std::function> &devicesMap)>; +} // namespace DistributedDB +#endif // KV_STORE_TYPE_H diff --git a/mock/distributeddb/interfaces/src/intercepted_data_impl.cpp b/mock/distributeddb/interfaces/src/intercepted_data_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bfb06d1571e25693a8ef4ef25beae0f3f4063b3 --- /dev/null +++ b/mock/distributeddb/interfaces/src/intercepted_data_impl.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "intercepted_data_impl.h" +#include "db_common.h" +#include "db_constant.h" +#include "generic_single_ver_kv_entry.h" +#include "parcel.h" +#include "version.h" + +namespace DistributedDB { +namespace { +bool CheckKey(const Key &key) +{ + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + LOGE("Key is too large:%zu.", key.size()); + return false; + } + return true; +} + +bool CheckValue(const Value &value, const std::function &checkSchema) +{ + if (value.size() > DBConstant::MAX_VALUE_SIZE) { + LOGE("Value is too large:%zu.", value.size()); + return false; + } + + if (checkSchema == nullptr) { + LOGE("Check schema failed, no check func."); + return false; + } + + int errCode = checkSchema(value); + if (errCode != E_OK) { + LOGE("Check schema failed, value is invalid:%d.", errCode); + return false; + } + return true; +} + +bool CheckLength(size_t len, size_t maxPacketSize) +{ + if (len > maxPacketSize) { + LOGE("Packet is too large:%zu.", len); + return false; + } + return true; +} +} // anonymous namespace + +InterceptedDataImpl::InterceptedDataImpl(std::vector dataItems, + const std::function &checkSchema) + : kvEntriesReady_(false), + isError_(false), + totalLength_(), + maxPacketSize_(), + checkSchema_(checkSchema), + dataItems_(dataItems), + kvEntries_(), + indexes_() +{ + totalLength_ = GenericSingleVerKvEntry::CalculateLens(dataItems, SOFTWARE_VERSION_CURRENT); + // New packet cannot exceed both twice the MTU and twice the original size. + // Besides, it cannot exceed 30 MB. + maxPacketSize_ = std::min(DBConstant::MAX_SYNC_BLOCK_SIZE, + std::max(totalLength_, static_cast(DBConstant::MAX_MTU_SIZE)) * 2); +} + +InterceptedDataImpl::~InterceptedDataImpl() +{} + +std::vector InterceptedDataImpl::GetEntries() +{ + if (!kvEntriesReady_) { + GetKvEntries(); + } + return kvEntries_; +} + +bool InterceptedDataImpl::CheckIndex(size_t index) +{ + if (!kvEntriesReady_) { + GetKvEntries(); + } + + if (index >= kvEntries_.size()) { + LOGE("Index is too large:%zu, size:%zu.", index, kvEntries_.size()); + return false; + } + return true; +} + +DBStatus InterceptedDataImpl::ModifyKey(size_t index, const Key &newKey) +{ + // Check index. + if (!CheckIndex(index)) { + isError_ = true; + return INVALID_ARGS; + } + + // Check key. + if (!CheckKey(newKey)) { + isError_ = true; + return INVALID_ARGS; + } + + // Check length. + const auto &oldKey = dataItems_[indexes_[index]]->GetKey(); + size_t newLength = totalLength_ - Parcel::GetVectorCharLen(oldKey) + Parcel::GetVectorCharLen(newKey); + if (!CheckLength(newLength, maxPacketSize_)) { + isError_ = true; + return INVALID_ARGS; + } + totalLength_ = newLength; + + // Modify data + auto entry = dataItems_[indexes_[index]]; + entry->SetKey(newKey); + Key hashKey; + int errCode = DBCommon::CalcValueHash(newKey, hashKey); + if (errCode != E_OK) { + LOGE("Calc hashkey failed."); + isError_ = true; + return INVALID_ARGS; + } + entry->SetHashKey(hashKey); + return OK; +} + +DBStatus InterceptedDataImpl::ModifyValue(size_t index, const Value &newValue) +{ + // Check index. + if (!CheckIndex(index)) { + isError_ = true; + return INVALID_ARGS; + } + + // Check value. + if (!CheckValue(newValue, checkSchema_)) { + isError_ = true; + return INVALID_ARGS; + } + + // Check length. + const auto &oldValue = dataItems_[indexes_[index]]->GetValue(); + size_t newLength = totalLength_ - Parcel::GetVectorCharLen(oldValue) + Parcel::GetVectorCharLen(newValue); + if (!CheckLength(newLength, maxPacketSize_)) { + isError_ = true; + return INVALID_ARGS; + } + totalLength_ = newLength; + + // Modify data + auto entry = dataItems_[indexes_[index]]; + entry->SetValue(newValue); + return OK; +} + +bool InterceptedDataImpl::IsError() const +{ + return isError_; +} + +void InterceptedDataImpl::GetKvEntries() +{ + for (size_t i = 0; i < dataItems_.size(); ++i) { + const auto &kvEntry = dataItems_[i]; + if ((kvEntry->GetFlag() & DataItem::DELETE_FLAG) == 0) { // For deleted data, do not modify. + kvEntries_.push_back({ kvEntry->GetKey(), kvEntry->GetValue() }); + indexes_.push_back(i); + } + } + kvEntriesReady_ = true; +} +} // namespace DistributedDB + diff --git a/mock/distributeddb/interfaces/src/intercepted_data_impl.h b/mock/distributeddb/interfaces/src/intercepted_data_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..30fefe4bbefea8beff25eb154a596efe9aedf1f3 --- /dev/null +++ b/mock/distributeddb/interfaces/src/intercepted_data_impl.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERCEPTED_DATA_IMPL_H +#define INTERCEPTED_DATA_IMPL_H + +#include +#include +#include + +#include "intercepted_data.h" +#include "macro_utils.h" +#include "single_ver_kv_entry.h" +#include "store_types.h" +#include "types_export.h" + +namespace DistributedDB { +class InterceptedDataImpl : public InterceptedData { +public: + InterceptedDataImpl(std::vector dataItems, const std::function &checkSchema); + virtual ~InterceptedDataImpl(); + DISABLE_COPY_ASSIGN_MOVE(InterceptedDataImpl); + + std::vector GetEntries() override; + DBStatus ModifyKey(size_t index, const Key &newKey) override; + DBStatus ModifyValue(size_t index, const Value &newValue) override; + + bool IsError() const; + +private: + bool CheckIndex(size_t index); + void GetKvEntries(); + + bool kvEntriesReady_; + bool isError_; + size_t totalLength_; + size_t maxPacketSize_; + std::function checkSchema_; + std::vector dataItems_; + std::vector kvEntries_; + std::vector indexes_; +}; +} // namespace DistributedDB +#endif // INTERCEPTED_DATA_IMPL_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/kv_store_changed_data_impl.cpp b/mock/distributeddb/interfaces/src/kv_store_changed_data_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f57e9c7d32409ae22f5d96a1eb16c17c51a1171 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_changed_data_impl.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kv_store_changed_data_impl.h" + +namespace DistributedDB { +KvStoreChangedDataImpl::~KvStoreChangedDataImpl() +{ + observerData_ = nullptr; +} + +const std::list &KvStoreChangedDataImpl::GetEntriesInserted() const +{ + std::lock_guard lock(mutex_); + if (insertedEntries_.empty() && observerData_ != nullptr) { + int errCode; + insertedEntries_ = observerData_->GetInsertedEntries(errCode); + } + + return insertedEntries_; +} + +const std::list &KvStoreChangedDataImpl::GetEntriesUpdated() const +{ + std::lock_guard lock(mutex_); + if (updatedEntries_.empty() && observerData_ != nullptr) { + int errCode; + updatedEntries_ = observerData_->GetUpdatedEntries(errCode); + } + + return updatedEntries_; +} + +const std::list &KvStoreChangedDataImpl::GetEntriesDeleted() const +{ + std::lock_guard lock(mutex_); + if (deletedEntries_.empty() && observerData_ != nullptr) { + int errCode; + deletedEntries_ = observerData_->GetDeletedEntries(errCode); + } + + return deletedEntries_; +} + +bool KvStoreChangedDataImpl::IsCleared() const +{ + if (observerData_ != nullptr) { + return observerData_->IsCleared(); + } + + return false; +} +} // namespace DistributedDB + diff --git a/mock/distributeddb/interfaces/src/kv_store_changed_data_impl.h b/mock/distributeddb/interfaces/src/kv_store_changed_data_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..417dbd2b8b11982480d5de179c7c13133b135935 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_changed_data_impl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_CHANGED_DATA_IMPL_H +#define KV_STORE_CHANGED_DATA_IMPL_H + +#include + +#include "kv_store_changed_data.h" +#include "kvdb_commit_notify_data.h" + +namespace DistributedDB { +class KvStoreChangedDataImpl : public KvStoreChangedData { +public: + explicit KvStoreChangedDataImpl(const KvDBCommitNotifyData *observerData) : observerData_(observerData) {} + virtual ~KvStoreChangedDataImpl(); + + DISABLE_COPY_ASSIGN_MOVE(KvStoreChangedDataImpl); + + const std::list &GetEntriesInserted() const override; + + const std::list &GetEntriesUpdated() const override; + + const std::list &GetEntriesDeleted() const override; + + bool IsCleared() const override; + +private: + const KvDBCommitNotifyData *observerData_; + mutable std::mutex mutex_; + mutable std::list insertedEntries_; + mutable std::list updatedEntries_; + mutable std::list deletedEntries_; +}; +} // namespace DistributedDB + +#endif // KV_STORE_CHANGED_DATA_IMPL_H + diff --git a/mock/distributeddb/interfaces/src/kv_store_delegate_impl.cpp b/mock/distributeddb/interfaces/src/kv_store_delegate_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d1a0be4449550aee11b767ec1df405e40c18e60a --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_delegate_impl.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "kv_store_delegate_impl.h" + +#include +#include + +#include "platform_specific.h" +#include "log_print.h" +#include "param_check_utils.h" +#include "db_constant.h" +#include "db_errno.h" +#include "db_types.h" +#include "kv_store_errno.h" +#include "kvdb_pragma.h" +#include "kv_store_observer.h" +#include "kvdb_manager.h" +#include "kv_store_snapshot_delegate_impl.h" +#include "kv_store_changed_data_impl.h" + +namespace DistributedDB { +namespace { + const std::string INVALID_CONNECTION = "[KvStoreDelegate] Invalid connection for operation"; +} +KvStoreDelegateImpl::KvStoreDelegateImpl(IKvDBConnection *conn, const std::string &storeId) + : conn_(conn), + storeId_(storeId), + releaseFlag_(false) +{} + +KvStoreDelegateImpl::~KvStoreDelegateImpl() +{ + if (!releaseFlag_) { + LOGF("[KvStoreDelegate] can not release object directly"); + return; + } + + LOGI("[KvStoreDelegate] deconstruct"); + conn_ = nullptr; +} + +DBStatus KvStoreDelegateImpl::Put(const Key &key, const Value &value) +{ + if (conn_ != nullptr) { + IOption option; + int errCode = conn_->Put(option, key, value); + if (errCode == E_OK) { + return OK; + } + + LOGE("[KvStoreDelegate] Put data failed:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreDelegateImpl::PutBatch(const std::vector &entries) +{ + if (conn_ != nullptr) { + IOption option; + int errCode = conn_->PutBatch(option, entries); + if (errCode == E_OK) { + return OK; + } + + LOGE("[KvStoreDelegate] Put batch data failed:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreDelegateImpl::Delete(const Key &key) +{ + if (conn_ != nullptr) { + IOption option; + int errCode = conn_->Delete(option, key); + if (errCode == E_OK || errCode == -E_NOT_FOUND) { + return OK; + } + + LOGE("[KvStoreDelegate] Delete data failed:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreDelegateImpl::DeleteBatch(const std::vector &keys) +{ + if (conn_ != nullptr) { + IOption option; + int errCode = conn_->DeleteBatch(option, keys); + if (errCode == E_OK || errCode == -E_NOT_FOUND) { + return OK; + } + + LOGE("[KvStoreDelegate] Delete batch data failed:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreDelegateImpl::Clear() +{ + if (conn_ != nullptr) { + IOption option; + int errCode = conn_->Clear(option); + if (errCode == E_OK) { + return OK; + } + + LOGE("[KvStoreDelegate] Clear data failed:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +std::string KvStoreDelegateImpl::GetStoreId() const +{ + return storeId_; +} + +void KvStoreDelegateImpl::GetKvStoreSnapshot(KvStoreObserver *observer, + const std::function &callback) +{ + if (!callback) { + LOGE("[KvStoreDelegate] Invalid callback for snapshot!"); + return; + } + + if (conn_ != nullptr) { + if (observer != nullptr && RegisterObserver(observer) != E_OK) { + LOGE("[KvStoreDelegate][GetSnapshot] Register observer failed!"); + callback(DB_ERROR, nullptr); + return; + } + + IKvDBSnapshot *snapshot = nullptr; + int errCode = conn_->GetSnapshot(snapshot); + if (errCode == E_OK) { + auto snapshotDelegate = new (std::nothrow) KvStoreSnapshotDelegateImpl(snapshot, observer); + if (snapshotDelegate != nullptr) { + callback(OK, snapshotDelegate); + return; + } + conn_->ReleaseSnapshot(snapshot); + snapshot = nullptr; + } + + // UnRegister the registered observer. + errCode = UnRegisterObserver(observer); + if (errCode != E_OK) { + LOGE("[KvStoreDelegate][GetSnapshot] UnRegister observer failed:%d!", errCode); + } + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + callback(DB_ERROR, nullptr); +} + +DBStatus KvStoreDelegateImpl::ReleaseKvStoreSnapshot(KvStoreSnapshotDelegate *&snapshotDelegate) +{ + if (conn_ != nullptr && snapshotDelegate != nullptr) { + KvStoreObserver *observer = nullptr; + (static_cast(snapshotDelegate))->GetObserver(observer); + if (observer != nullptr && UnRegisterObserver(observer) != E_OK) { + LOGE("[KvStoreDelegate][ReleaseSnapshot] UnRegistObserver failed!"); + return DB_ERROR; + } + + IKvDBSnapshot *snapshot = nullptr; + (static_cast(snapshotDelegate))->GetSnapshot(snapshot); + conn_->ReleaseSnapshot(snapshot); + snapshot = nullptr; + delete snapshotDelegate; + snapshotDelegate = nullptr; + return OK; + } + + return DB_ERROR; +} + +DBStatus KvStoreDelegateImpl::RegisterObserver(KvStoreObserver *observer) +{ + if (observer == nullptr) { + return INVALID_ARGS; + } + + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + std::lock_guard lockGuard(observerMapLock_); + if (observerMap_.find(observer) != observerMap_.end()) { + LOGE("[KvStoreDelegate] Observer has been already registered!"); + return DB_ERROR; + } + + Key key; + int errCode = E_OK; + KvDBObserverHandle *observerHandle = conn_->RegisterObserver( + static_cast(DATABASE_COMMIT_EVENT), + key, + [observer](const KvDBCommitNotifyData &ptr) { + KvStoreChangedDataImpl data(&ptr); + observer->OnChange(data); + }, + errCode); + + if (errCode != E_OK || observerHandle == nullptr) { + LOGE("[KvStoreDelegate] Register listener failed:%d!", errCode); + return DB_ERROR; + } + + observerMap_.insert(std::pair(observer, observerHandle)); + return OK; +} + +// Unregister a data change observer +DBStatus KvStoreDelegateImpl::UnRegisterObserver(const KvStoreObserver *observer) +{ + if (observer == nullptr) { + return INVALID_ARGS; + } + + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + std::lock_guard lockGuard(observerMapLock_); + auto iter = observerMap_.find(observer); + if (iter == observerMap_.end()) { + LOGE("[KvStoreDelegate] observer has not been registered!"); + return NOT_FOUND; + } + + const KvDBObserverHandle *observerHandle = iter->second; + int errCode = conn_->UnRegisterObserver(observerHandle); + if (errCode != E_OK) { + LOGE("[KvStoreDelegate] UnRegister observer failed:%d!", errCode); + return DB_ERROR; + } + observerMap_.erase(iter); + return OK; +} + +DBStatus KvStoreDelegateImpl::StartTransaction() +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->StartTransaction(); + if (errCode != E_OK) { + LOGE("[KvStoreDelegate] StartTransaction failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreDelegateImpl::Commit() +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->Commit(); + if (errCode != E_OK) { + LOGE("[KvStoreDelegate] Commit failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreDelegateImpl::Rollback() +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->RollBack(); + if (errCode != E_OK) { + LOGE("[KvStoreDelegate] Rollback failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreDelegateImpl::SetConflictResolutionPolicy(ResolutionPolicyType type, + const ConflictResolution &resolution) +{ + if (type == AUTO_LAST_WIN) { + return OK; + } + + if (type == CUSTOMER_RESOLUTION && resolution != nullptr) { + return OK; + } + LOGE("[KvStoreDelegate] Invalid conflict resolution policy:%d", type); + return DB_ERROR; +} + +DBStatus KvStoreDelegateImpl::Rekey(const CipherPassword &password) +{ + if (conn_ != nullptr) { + int errCode = conn_->Rekey(password); + if (errCode == E_OK) { + return OK; + } + + LOGE("[KvStoreDelegate] rekey failed:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreDelegateImpl::Export(const std::string &filePath, const CipherPassword &passwd) +{ + std::string fileDir; + std::string fileName; + OS::SplitFilePath(filePath, fileDir, fileName); + + std::string canonicalUrl; + if (!ParamCheckUtils::CheckDataDir(fileDir, canonicalUrl)) { + return INVALID_ARGS; + } + + if (!OS::CheckPathExistence(canonicalUrl)) { + return NO_PERMISSION; + } + + canonicalUrl = canonicalUrl + "/" + fileName; + if (OS::CheckPathExistence(canonicalUrl)) { + LOGE("[KvStoreDelegate] The exported file has already been existed"); + return FILE_ALREADY_EXISTED; + } + + if (conn_ != nullptr) { + int errCode = conn_->Export(canonicalUrl, passwd); + if (errCode == E_OK) { + return OK; + } + LOGE("[KvStoreDelegate] Export failed:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreDelegateImpl::Import(const std::string &filePath, const CipherPassword &passwd) +{ + std::string fileDir; + std::string fileName; + OS::SplitFilePath(filePath, fileDir, fileName); + + std::string canonicalUrl; + if (!ParamCheckUtils::CheckDataDir(fileDir, canonicalUrl)) { + return INVALID_ARGS; + } + + canonicalUrl = canonicalUrl + "/" + fileName; + if (!OS::CheckPathExistence(canonicalUrl)) { + LOGE("[KvStoreDelegate] The imported file not existed:%d", errno); + return INVALID_FILE; + } + + if (conn_ != nullptr) { + int errCode = conn_->Import(canonicalUrl, passwd); + if (errCode == E_OK) { + return OK; + } + LOGE("[KvStoreDelegate] Import failed:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +void KvStoreDelegateImpl::SetReleaseFlag(bool flag) +{ + releaseFlag_ = flag; +} + +DBStatus KvStoreDelegateImpl::Close() +{ + if (conn_ != nullptr) { + int errCode = KvDBManager::ReleaseDatabaseConnection(conn_); + if (errCode == -E_BUSY) { + LOGW("[KvStoreDelegate] busy for close"); + return BUSY; + } + + LOGI("[KvStoreDelegate] Close"); + conn_ = nullptr; + } + return OK; +} + +DBStatus KvStoreDelegateImpl::Pragma(PragmaCmd cmd, PragmaData ¶mData) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + int errCode; + switch (cmd) { + case PERFORMANCE_ANALYSIS_GET_REPORT: + errCode = conn_->Pragma(PRAGMA_PERFORMANCE_ANALYSIS_GET_REPORT, paramData); + break; + case PERFORMANCE_ANALYSIS_OPEN: + errCode = conn_->Pragma(PRAGMA_PERFORMANCE_ANALYSIS_OPEN, paramData); + break; + case PERFORMANCE_ANALYSIS_CLOSE: + errCode = conn_->Pragma(PRAGMA_PERFORMANCE_ANALYSIS_CLOSE, paramData); + break; + case PERFORMANCE_ANALYSIS_SET_REPORTFILENAME: + errCode = conn_->Pragma(PRAGMA_PERFORMANCE_ANALYSIS_SET_REPORTFILENAME, paramData); + break; + default: + errCode = -E_NOT_SUPPORT; + break; + } + + if (errCode != E_OK) { + LOGE("[KvStoreDelegate] Pragma failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/interfaces/src/kv_store_delegate_impl.h b/mock/distributeddb/interfaces/src/kv_store_delegate_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..b2fcdea73758ae5eb97ff3b35597380b1731c763 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_delegate_impl.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_DELEGATE_IMPL_H +#define KV_STORE_DELEGATE_IMPL_H + +#ifndef OMIT_MULTI_VER +#include +#include +#include + +#include "store_types.h" +#include "ikvdb_connection.h" +#include "ikvdb_factory.h" +#include "kv_store_delegate.h" + +namespace DistributedDB { +class KvStoreDelegateImpl final : public KvStoreDelegate { +public: + KvStoreDelegateImpl(IKvDBConnection *conn, const std::string &storeId); + ~KvStoreDelegateImpl(); + + DISABLE_COPY_ASSIGN_MOVE(KvStoreDelegateImpl); + + // Used to Put a k-v pair to the kvstore. + // Return OK if the operation is successful. + DBStatus Put(const Key &key, const Value &value) override; + + // Used to Put a vector contains k-v pairs to the kvstore. + // Return OK if the operation is successful. + DBStatus PutBatch(const std::vector &entries) override; + + // Delete a record with the given key. + // Return OK if the operation is successful. + DBStatus Delete(const Key &key) override; + + // Batch delete records with the given keys. + // Return OK if the operation is successful. + DBStatus DeleteBatch(const std::vector &keys) override; + + // Delete all record of th kvstore. + // Return OK if the operation is successful. + DBStatus Clear() override; + + // Return a storeId of the KvStore instance + std::string GetStoreId() const override; + + // Get a snapshot of the kvstore. the observer is used to notify data changed, it can be null. + // return value is DBStatus and KvStoreSnapshotDelegate*, these values will be passed to the callback. + void GetKvStoreSnapshot(KvStoreObserver *observer, + const std::function &callback) override; + + // Release a snapshot, it will return OK if the operation is successful. + DBStatus ReleaseKvStoreSnapshot(KvStoreSnapshotDelegate *&snapshotDelegate) override; + + // Register a data change observer + DBStatus RegisterObserver(KvStoreObserver *observer) override; + + // Unregister a data change observer + DBStatus UnRegisterObserver(const KvStoreObserver *observer) override; + + // Start a transaction + DBStatus StartTransaction() override; + + // Commit a transaction + DBStatus Commit() override; + + // Rollback a transaction + DBStatus Rollback() override; + + // Used to set the resolution policy for conflicts. + // Return OK if operation is successful. + DBStatus SetConflictResolutionPolicy(ResolutionPolicyType type, const ConflictResolution &resolution) override; + + // Rekey the database. + DBStatus Rekey(const CipherPassword &password) override; + + // Empty passwords represent non-encrypted files. + // Export existing database files to a specified database file in the specified directory. + DBStatus Export(const std::string &filePath, const CipherPassword &passwd) override; + + // Import the existing database files to the specified database file in the specified directory. + DBStatus Import(const std::string &filePath, const CipherPassword &passwd) override; + + // Set release flag, KvStoreManagerDelegate will set when release the kvstore + void SetReleaseFlag(bool flag); + + // Close the KvStoreDelegateImpl + DBStatus Close(); + + // Special pragma interface, see PragmaCmd and PragmaData, + DBStatus Pragma(PragmaCmd cmd, PragmaData ¶mData) override; + +private: + IKvDBConnection *conn_; + std::string storeId_; + bool releaseFlag_; + std::mutex observerMapLock_; + std::map observerMap_; +}; +} // namespace DistributedDB + +#endif // KV_STORE_DELEGATE_IMPL_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/kv_store_delegate_manager.cpp b/mock/distributeddb/interfaces/src/kv_store_delegate_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..add0fadb80c34261dda2338922b4347a09215f03 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_delegate_manager.cpp @@ -0,0 +1,614 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kv_store_delegate_manager.h" + +#include +#include +#include +#include +#include + +#include "db_constant.h" +#include "platform_specific.h" +#include "log_print.h" +#include "db_common.h" +#include "db_dfx_adapter.h" +#include "kv_store_errno.h" +#include "kvdb_pragma.h" +#include "kvdb_properties.h" +#include "kvdb_manager.h" +#include "kv_store_nb_delegate_impl.h" +#include "network_adapter.h" +#include "runtime_context.h" +#include "param_check_utils.h" +#include "auto_launch.h" +#ifndef OMIT_MULTI_VER +#include "kv_store_delegate_impl.h" +#endif + +namespace DistributedDB { +const std::string KvStoreDelegateManager::DEFAULT_PROCESS_APP_ID = "default"; +std::mutex KvStoreDelegateManager::communicatorMutex_; +std::shared_ptr KvStoreDelegateManager::processCommunicator_ = nullptr; +std::mutex KvStoreDelegateManager::multiUserMutex_; + +namespace { + const int GET_CONNECT_RETRY = 3; + const int RETRY_GET_CONN_INTER = 30; + + IKvDBConnection *GetOneConnectionWithRetry(const KvDBProperties &properties, int &errCode) + { + for (int i = 0; i < GET_CONNECT_RETRY; i++) { + auto conn = KvDBManager::GetDatabaseConnection(properties, errCode); + if (conn != nullptr) { + return conn; + } + if (errCode == -E_STALE) { + std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_GET_CONN_INTER)); + } else { + return nullptr; + } + } + return nullptr; + } + + DBStatus CheckAndGetSchema(bool isMemoryDb, const std::string &schema, SchemaObject &schemaObj) + { + if (isMemoryDb && !schema.empty()) { + LOGW("[KvStoreDelegateManager] memory database doesn't support the schema."); + return NOT_SUPPORT; + } + if (schema.empty()) { + return OK; + } + schemaObj.ParseFromSchemaString(schema); + if (!schemaObj.IsSchemaValid()) { + return INVALID_SCHEMA; + } + return OK; + } + + void InitPropWithNbOption(KvDBProperties &properties, const std::string &storePath, + const SchemaObject &schema, const KvStoreNbDelegate::Option &option) + { + properties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, option.createIfNecessary); + properties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + properties.SetBoolProp(KvDBProperties::MEMORY_MODE, option.isMemoryDb); + properties.SetBoolProp(KvDBProperties::ENCRYPTED_MODE, option.isEncryptedDb); + if (!option.isMemoryDb) { // memory db ignore store path + properties.SetStringProp(KvDBProperties::DATA_DIR, storePath); + } + properties.SetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, option.createDirByStoreIdOnly); + properties.SetSchema(schema); + properties.SetBoolProp(KvDBProperties::CHECK_INTEGRITY, option.isNeedIntegrityCheck); + properties.SetBoolProp(KvDBProperties::RM_CORRUPTED_DB, option.isNeedRmCorruptedDb); + if (RuntimeContext::GetInstance()->IsProcessSystemApiAdapterValid()) { + properties.SetIntProp(KvDBProperties::SECURITY_LABEL, option.secOption.securityLabel); + properties.SetIntProp(KvDBProperties::SECURITY_FLAG, option.secOption.securityFlag); + } + properties.SetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, option.conflictResolvePolicy); + + if (option.isEncryptedDb) { + properties.SetPassword(option.cipher, option.passwd); + } + properties.SetBoolProp(KvDBProperties::COMPRESS_ON_SYNC, option.isNeedCompressOnSync); + if (option.isNeedCompressOnSync) { + properties.SetIntProp(KvDBProperties::COMPRESSION_RATE, + ParamCheckUtils::GetValidCompressionRate(option.compressionRate)); + } + properties.SetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, option.syncDualTupleMode); + } + + bool CheckObserverConflictParam(const KvStoreNbDelegate::Option &option) + { + if ((option.notifier && !ParamCheckUtils::CheckConflictNotifierType(option.conflictType)) || + (!option.notifier && option.conflictType != 0)) { + LOGE("Invalid conflict type, conflict type is [%d]", option.conflictType); + return false; + } + if ((option.observer != nullptr && !ParamCheckUtils::CheckObserver(option.key, option.mode)) || + (option.observer == nullptr && (!option.key.empty() || option.mode != 0))) { + LOGE("Invalid observer param, observer mode is [%u]", option.mode); + return false; + } + return true; + } + +#ifndef OMIT_MULTI_VER + void InitPropWithOption(KvDBProperties &properties, const std::string &storePath, + const KvStoreDelegate::Option &option) + { + properties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, option.createIfNecessary); + properties.SetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, option.createDirByStoreIdOnly); + properties.SetIntProp(KvDBProperties::DATABASE_TYPE, + ((option.localOnly == true) ? KvDBProperties::LOCAL_TYPE : KvDBProperties::MULTI_VER_TYPE)); + properties.SetBoolProp(KvDBProperties::MEMORY_MODE, false); + properties.SetBoolProp(KvDBProperties::ENCRYPTED_MODE, option.isEncryptedDb); + properties.SetStringProp(KvDBProperties::DATA_DIR, storePath); + if (option.isEncryptedDb) { + properties.SetPassword(option.cipher, option.passwd); + } + } +#endif +} + +KvStoreDelegateManager::KvStoreDelegateManager(const std::string &appId, const std::string &userId) + : appId_(appId), + userId_(userId) +{} + +KvStoreDelegateManager::~KvStoreDelegateManager() {} + +DBStatus KvStoreDelegateManager::SetKvStoreConfig(const KvStoreConfig &kvStoreConfig) +{ + std::string canonicalDir; + if (!IsDataDirSafe(kvStoreConfig.dataDir, canonicalDir)) { + return INVALID_ARGS; + } + if (!OS::CheckPathExistence(canonicalDir)) { + LOGE("[KvStoreMgr] Data dir doesn't exist or no perm"); + return INVALID_ARGS; + } + { + std::lock_guard lock(mutex_); + kvStoreConfig_ = kvStoreConfig; + kvStoreConfig_.dataDir = canonicalDir; + } + return OK; +} + +#ifndef OMIT_MULTI_VER +void KvStoreDelegateManager::GetKvStore(const std::string &storeId, const KvStoreDelegate::Option &option, + const std::function &callback) +{ + if (!callback) { + LOGE("[KvStoreMgr] Invalid callback for kv store!"); + return; + } + + // Multi version and local database mode not allow the creation of a memory database + if (!ParamCheckUtils::CheckStoreParameter(storeId, appId_, userId_) || GetKvStorePath().empty()) { + callback(INVALID_ARGS, nullptr); + return; + } + + if (option.isEncryptedDb) { + if (!ParamCheckUtils::CheckEncryptedParameter(option.cipher, option.passwd)) { + callback(INVALID_ARGS, nullptr); + return; + } + } + + KvDBProperties properties; + InitPropWithOption(properties, GetKvStorePath(), option); + DBCommon::SetDatabaseIds(properties, appId_, userId_, storeId); + + int errCode; + IKvDBConnection *conn = GetOneConnectionWithRetry(properties, errCode); + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + DBDfxAdapter::ReportFault( { DBDfxAdapter::EVENT_OPEN_DATABASE_FAILED, userId_, appId_, storeId, errCode } ); + } + if (conn == nullptr) { + DBStatus status = TransferDBErrno(errCode); + callback(status, nullptr); + return; + } + + auto kvStore = new (std::nothrow) KvStoreDelegateImpl(conn, storeId); + if (kvStore == nullptr) { + LOGE("[KvStoreMgr] Failed to alloc the delegate"); + conn->Close(); + conn = nullptr; + callback(DB_ERROR, nullptr); + return; + } + callback(OK, kvStore); +} +#endif + +DBStatus KvStoreDelegateManager::SetObserverNotifier(KvStoreNbDelegate *kvStore, + const KvStoreNbDelegate::Option &option) +{ + DBStatus status; + if (option.observer != nullptr) { + status = kvStore->RegisterObserver(option.key, option.mode, option.observer); + if (status != OK) { + LOGE("[KvStoreMgr] RegisterObserver failed."); + return status; + } + } + if (option.notifier != nullptr) { + status = kvStore->SetConflictNotifier(option.conflictType, option.notifier); + if (status != OK) { + LOGE("[KvStoreMgr] SetConflictNotifier failed."); + return status; + } + } + return OK; +} + +bool KvStoreDelegateManager::GetKvStoreParamCheck(const std::string &storeId, const KvStoreNbDelegate::Option &option, + const std::function &callback) const +{ + if (!callback) { + LOGE("[KvStoreMgr] Invalid callback for kv store"); + return false; + } + if (!ParamCheckUtils::CheckStoreParameter(storeId, appId_, userId_) || + (GetKvStorePath().empty() && !option.isMemoryDb)) { + LOGE("[KvStoreMgr] Invalid id or path info for the store"); + callback(INVALID_ARGS, nullptr); + return false; + } + + // check if want an encrypted db + if (option.isEncryptedDb) { + if (option.isMemoryDb) { + LOGE("Memory db not support encrypt!"); + callback(NOT_SUPPORT, nullptr); + return false; + } + if (!ParamCheckUtils::CheckEncryptedParameter(option.cipher, option.passwd)) { + callback(INVALID_ARGS, nullptr); + return false; + } + } + // check secOption + if (!option.isMemoryDb) { + if (!ParamCheckUtils::CheckSecOption(option.secOption)) { + callback(INVALID_ARGS, nullptr); + return false; + } + } else { + if (option.secOption.securityLabel != SecurityLabel::NOT_SET || + option.secOption.securityFlag != 0) { + LOGE("Memory db has no physical files, Is not controlled by security labels, so not support set labels"); + callback(INVALID_ARGS, nullptr); + return false; + } + } + + if (!CheckObserverConflictParam(option)) { + callback(INVALID_ARGS, nullptr); + return false; + } + return true; +} + +void KvStoreDelegateManager::GetKvStore(const std::string &storeId, const KvStoreNbDelegate::Option &option, + const std::function &callback) +{ + if (!GetKvStoreParamCheck(storeId, option, callback)) { + return; + } + // check if schema is supported and valid + SchemaObject schema; + DBStatus retCode = CheckAndGetSchema(option.isMemoryDb, option.schema, schema); + if (retCode != OK) { + callback(retCode, nullptr); + return; + } + KvDBProperties properties; + InitPropWithNbOption(properties, GetKvStorePath(), schema, option); + DBCommon::SetDatabaseIds(properties, appId_, userId_, storeId); + + int errCode; + IKvDBConnection *conn = GetOneConnectionWithRetry(properties, errCode); + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + DBDfxAdapter::ReportFault( { DBDfxAdapter::EVENT_OPEN_DATABASE_FAILED, userId_, appId_, storeId, errCode } ); + } + DBStatus status = TransferDBErrno(errCode); + if (conn == nullptr) { + callback(status, nullptr); + return; + } + + auto kvStore = new (std::nothrow) KvStoreNbDelegateImpl(conn, storeId); + if (kvStore == nullptr) { + conn->Close(); + conn = nullptr; + callback(DB_ERROR, nullptr); + return; + } + + status = SetObserverNotifier(kvStore, option); + if (status != OK) { + CloseKvStore(kvStore); + callback(status, nullptr); + return; + } + + bool enAutoSync = false; + (void)conn->Pragma(PRAGMA_AUTO_SYNC, static_cast(&enAutoSync)); + + SecurityOption secOption = option.secOption; + (void)conn->Pragma(PRAGMA_TRIGGER_TO_MIGRATE_DATA, &secOption); + + callback(OK, kvStore); +} + +#ifndef OMIT_MULTI_VER +DBStatus KvStoreDelegateManager::CloseKvStore(KvStoreDelegate *kvStore) +{ + if (kvStore == nullptr) { + return INVALID_ARGS; + } + + auto kvStoreImpl = static_cast(kvStore); + DBStatus status = kvStoreImpl->Close(); + if (status == BUSY) { + LOGD("DelegateImpl is busy now."); + return BUSY; + } + + kvStoreImpl->SetReleaseFlag(true); + delete kvStore; + kvStore = nullptr; + return OK; +} +#endif + +DBStatus KvStoreDelegateManager::CloseKvStore(KvStoreNbDelegate *kvStore) +{ + if (kvStore == nullptr) { + return INVALID_ARGS; + } + + auto kvStoreImpl = static_cast(kvStore); + DBStatus status = kvStoreImpl->Close(); + if (status == BUSY) { + LOGD("NbDelegateImpl is busy now."); + return BUSY; + } + kvStoreImpl->SetReleaseFlag(true); + delete kvStore; + kvStore = nullptr; + return OK; +} + +DBStatus KvStoreDelegateManager::DeleteKvStore(const std::string &storeId) +{ + if (!ParamCheckUtils::IsStoreIdSafe(storeId) || GetKvStorePath().empty()) { + LOGE("Invalid store info for deleting"); + return INVALID_ARGS; + } + + KvDBProperties properties; + properties.SetStringProp(KvDBProperties::DATA_DIR, GetKvStorePath()); + DBCommon::SetDatabaseIds(properties, appId_, userId_, storeId); + int errCode = KvDBManager::RemoveDatabase(properties); + if (errCode == E_OK) { + LOGI("Database deleted successfully!"); + return OK; + } + LOGE("Delete the kv store error:%d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreDelegateManager::SetProcessLabel(const std::string &appId, const std::string &userId) +{ + if (appId.size() > DBConstant::MAX_APP_ID_LENGTH || appId.empty() || + userId.size() > DBConstant::MAX_USER_ID_LENGTH || userId.empty()) { + LOGE("Invalid app or user info[%zu]-[%zu]", appId.length(), userId.length()); + return INVALID_ARGS; + } + + int errCode = KvDBManager::SetProcessLabel(appId, userId); + if (errCode != E_OK) { + LOGE("Failed to set the process label:%d", errCode); + return DB_ERROR; + } + return OK; +} + +DBStatus KvStoreDelegateManager::SetProcessCommunicator(const std::shared_ptr &inCommunicator) +{ + std::lock_guard lock(communicatorMutex_); + if (processCommunicator_ != nullptr) { + LOGE("processCommunicator_ is not null!"); + return DB_ERROR; + } + + std::string processLabel = RuntimeContext::GetInstance()->GetProcessLabel(); + if (processLabel.empty()) { + LOGE("ProcessLabel is not set!"); + return DB_ERROR; + } + + NetworkAdapter *adapter = new (std::nothrow) NetworkAdapter(processLabel, inCommunicator); + if (adapter == nullptr) { + LOGE("New NetworkAdapter failed!"); + return DB_ERROR; + } + processCommunicator_ = inCommunicator; + if (RuntimeContext::GetInstance()->SetCommunicatorAdapter(adapter) != E_OK) { + LOGE("SetProcessCommunicator not support!"); + delete adapter; + return DB_ERROR; + } + KvDBManager::RestoreSyncableKvStore(); + return OK; +} + +DBStatus KvStoreDelegateManager::GetKvStoreDiskSize(const std::string &storeId, uint64_t &size) +{ + std::string dataDir = GetKvStorePath(); + if (!ParamCheckUtils::CheckStoreParameter(storeId, appId_, userId_)) { + LOGE("[KvStoreMgr] Invalid store info for size"); + return INVALID_ARGS; + } + KvDBProperties properties; + properties.SetStringProp(KvDBProperties::DATA_DIR, dataDir); + DBCommon::SetDatabaseIds(properties, appId_, userId_, storeId); + int errCode = KvDBManager::CalculateKvStoreSize(properties, size); + if (errCode != E_OK) { + if (errCode == -E_NOT_FOUND) { + return NOT_FOUND; + } + + LOGE("[KvStoreMgr] Get the file size failed[%d]", errCode); + return DB_ERROR; + } + return OK; +} + +void KvStoreDelegateManager::SetKvStoreCorruptionHandler(const KvStoreCorruptionHandler &handler) +{ + KvDBManager::SetDatabaseCorruptionHandler(handler); +} + +DBStatus KvStoreDelegateManager::GetDatabaseDir(const std::string &storeId, const std::string &appId, + const std::string &userId, std::string &directory) +{ + if (!ParamCheckUtils::CheckStoreParameter(storeId, appId, userId)) { + return INVALID_ARGS; + } + + std::string identifier = DBCommon::GenerateIdentifierId(storeId, appId, userId); + std::string dir = DBCommon::TransferHashString(identifier); + if (dir.empty()) { + return DB_ERROR; + } + directory = DBCommon::TransferStringToHex(dir); + return OK; +} + +DBStatus KvStoreDelegateManager::GetDatabaseDir(const std::string &storeId, std::string &directory) +{ + if (!ParamCheckUtils::IsStoreIdSafe(storeId)) { + return INVALID_ARGS; + } + + if (storeId.find(DBConstant::ID_CONNECTOR) != std::string::npos) { + return INVALID_ARGS; + } + + std::string dir = DBCommon::TransferHashString(storeId); + if (dir.empty()) { + return DB_ERROR; + } + directory = DBCommon::TransferStringToHex(dir); + return OK; +} + +// private +bool KvStoreDelegateManager::IsDataDirSafe(const std::string &dataDir, std::string &canonicalDir) const +{ + return ParamCheckUtils::CheckDataDir(dataDir, canonicalDir); +} + +const std::string &KvStoreDelegateManager::GetKvStorePath() const +{ + std::lock_guard lock(mutex_); + return kvStoreConfig_.dataDir; +} + +DBStatus KvStoreDelegateManager::SetPermissionCheckCallback(const PermissionCheckCallback &callback) +{ + int errCode = RuntimeContext::GetInstance()->SetPermissionCheckCallback(callback); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreDelegateManager::SetPermissionCheckCallback(const PermissionCheckCallbackV2 &callback) +{ + int errCode = RuntimeContext::GetInstance()->SetPermissionCheckCallback(callback); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreDelegateManager::EnableKvStoreAutoLaunch(const std::string &userId, const std::string &appId, + const std::string &storeId, const AutoLaunchOption &option, const AutoLaunchNotifier ¬ifier) +{ + if (RuntimeContext::GetInstance() == nullptr) { + return DB_ERROR; + } + AutoLaunchParam param{ userId, appId, storeId, option, notifier, {}}; + std::shared_ptr ptr = std::make_shared(); + int errCode = AutoLaunch::GetAutoLaunchProperties(param, DBType::DB_KV, true, ptr); + if (errCode != E_OK) { + LOGE("[KvStoreManager] Enable auto launch failed:%d", errCode); + return TransferDBErrno(errCode); + } + + std::shared_ptr kvPtr = std::static_pointer_cast(ptr); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(*kvPtr, notifier, option); + if (errCode != E_OK) { + LOGE("[KvStoreManager] Enable auto launch failed:%d", errCode); + return TransferDBErrno(errCode); + } + LOGI("[KvStoreManager] Enable auto launch"); + return OK; +} + +DBStatus KvStoreDelegateManager::DisableKvStoreAutoLaunch(const std::string &userId, const std::string &appId, + const std::string &storeId) +{ + if (RuntimeContext::GetInstance() == nullptr) { + return DB_ERROR; + } + + std::string syncIdentifier = DBCommon::GenerateIdentifierId(storeId, appId, userId); + std::string hashIdentifier = DBCommon::TransferHashString(syncIdentifier); + std::string dualIdentifier = DBCommon::TransferHashString(DBCommon::GenerateDualTupleIdentifierId(storeId, appId)); + int errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(hashIdentifier, dualIdentifier, userId); + if (errCode != E_OK) { + LOGE("[KvStoreManager] Disable auto launch failed:%d", errCode); + return TransferDBErrno(errCode); + } + LOGI("[KvStoreManager] Disable auto launch"); + return OK; +} + +void KvStoreDelegateManager::SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback) +{ + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(callback, DBType::DB_KV); +} + +std::string KvStoreDelegateManager::GetKvStoreIdentifier(const std::string &userId, const std::string &appId, + const std::string &storeId, bool syncDualTupleMode) +{ + if (!ParamCheckUtils::CheckStoreParameter(storeId, appId, userId, syncDualTupleMode)) { + return ""; + } + if (syncDualTupleMode) { + return DBCommon::TransferHashString(appId + "-" + storeId); + } + return DBCommon::TransferHashString(userId + "-" + appId + "-" + storeId); +} + +DBStatus KvStoreDelegateManager::SetProcessSystemAPIAdapter(const std::shared_ptr &adapter) +{ + return TransferDBErrno(RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(adapter)); +} + +void KvStoreDelegateManager::SetStoreStatusNotifier(const StoreStatusNotifier ¬ifier) +{ + RuntimeContext::GetInstance()->SetStoreStatusNotifier(notifier); +} + +DBStatus KvStoreDelegateManager::SetSyncActivationCheckCallback(const SyncActivationCheckCallback &callback) +{ + std::lock_guard lock(multiUserMutex_); + int errCode = RuntimeContext::GetInstance()->SetSyncActivationCheckCallback(callback); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreDelegateManager::NotifyUserChanged() +{ + std::lock_guard lock(multiUserMutex_); + int errCode = RuntimeContext::GetInstance()->NotifyUserChanged(); + return TransferDBErrno(errCode); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/interfaces/src/kv_store_errno.cpp b/mock/distributeddb/interfaces/src/kv_store_errno.cpp new file mode 100644 index 0000000000000000000000000000000000000000..77384a55e36436b0c7bb41285492a2a197b9d417 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_errno.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kv_store_errno.h" +#include "db_errno.h" + +namespace DistributedDB { +struct DBErrnoPair { + int errCode; + DBStatus status; +}; + +namespace { + const DBErrnoPair ERRNO_MAP[] = { + { E_OK, OK }, + { -E_BUSY, BUSY }, + { -E_NOT_FOUND, NOT_FOUND }, + { -E_INVALID_ARGS, INVALID_ARGS }, + { -E_TIMEOUT, TIME_OUT }, + { -E_NOT_SUPPORT, NOT_SUPPORT }, + { -E_INVALID_PASSWD_OR_CORRUPTED_DB, INVALID_PASSWD_OR_CORRUPTED_DB }, + { -E_MAX_LIMITS, OVER_MAX_LIMITS }, + { -E_INVALID_FILE, INVALID_FILE }, + { -E_INVALID_PATH, NO_PERMISSION }, + { -E_READ_ONLY, READ_ONLY }, + { -E_INVALID_SCHEMA, INVALID_SCHEMA }, + { -E_SCHEMA_MISMATCH, SCHEMA_MISMATCH }, + { -E_SCHEMA_VIOLATE_VALUE, SCHEMA_VIOLATE_VALUE }, + { -E_VALUE_MISMATCH_FEILD_COUNT, INVALID_VALUE_FIELDS }, + { -E_VALUE_MISMATCH_FEILD_TYPE, INVALID_FIELD_TYPE }, + { -E_VALUE_MISMATCH_CONSTRAINT, CONSTRAIN_VIOLATION }, + { -E_INVALID_FORMAT, INVALID_FORMAT }, + { -E_STALE, STALE }, + { -E_LOCAL_DELETED, LOCAL_DELETED }, + { -E_LOCAL_DEFEAT, LOCAL_DEFEAT }, + { -E_LOCAL_COVERED, LOCAL_COVERED }, + { -E_INVALID_QUERY_FORMAT, INVALID_QUERY_FORMAT }, + { -E_INVALID_QUERY_FIELD, INVALID_QUERY_FIELD }, + { -E_ALREADY_SET, ALREADY_SET }, + { -E_EKEYREVOKED, EKEYREVOKED_ERROR }, + { -E_SECURITY_OPTION_CHECK_ERROR, SECURITY_OPTION_CHECK_ERROR }, + { -E_INTERCEPT_DATA_FAIL, INTERCEPT_DATA_FAIL }, + { -E_LOG_OVER_LIMITS, LOG_OVER_LIMITS }, + { -E_DISTRIBUTED_SCHEMA_NOT_FOUND, DISTRIBUTED_SCHEMA_NOT_FOUND}, + { -E_DISTRIBUTED_SCHEMA_CHANGED, DISTRIBUTED_SCHEMA_CHANGED}, + { -E_MODE_MISMATCH, MODE_MISMATCH}, + { -E_NO_NEED_ACTIVE, NOT_ACTIVE}, + }; +} + +DBStatus TransferDBErrno(int err) +{ + for (const auto &item : ERRNO_MAP) { + if (item.errCode == err) { + return item.status; + } + } + return DB_ERROR; +} +}; diff --git a/mock/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.cpp b/mock/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..36e8b0bdca26840f9fa92d7783a018e844a65798 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kv_store_nb_conflict_data_impl.h" + +namespace DistributedDB { +KvStoreNbConflictDataImpl::KvStoreNbConflictDataImpl() {} + +KvStoreNbConflictDataImpl::~KvStoreNbConflictDataImpl() {} + +KvStoreNbConflictType KvStoreNbConflictDataImpl::GetType() const +{ + return static_cast(data_.type); +} + +void KvStoreNbConflictDataImpl::GetKey(Key &key) const +{ + key = data_.key; +} + +DBStatus KvStoreNbConflictDataImpl::GetValue(ValueType type, Value &value) const +{ + if (IsDeleted(type)) { + return DB_ERROR; + } + + if (type == ValueType::OLD_VALUE) { + value = data_.oldData.value; + } else { + value = data_.newData.value; + } + + return OK; +} + +bool KvStoreNbConflictDataImpl::IsDeleted(ValueType type) const +{ + if (type == ValueType::OLD_VALUE) { + return data_.oldData.isDeleted; + } else { + return data_.newData.isDeleted; + } +} + +bool KvStoreNbConflictDataImpl::IsNative(ValueType type) const +{ + if (type == ValueType::OLD_VALUE) { + return data_.oldData.isLocal; + } else { + return data_.newData.isLocal; + } +} + +void KvStoreNbConflictDataImpl::SetConflictData(const KvDBConflictEntry &conflictData) +{ + data_ = conflictData; +} +} diff --git a/mock/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.h b/mock/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..ff382f7c9a797227a3c0372127d22ae99bbfecd2 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_NB_CONFLICT_DATA_IMPL_H +#define KV_STORE_NB_CONFLICT_DATA_IMPL_H + +#include "db_types.h" +#include "kvdb_conflict_entry.h" +#include "kv_store_nb_conflict_data.h" + +namespace DistributedDB { +class KvStoreNbConflictDataImpl final : public KvStoreNbConflictData { +public: + KvStoreNbConflictDataImpl(); + ~KvStoreNbConflictDataImpl(); + + KvStoreNbConflictType GetType() const override; + + void GetKey(Key &key) const override; + + DBStatus GetValue(ValueType type, Value &value) const override; + + bool IsDeleted(ValueType type) const override; + + bool IsNative(ValueType type) const override; + + void SetConflictData(const KvDBConflictEntry &conflictData); + +private: + KvDBConflictEntry data_; +}; +} // namespace DistributedDB + +#endif // KV_STORE_NB_CONFLICT_DATA_IMPL_H diff --git a/mock/distributeddb/interfaces/src/kv_store_nb_delegate_impl.cpp b/mock/distributeddb/interfaces/src/kv_store_nb_delegate_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2cabf7ac90a15f587356692904ed0afce6f68cdf --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_nb_delegate_impl.cpp @@ -0,0 +1,917 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kv_store_nb_delegate_impl.h" + +#include +#include + +#include "platform_specific.h" +#include "log_print.h" +#include "db_constant.h" +#include "db_errno.h" +#include "db_types.h" +#include "param_check_utils.h" +#include "store_types.h" +#include "kvdb_pragma.h" +#include "kvdb_manager.h" +#include "kv_store_errno.h" +#include "kv_store_observer.h" +#include "kv_store_changed_data_impl.h" +#include "kv_store_nb_conflict_data_impl.h" +#include "kv_store_result_set_impl.h" +#include "sync_operation.h" +#include "performance_analysis.h" + +namespace DistributedDB { +namespace { + struct PragmaCmdPair { + int externCmd = 0; + int innerCmd = 0; + }; + + const PragmaCmdPair g_pragmaMap[] = { + {GET_DEVICE_IDENTIFIER_OF_ENTRY, PRAGMA_GET_DEVICE_IDENTIFIER_OF_ENTRY}, + {AUTO_SYNC, PRAGMA_AUTO_SYNC}, + {PERFORMANCE_ANALYSIS_GET_REPORT, PRAGMA_PERFORMANCE_ANALYSIS_GET_REPORT}, + {PERFORMANCE_ANALYSIS_OPEN, PRAGMA_PERFORMANCE_ANALYSIS_OPEN}, + {PERFORMANCE_ANALYSIS_CLOSE, PRAGMA_PERFORMANCE_ANALYSIS_CLOSE}, + {PERFORMANCE_ANALYSIS_SET_REPORTFILENAME, PRAGMA_PERFORMANCE_ANALYSIS_SET_REPORTFILENAME}, + {GET_IDENTIFIER_OF_DEVICE, PRAGMA_GET_IDENTIFIER_OF_DEVICE}, + {GET_QUEUED_SYNC_SIZE, PRAGMA_GET_QUEUED_SYNC_SIZE}, + {SET_QUEUED_SYNC_LIMIT, PRAGMA_SET_QUEUED_SYNC_LIMIT}, + {GET_QUEUED_SYNC_LIMIT, PRAGMA_GET_QUEUED_SYNC_LIMIT}, + {SET_WIPE_POLICY, PRAGMA_SET_WIPE_POLICY}, + {RESULT_SET_CACHE_MODE, PRAGMA_RESULT_SET_CACHE_MODE}, + {RESULT_SET_CACHE_MAX_SIZE, PRAGMA_RESULT_SET_CACHE_MAX_SIZE}, + {SET_SYNC_RETRY, PRAGMA_SET_SYNC_RETRY}, + {SET_MAX_LOG_LIMIT, PRAGMA_SET_MAX_LOG_LIMIT}, + {EXEC_CHECKPOINT, PRAGMA_EXEC_CHECKPOINT}, + }; + + const std::string INVALID_CONNECTION = "[KvStoreNbDelegate] Invalid connection for operation"; +} + +KvStoreNbDelegateImpl::KvStoreNbDelegateImpl(IKvDBConnection *conn, const std::string &storeId) + : conn_(conn), + storeId_(storeId), + releaseFlag_(false) +{} + +KvStoreNbDelegateImpl::~KvStoreNbDelegateImpl() +{ + if (!releaseFlag_) { + LOGF("[KvStoreNbDelegate] Can't release directly"); + return; + } + + conn_ = nullptr; +} + +DBStatus KvStoreNbDelegateImpl::Get(const Key &key, Value &value) const +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + return GetInner(option, key, value); +} + +DBStatus KvStoreNbDelegateImpl::GetEntries(const Key &keyPrefix, std::vector &entries) const +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + return GetEntriesInner(option, keyPrefix, entries); +} + +DBStatus KvStoreNbDelegateImpl::GetEntries(const Key &keyPrefix, KvStoreResultSet *&resultSet) const +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + IOption option; + option.dataType = IOption::SYNC_DATA; + IKvDBResultSet *kvDbResultSet = nullptr; + int errCode = conn_->GetResultSet(option, keyPrefix, kvDbResultSet); + if (errCode == E_OK) { + resultSet = new (std::nothrow) KvStoreResultSetImpl(kvDbResultSet); + if (resultSet != nullptr) { + return OK; + } + + LOGE("[KvStoreNbDelegate] Alloc result set failed."); + conn_->ReleaseResultSet(kvDbResultSet); + kvDbResultSet = nullptr; + return DB_ERROR; + } + + LOGE("[KvStoreNbDelegate] Get result set failed: %d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::GetEntries(const Query &query, std::vector &entries) const +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + if (conn_ != nullptr) { + int errCode = conn_->GetEntries(option, query, entries); + if (errCode == E_OK) { + return OK; + } else if (errCode == -E_NOT_FOUND) { + LOGD("[KvStoreNbDelegate] Not found the data by query"); + return NOT_FOUND; + } + + LOGE("[KvStoreNbDelegate] Get the batch data by query err:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreNbDelegateImpl::GetEntries(const Query &query, KvStoreResultSet *&resultSet) const +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + IOption option; + option.dataType = IOption::SYNC_DATA; + IKvDBResultSet *kvDbResultSet = nullptr; + int errCode = conn_->GetResultSet(option, query, kvDbResultSet); + if (errCode == E_OK) { + resultSet = new (std::nothrow) KvStoreResultSetImpl(kvDbResultSet); + if (resultSet != nullptr) { + return OK; + } + + LOGE("[KvStoreNbDelegate] Alloc result set failed."); + conn_->ReleaseResultSet(kvDbResultSet); + kvDbResultSet = nullptr; + return DB_ERROR; + } + + LOGE("[KvStoreNbDelegate] Get result set for query failed: %d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::GetCount(const Query &query, int &count) const +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + IOption option; + option.dataType = IOption::SYNC_DATA; + int errCode = conn_->GetCount(option, query, count); + if (errCode == E_OK) { + if (count == 0) { + return NOT_FOUND; + } + return OK; + } + + LOGE("[KvStoreNbDelegate] Get count for query failed: %d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::CloseResultSet(KvStoreResultSet *&resultSet) +{ + if (resultSet == nullptr) { + return INVALID_ARGS; + } + + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + // release inner result set + IKvDBResultSet *kvDbResultSet = nullptr; + (static_cast(resultSet))->GetResultSet(kvDbResultSet); + conn_->ReleaseResultSet(kvDbResultSet); + // release external result set + delete resultSet; + resultSet = nullptr; + return OK; +} + +DBStatus KvStoreNbDelegateImpl::Put(const Key &key, const Value &value) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + return PutInner(option, key, value); +} + +DBStatus KvStoreNbDelegateImpl::PutBatch(const std::vector &entries) +{ + if (conn_ != nullptr) { + IOption option; + option.dataType = IOption::SYNC_DATA; + int errCode = conn_->PutBatch(option, entries); + if (errCode == E_OK) { + return OK; + } + + LOGE("[KvStoreNbDelegate] Put batch data failed:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreNbDelegateImpl::DeleteBatch(const std::vector &keys) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + IOption option; + option.dataType = IOption::SYNC_DATA; + int errCode = conn_->DeleteBatch(option, keys); + if (errCode == E_OK || errCode == -E_NOT_FOUND) { + return OK; + } + + LOGE("[KvStoreNbDelegate] Delete batch data failed:%d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::Delete(const Key &key) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + return DeleteInner(option, key); +} + +DBStatus KvStoreNbDelegateImpl::GetLocal(const Key &key, Value &value) const +{ + IOption option; + option.dataType = IOption::LOCAL_DATA; + return GetInner(option, key, value); +} + +DBStatus KvStoreNbDelegateImpl::GetLocalEntries(const Key &keyPrefix, std::vector &entries) const +{ + IOption option; + option.dataType = IOption::LOCAL_DATA; + return GetEntriesInner(option, keyPrefix, entries); +} + +DBStatus KvStoreNbDelegateImpl::PutLocal(const Key &key, const Value &value) +{ + IOption option; + option.dataType = IOption::LOCAL_DATA; + return PutInner(option, key, value); +} + +DBStatus KvStoreNbDelegateImpl::DeleteLocal(const Key &key) +{ + IOption option; + option.dataType = IOption::LOCAL_DATA; + return DeleteInner(option, key); +} + +DBStatus KvStoreNbDelegateImpl::PublishLocal(const Key &key, bool deleteLocal, bool updateTimestamp, + const KvStoreNbPublishOnConflict &onConflict) +{ + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + LOGW("[KvStoreNbDelegate][Publish] Invalid para"); + return INVALID_ARGS; + } + + if (conn_ != nullptr) { + PragmaPublishInfo publishInfo{ key, deleteLocal, updateTimestamp, onConflict }; + int errCode = conn_->Pragma(PRAGMA_PUBLISH_LOCAL, static_cast(&publishInfo)); + if (errCode != E_OK) { + LOGD("[KvStoreNbDelegate] Publish local err:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreNbDelegateImpl::UnpublishToLocal(const Key &key, bool deletePublic, bool updateTimestamp) +{ + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + LOGW("[KvStoreNbDelegate][Unpublish] Invalid para"); + return INVALID_ARGS; + } + + if (conn_ != nullptr) { + PragmaUnpublishInfo unpublishInfo{ key, deletePublic, updateTimestamp }; + int errCode = conn_->Pragma(PRAGMA_UNPUBLISH_SYNC, static_cast(&unpublishInfo)); + if (errCode != E_OK) { + LOGD("[KvStoreNbDelegate] Unpublish result:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; + } + + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; +} + +DBStatus KvStoreNbDelegateImpl::PutLocalBatch(const std::vector &entries) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + IOption option; + option.dataType = IOption::LOCAL_DATA; + int errCode = conn_->PutBatch(option, entries); + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Put local batch data failed:%d", errCode); + return TransferDBErrno(errCode); + } + + return OK; +} + +DBStatus KvStoreNbDelegateImpl::DeleteLocalBatch(const std::vector &keys) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + IOption option; + option.dataType = IOption::LOCAL_DATA; + int errCode = conn_->DeleteBatch(option, keys); + if (errCode == E_OK || errCode == -E_NOT_FOUND) { + return OK; + } + + LOGE("[KvStoreNbDelegate] Delete local batch data failed:%d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::RegisterObserver(const Key &key, unsigned int mode, KvStoreObserver *observer) +{ + if (key.size() > DBConstant::MAX_KEY_SIZE) { + return INVALID_ARGS; + } + + if (!ParamCheckUtils::CheckObserver(key, mode)) { + LOGE("Register nb observer by illegal mode or key size!"); + return INVALID_ARGS; + } + + if (observer == nullptr) { + return INVALID_ARGS; + } + + std::lock_guard lockGuard(observerMapLock_); + if (observerMap_.find(observer) != observerMap_.end()) { + LOGE("[KvStoreNbDelegate] Observer has been already registered!"); + return DB_ERROR; + } + + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + if (conn_->IsTransactionStarted()) { + return BUSY; + } + + int errCode = E_OK; + KvDBObserverHandle *observerHandle = conn_->RegisterObserver( + mode, key, + [observer](const KvDBCommitNotifyData ¬ifyData) { + KvStoreChangedDataImpl data(¬ifyData); + observer->OnChange(data); + }, + errCode); + + if (errCode != E_OK || observerHandle == nullptr) { + LOGE("[KvStoreNbDelegate] RegisterListener failed:%d!", errCode); + return DB_ERROR; + } + + observerMap_.insert(std::pair(observer, observerHandle)); + LOGI("[KvStoreNbDelegate] RegisterObserver ok mode:%u", mode); + return OK; +} + +DBStatus KvStoreNbDelegateImpl::UnRegisterObserver(const KvStoreObserver *observer) +{ + if (observer == nullptr) { + return INVALID_ARGS; + } + + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + std::lock_guard lockGuard(observerMapLock_); + auto iter = observerMap_.find(observer); + if (iter == observerMap_.end()) { + LOGE("[KvStoreNbDelegate] Observer has not been registered!"); + return NOT_FOUND; + } + + const KvDBObserverHandle *observerHandle = iter->second; + int errCode = conn_->UnRegisterObserver(observerHandle); + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] UnRegistObserver failed:%d!", errCode); + return DB_ERROR; + } + observerMap_.erase(iter); + return OK; +} + +DBStatus KvStoreNbDelegateImpl::RemoveDeviceData(const std::string &device) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->Pragma(PRAGMA_RM_DEVICE_DATA, + const_cast(static_cast(&device))); + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Remove device data failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +std::string KvStoreNbDelegateImpl::GetStoreId() const +{ + return storeId_; +} + +DBStatus KvStoreNbDelegateImpl::Sync(const std::vector &devices, SyncMode mode, + const std::function &devicesMap)> &onComplete, + bool wait = false) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + PragmaSync pragmaData(devices, mode, std::bind(&KvStoreNbDelegateImpl::OnSyncComplete, + this, std::placeholders::_1, onComplete), wait); + int errCode = conn_->Pragma(PRAGMA_SYNC_DEVICES, &pragmaData); + if (errCode < E_OK) { + LOGE("[KvStoreNbDelegate] Sync data failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreNbDelegateImpl::Sync(const std::vector &devices, SyncMode mode, + const std::function &devicesMap)> &onComplete, + const Query &query, bool wait) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + QuerySyncObject querySyncObj(query); + PragmaSync pragmaData(devices, mode, querySyncObj, std::bind(&KvStoreNbDelegateImpl::OnSyncComplete, + this, std::placeholders::_1, onComplete), wait); + int errCode = conn_->Pragma(PRAGMA_SYNC_DEVICES, &pragmaData); + if (errCode < E_OK) { + LOGE("[KvStoreNbDelegate] QuerySync data failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreNbDelegateImpl::Pragma(PragmaCmd cmd, PragmaData ¶mData) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = -E_NOT_SUPPORT; + for (const auto &item : g_pragmaMap) { + if (item.externCmd == cmd) { + errCode = conn_->Pragma(item.innerCmd, paramData); + break; + } + } + + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Pragma failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreNbDelegateImpl::SetConflictNotifier(int conflictType, const KvStoreNbConflictNotifier ¬ifier) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + if (!ParamCheckUtils::CheckConflictNotifierType(conflictType)) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return INVALID_ARGS; + } + + int errCode; + if (!notifier) { + errCode = conn_->SetConflictNotifier(conflictType, nullptr); + goto END; + } + + errCode = conn_->SetConflictNotifier(conflictType, + [conflictType, notifier](const KvDBCommitNotifyData &data) { + int resultCode; + const std::list entries = data.GetCommitConflicts(resultCode); + if (resultCode != E_OK) { + LOGE("Get commit conflicted entries failed:%d!", resultCode); + return; + } + + for (const auto &entry : entries) { + // Prohibit signed numbers to perform bit operations + uint32_t entryType = static_cast(entry.type); + uint32_t type = static_cast(conflictType); + if (entryType & type) { + KvStoreNbConflictDataImpl dataImpl; + dataImpl.SetConflictData(entry); + notifier(dataImpl); + } + } + }); + +END: + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Register conflict failed:%d!", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreNbDelegateImpl::Rekey(const CipherPassword &password) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->Rekey(password); + if (errCode == E_OK) { + return OK; + } + + LOGE("[KvStoreNbDelegate] Rekey failed:%d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::Export(const std::string &filePath, const CipherPassword &passwd) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + std::string fileDir; + std::string fileName; + OS::SplitFilePath(filePath, fileDir, fileName); + + std::string canonicalUrl; + if (!ParamCheckUtils::CheckDataDir(fileDir, canonicalUrl)) { + return INVALID_ARGS; + } + + if (!OS::CheckPathExistence(canonicalUrl)) { + return NO_PERMISSION; + } + + canonicalUrl = canonicalUrl + "/" + fileName; + if (OS::CheckPathExistence(canonicalUrl)) { + return FILE_ALREADY_EXISTED; + } + + int errCode = conn_->Export(canonicalUrl, passwd); + if (errCode == E_OK) { + return OK; + } + LOGE("[KvStoreNbDelegate] Export failed:%d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + std::string fileDir; + std::string fileName; + OS::SplitFilePath(filePath, fileDir, fileName); + + std::string canonicalUrl; + if (!ParamCheckUtils::CheckDataDir(fileDir, canonicalUrl)) { + return INVALID_ARGS; + } + + canonicalUrl = canonicalUrl + "/" + fileName; + if (!OS::CheckPathExistence(canonicalUrl)) { + LOGE("Import file path err, DBStatus = INVALID_FILE errno = [%d]", errno); + return INVALID_FILE; + } + + int errCode = conn_->Import(canonicalUrl, passwd); + if (errCode == E_OK) { + LOGI("[KvStoreNbDelegate] Import ok"); + return OK; + } + + LOGE("[KvStoreNbDelegate] Import failed:%d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::StartTransaction() +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->StartTransaction(); + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] StartTransaction failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreNbDelegateImpl::Commit() +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->Commit(); + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Commit failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreNbDelegateImpl::Rollback() +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->RollBack(); + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Rollback failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +void KvStoreNbDelegateImpl::SetReleaseFlag(bool flag) +{ + releaseFlag_ = flag; +} + +DBStatus KvStoreNbDelegateImpl::Close() +{ + if (conn_ != nullptr) { + int errCode = KvDBManager::ReleaseDatabaseConnection(conn_); + if (errCode == -E_BUSY) { + LOGI("[KvStoreNbDelegate] Busy for close"); + return BUSY; + } + + LOGI("[KvStoreNbDelegateImpl] Database connection Close"); + conn_ = nullptr; + } + return OK; +} + +DBStatus KvStoreNbDelegateImpl::CheckIntegrity() const +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + return TransferDBErrno(conn_->CheckIntegrity()); +} + +DBStatus KvStoreNbDelegateImpl::GetSecurityOption(SecurityOption &option) const +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + return TransferDBErrno(conn_->GetSecurityOption(option.securityLabel, option.securityFlag)); +} + +DBStatus KvStoreNbDelegateImpl::SetRemotePushFinishedNotify(const RemotePushFinishedNotifier ¬ifier) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + PragmaRemotePushNotify notify(notifier); + int errCode = conn_->Pragma(PRAGMA_REMOTE_PUSH_FINISHED_NOTIFY, reinterpret_cast(¬ify)); + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Set remote push finished notify failed : %d", errCode); + } + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::GetInner(const IOption &option, const Key &key, Value &value) const +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->Get(option, key, value); + if (errCode == E_OK) { + return OK; + } + LOGW("[KvStoreNbDelegate] Get the data failed:%d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::GetEntriesInner(const IOption &option, + const Key &keyPrefix, std::vector &entries) const +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->GetEntries(option, keyPrefix, entries); + if (errCode == E_OK) { + return OK; + } + LOGW("[KvStoreNbDelegate] Get the batch data failed:%d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::PutInner(const IOption &option, const Key &key, const Value &value) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_PUT_DATA); + } + + int errCode = conn_->Put(option, key, value); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_PUT_DATA); + } + + if (errCode == E_OK) { + return OK; + } + LOGE("[KvStoreNbDelegate] Put the data failed:%d", errCode); + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::DeleteInner(const IOption &option, const Key &key) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + int errCode = conn_->Delete(option, key); + if (errCode == E_OK || errCode == -E_NOT_FOUND) { + return OK; + } + + LOGE("[KvStoreNbDelegate] Delete the data failed:%d", errCode); + return TransferDBErrno(errCode); +} + +void KvStoreNbDelegateImpl::OnSyncComplete(const std::map &statuses, + const std::function &devicesMap)> &onComplete) const +{ + const auto &statusMap = SyncOperation::DBStatusTransMap(); + std::map result; + for (const auto &pair : statuses) { + DBStatus status = DB_ERROR; + auto iter = statusMap.find(pair.second); + if (iter != statusMap.end()) { + status = iter->second; + } + result.insert(std::pair(pair.first, status)); + } + if (onComplete) { + onComplete(result); + } +} + +DBStatus KvStoreNbDelegateImpl::SetEqualIdentifier(const std::string &identifier, + const std::vector &targets) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + PragmaSetEqualIdentifier pragma(identifier, targets); + int errCode = conn_->Pragma(PRAGMA_ADD_EQUAL_IDENTIFIER, reinterpret_cast(&pragma)); + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Set store equal identifier failed : %d", errCode); + } + + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::SetPushDataInterceptor(const PushDataInterceptor &interceptor) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + PushDataInterceptor notify = interceptor; + int errCode = conn_->Pragma(PRAGMA_INTERCEPT_SYNC_DATA, static_cast(¬ify)); + if (errCode != E_OK) { + LOGE("[KvStoreNbDelegate] Set data interceptor notify failed : %d", errCode); + } + return TransferDBErrno(errCode); +} + +DBStatus KvStoreNbDelegateImpl::SubscribeRemoteQuery(const std::vector &devices, + const std::function &devicesMap)> &onComplete, + const Query &query, bool wait) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + QuerySyncObject querySyncObj(query); + PragmaSync pragmaData(devices, SyncModeType::SUBSCRIBE_QUERY, querySyncObj, + std::bind(&KvStoreNbDelegateImpl::OnSyncComplete, this, std::placeholders::_1, onComplete), wait); + int errCode = conn_->Pragma(PRAGMA_SUBSCRIBE_QUERY, &pragmaData); + if (errCode < E_OK) { + LOGE("[KvStoreNbDelegate] Subscribe remote data with query failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus KvStoreNbDelegateImpl::UnSubscribeRemoteQuery(const std::vector &devices, + const std::function &devicesMap)> &onComplete, + const Query &query, bool wait) +{ + if (conn_ == nullptr) { + LOGE("%s", INVALID_CONNECTION.c_str()); + return DB_ERROR; + } + + QuerySyncObject querySyncObj(query); + PragmaSync pragmaData(devices, SyncModeType::UNSUBSCRIBE_QUERY, querySyncObj, + std::bind(&KvStoreNbDelegateImpl::OnSyncComplete, this, std::placeholders::_1, onComplete), wait); + int errCode = conn_->Pragma(PRAGMA_SUBSCRIBE_QUERY, &pragmaData); + if (errCode < E_OK) { + LOGE("[KvStoreNbDelegate] Unsubscribe remote data with query failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/interfaces/src/kv_store_nb_delegate_impl.h b/mock/distributeddb/interfaces/src/kv_store_nb_delegate_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..cac5fbaab8a827bba71bf5d6e0df696f05c69cb5 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_nb_delegate_impl.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_NB_DELEGATE_IMPL_H +#define KV_STORE_NB_DELEGATE_IMPL_H + +#include +#include +#include +#include + +#include "store_types.h" +#include "db_types.h" +#include "ikvdb_connection.h" +#include "kv_store_nb_conflict_data.h" +#include "kv_store_nb_delegate.h" + +namespace DistributedDB { +class KvStoreNbDelegateImpl final : public KvStoreNbDelegate { +public: + KvStoreNbDelegateImpl(IKvDBConnection *conn, const std::string &storeId); + ~KvStoreNbDelegateImpl() override; + + DISABLE_COPY_ASSIGN_MOVE(KvStoreNbDelegateImpl); + + // Public zone interfaces + DBStatus Get(const Key &key, Value &value) const override; + + DBStatus GetEntries(const Key &keyPrefix, std::vector &entries) const override; + + DBStatus GetEntries(const Key &keyPrefix, KvStoreResultSet *&resultSet) const override; + + DBStatus GetEntries(const Query &query, std::vector &entries) const override; + + DBStatus GetEntries(const Query &query, KvStoreResultSet *&resultSet) const override; + + DBStatus GetCount(const Query &query, int &count) const override; + + DBStatus CloseResultSet(KvStoreResultSet *&resultSet) override; + + DBStatus Put(const Key &key, const Value &value) override; + + DBStatus PutBatch(const std::vector &entries) override; + + DBStatus DeleteBatch(const std::vector &keys) override; + + DBStatus Delete(const Key &key) override; + + // Local zone interfaces + DBStatus GetLocal(const Key &key, Value &value) const override; + + DBStatus GetLocalEntries(const Key &keyPrefix, std::vector &entries) const override; + + DBStatus PutLocal(const Key &key, const Value &value) override; + + DBStatus DeleteLocal(const Key &key) override; + + DBStatus PublishLocal(const Key &key, bool deleteLocal, bool updateTimestamp, + const KvStoreNbPublishOnConflict &onConflict) override; + + DBStatus UnpublishToLocal(const Key &key, bool deletePublic, bool updateTimestamp) override; + + // Observer interfaces + DBStatus RegisterObserver(const Key &key, unsigned int mode, KvStoreObserver *observer) override; + + DBStatus UnRegisterObserver(const KvStoreObserver *observer) override; + + DBStatus RemoveDeviceData(const std::string &device) override; + + // Other interfaces + std::string GetStoreId() const override; + + // Sync function interface, if wait set true, this function will be blocked until sync finished + DBStatus Sync(const std::vector &devices, SyncMode mode, + const std::function &devicesMap)> &onComplete, + bool wait) override; + + // Special pragma interface, see PragmaCmd and PragmaData, + DBStatus Pragma(PragmaCmd cmd, PragmaData ¶mData) override; + + // Set the conflict notifier for getting the specified type conflict data. + DBStatus SetConflictNotifier(int conflictType, const KvStoreNbConflictNotifier ¬ifier) override; + + // Rekey the database. + DBStatus Rekey(const CipherPassword &password) override; + + // Empty passwords represent non-encrypted files. + // Export existing database files to a specified database file in the specified directory. + DBStatus Export(const std::string &filePath, const CipherPassword &passwd) override; + + // Import the existing database files to the specified database file in the specified directory. + DBStatus Import(const std::string &filePath, const CipherPassword &passwd) override; + + // Start a transaction + DBStatus StartTransaction() override; + + // Commit a transaction + DBStatus Commit() override; + + // Rollback a transaction + DBStatus Rollback() override; + + DBStatus PutLocalBatch(const std::vector &entries) override; + + DBStatus DeleteLocalBatch(const std::vector &keys) override; + + // Get the SecurityOption of this kvStore. + DBStatus GetSecurityOption(SecurityOption &option) const override; + + DBStatus SetRemotePushFinishedNotify(const RemotePushFinishedNotifier ¬ifier) override; + + void SetReleaseFlag(bool flag); + + DBStatus Close(); + + // Sync function interface, if wait set true, this function will be blocked until sync finished. + // Param query used to filter the records to be synchronized. + // Now just support push mode and query by prefixKey. + DBStatus Sync(const std::vector &devices, SyncMode mode, + const std::function &devicesMap)> &onComplete, + const Query &query, bool wait) override; + + DBStatus CheckIntegrity() const override; + + // Set an equal identifier for this database, After this called, send msg to the target will use this identifier + DBStatus SetEqualIdentifier(const std::string &identifier, const std::vector &targets) override; + + DBStatus SetPushDataInterceptor(const PushDataInterceptor &interceptor) override; + + // Register a subscriber query on peer devices. The data in the peer device meets the subscriber query condition + // will automatically push to the local device when it's changed. + DBStatus SubscribeRemoteQuery(const std::vector &devices, + const std::function &devicesMap)> &onComplete, + const Query &query, bool wait) override; + + // Unregister a subscriber query on peer devices. + DBStatus UnSubscribeRemoteQuery(const std::vector &devices, + const std::function &devicesMap)> &onComplete, + const Query &query, bool wait) override; + +private: + DBStatus GetInner(const IOption &option, const Key &key, Value &value) const; + DBStatus PutInner(const IOption &option, const Key &key, const Value &value); + DBStatus DeleteInner(const IOption &option, const Key &key); + DBStatus GetEntriesInner(const IOption &option, const Key &keyPrefix, std::vector &entries) const; + + void OnSyncComplete(const std::map &statuses, + const std::function &devicesMap)> &onComplete) const; + + IKvDBConnection *conn_; + std::string storeId_; + bool releaseFlag_; + std::mutex observerMapLock_; + std::map observerMap_; +}; +} // namespace DistributedDB + +#endif // KV_STORE_NB_DELEGATE_IMPL_H diff --git a/mock/distributeddb/interfaces/src/kv_store_result_set_impl.cpp b/mock/distributeddb/interfaces/src/kv_store_result_set_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18f8b6624e2854376e72a6b8abc519dad68eea33 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_result_set_impl.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kv_store_result_set_impl.h" + +#include "db_errno.h" + +namespace DistributedDB { +const int KvStoreResultSetImpl::INIT_POSTION = -1; + +KvStoreResultSetImpl::KvStoreResultSetImpl(IKvDBResultSet *resultSet) + : resultSet_(resultSet) +{ +} + +int KvStoreResultSetImpl::GetCount() const +{ + if (resultSet_ == nullptr) { + return 0; + } + return resultSet_->GetCount(); +} + +int KvStoreResultSetImpl::GetPosition() const +{ + if (resultSet_ == nullptr) { + return INIT_POSTION; + } + return resultSet_->GetPosition(); +} + +bool KvStoreResultSetImpl::Move(int offset) +{ + int64_t position = GetPosition(); + int64_t aimPos = position + offset; + if (aimPos > INT_MAX) { + return MoveToPosition(INT_MAX); + } + if (aimPos < INIT_POSTION) { + return MoveToPosition(INIT_POSTION); + } + return MoveToPosition(aimPos); +} + +bool KvStoreResultSetImpl::MoveToPosition(int position) +{ + if (resultSet_ == nullptr) { + return false; + } + if (resultSet_->MoveTo(position) == E_OK) { + return true; + } + return false; +} + +bool KvStoreResultSetImpl::MoveToFirst() +{ + return MoveToPosition(0); +} + +bool KvStoreResultSetImpl::MoveToLast() +{ + return MoveToPosition(GetCount() - 1); +} + +bool KvStoreResultSetImpl::MoveToNext() +{ + // move 1 step forward in this result set + return Move(1); +} + +bool KvStoreResultSetImpl::MoveToPrevious() +{ + // move 1 step backward in this result set + return Move(-1); +} + +bool KvStoreResultSetImpl::IsFirst() const +{ + if (resultSet_ == nullptr) { + return false; + } + int position = resultSet_->GetPosition(); + if (GetCount() == 0) { + return false; + } + if (position == 0) { + return true; + } + return false; +} + +bool KvStoreResultSetImpl::IsLast() const +{ + if (resultSet_ == nullptr) { + return false; + } + int position = resultSet_->GetPosition(); + int count = GetCount(); + if (count == 0) { + return false; + } + if (position == (count - 1)) { + return true; + } + return false; +} + +bool KvStoreResultSetImpl::IsBeforeFirst() const +{ + if (resultSet_ == nullptr) { + return false; + } + int position = resultSet_->GetPosition(); + + if (GetCount() == 0) { + return true; + } + if (position <= INIT_POSTION) { + return true; + } + return false; +} + +bool KvStoreResultSetImpl::IsAfterLast() const +{ + if (resultSet_ == nullptr) { + return false; + } + int position = resultSet_->GetPosition(); + int count = GetCount(); + if (count == 0) { + return true; + } + if (position >= count) { + return true; + } + return false; +} + +DBStatus KvStoreResultSetImpl::GetEntry(Entry &entry) const +{ + if (resultSet_ == nullptr) { + return DB_ERROR; + } + if (GetCount() == 0) { + return NOT_FOUND; + } + + if (resultSet_->GetEntry(entry) == E_OK) { + return OK; + } + return NOT_FOUND; +} + +void KvStoreResultSetImpl::GetResultSet(IKvDBResultSet *&resultSet) const +{ + resultSet = resultSet_; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/interfaces/src/kv_store_result_set_impl.h b/mock/distributeddb/interfaces/src/kv_store_result_set_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..700434b16ce8b89b41f3440c40ee41b04e446d84 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_result_set_impl.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_RESULT_SET_IMPL_H +#define KV_STORE_RESULT_SET_IMPL_H + +#include + +#include "kv_store_result_set.h" +#include "ikvdb_result_set.h" + +namespace DistributedDB { +class KvStoreResultSetImpl final : public KvStoreResultSet { +public: + explicit KvStoreResultSetImpl(IKvDBResultSet *resultSet); + ~KvStoreResultSetImpl() override {}; + + DISABLE_COPY_ASSIGN_MOVE(KvStoreResultSetImpl); + + // Returns the numbers of rows in the result set. + int GetCount() const override; + + // Returns the current position of the result set in the row set. + int GetPosition() const override; + + // Move the result set to the first row, return false if the result set is empty. + bool MoveToFirst() override; + + // Move the result set to the last row, return false if the result set is empty. + bool MoveToLast() override; + + // Move the result set to the next row, return false if the result set is already past + // the last entry in the result set. + bool MoveToNext() override; + + // Move the result set to the previous row, return false if the result set is already before + // the first entry in the result set + bool MoveToPrevious() override; + + // Move the result set by a relative amount, forward or backward, from the current position. + bool Move(int offset) override; + + // Move the result set to an absolute position, the valid range of value is [-1, count] + bool MoveToPosition(int position) override; + + // Returns whether the result set is pointing to the first row. + bool IsFirst() const override; + + // Returns whether the result set is pointing to the last row. + bool IsLast() const override; + + // Returns whether the result set is pointing to the position before the first row. + bool IsBeforeFirst() const override; + + // Returns whether the result set is pointing to the position after the last row + bool IsAfterLast() const override; + + // Get a key-value entry. + DBStatus GetEntry(Entry &entry) const override; + + // Get the result set obj + void GetResultSet(IKvDBResultSet *&resultSet) const; + +private: + static const int INIT_POSTION; + IKvDBResultSet * const resultSet_; +}; +} // namespace DistributedDB + +#endif // KV_STORE_RESULT_SET_IMPL_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.cpp b/mock/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17ce4a8f9f1b7277723bc5691787a29231d738c4 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "kv_store_snapshot_delegate_impl.h" + +#include "kv_store_errno.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +KvStoreSnapshotDelegateImpl::KvStoreSnapshotDelegateImpl(IKvDBSnapshot *snapshot, KvStoreObserver *observer) + : snapShot_(snapshot), + observer_(observer) +{} + +void KvStoreSnapshotDelegateImpl::Get( + const Key &key, const std::function &callback) const +{ + if (!callback) { + LOGE("[KvStoreSnapshot] Invalid callback!"); + return; + } + + DBStatus status = DB_ERROR; + Value value; + if (snapShot_ != nullptr) { + int errCode = snapShot_->Get(key, value); + if (errCode == E_OK) { + status = OK; + } else { + if (errCode != -E_NOT_FOUND) { + LOGE("[KvStoreSnapshot] Get data failed:%d", errCode); + } + status = TransferDBErrno(errCode); + } + } + + callback(status, value); +} + +void KvStoreSnapshotDelegateImpl::GetEntries( + const Key &keyPrefix, const std::function &)> &callback) const +{ + if (!callback) { + LOGE("[KvStoreSnapshot] Invalid callback!"); + return; + } + + DBStatus status = DB_ERROR; + std::vector entries; + if (snapShot_ != nullptr) { + int errCode = snapShot_->GetEntries(keyPrefix, entries); + if (errCode == E_OK) { + status = OK; + } else { + if (errCode != -E_NOT_FOUND) { + LOGE("[KvStoreSnapshot] Get entries failed:%d", errCode); + } + status = TransferDBErrno(errCode); + } + } + + callback(status, entries); +} + +void KvStoreSnapshotDelegateImpl::GetSnapshot(IKvDBSnapshot *&snapshot) const +{ + snapshot = snapShot_; +} + +void KvStoreSnapshotDelegateImpl::GetObserver(KvStoreObserver *&observer) const +{ + observer = observer_; +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.h b/mock/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..71d8ed5afff42374cd9f0c6c6147428a3f43b182 --- /dev/null +++ b/mock/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_STORE_SNAPSHOT_DELEGATE_IMPL_H +#define KV_STORE_SNAPSHOT_DELEGATE_IMPL_H + +#ifndef OMIT_MULTI_VER +#include "kv_store_delegate_impl.h" + +#include "ikvdb_snapshot.h" + +namespace DistributedDB { +class KvStoreSnapshotDelegateImpl final : public KvStoreSnapshotDelegate { +public: + KvStoreSnapshotDelegateImpl(IKvDBSnapshot *snapshot, KvStoreObserver *observer); + ~KvStoreSnapshotDelegateImpl() override {}; + + DISABLE_COPY_ASSIGN_MOVE(KvStoreSnapshotDelegateImpl); + + // Get a value from the snapshot with the given key. + // The return value is DBStatus and Value, these values will be passed to the callback. + void Get(const Key &key, const std::function &callback) const override; + + // Get entries from the snapshot which keys start with keyPrefix. + // The return value is DBStatus and Entries, these values will be passed to the callback. + void GetEntries(const Key &keyPrefix, + const std::function &)> &callback) const override; + + // Get the snapshot + void GetSnapshot(IKvDBSnapshot *&snapshot) const; + + // Get the observer + void GetObserver(KvStoreObserver *&observer) const; + +private: + IKvDBSnapshot * const snapShot_; + KvStoreObserver * const observer_; +}; +} // namespace DistributedDB + +#endif // KV_STORE_SNAPSHOT_DELEGATE_IMPL_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/relational/relational_store_changed_data_impl.cpp b/mock/distributeddb/interfaces/src/relational/relational_store_changed_data_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4afb818b65682b691c7c5cfaa9f48ce5a2d3ba42 --- /dev/null +++ b/mock/distributeddb/interfaces/src/relational/relational_store_changed_data_impl.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "relational_store_changed_data_impl.h" + +namespace DistributedDB { +RelationalStoreChangedDataImpl::~RelationalStoreChangedDataImpl() +{ +} + +DB_API std::string RelationalStoreChangedDataImpl::GetDataChangeDevice() const +{ + std::lock_guard lock(mutex_); + // add get changedDevice_ code; + return changedDevice_; +} + +DB_API void RelationalStoreChangedDataImpl::GetStoreProperty(StoreProperty &storeProperty) const +{ + std::lock_guard lock(mutex_); + storeProperty = storeProperty_; +} + +void RelationalStoreChangedDataImpl::SetStoreProperty(const StoreProperty &storeProperty) +{ + std::lock_guard lock(mutex_); + storeProperty_ = storeProperty; +} +} // namespace DistributedDB + diff --git a/mock/distributeddb/interfaces/src/relational/relational_store_changed_data_impl.h b/mock/distributeddb/interfaces/src/relational/relational_store_changed_data_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..9544b202ab3dafeebe9abebbc781b4357f82c743 --- /dev/null +++ b/mock/distributeddb/interfaces/src/relational/relational_store_changed_data_impl.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RELATION_STORE_CHANGED_DATA_IMPL_H +#define RELATION_STORE_CHANGED_DATA_IMPL_H + +#include +#include "macro_utils.h" +#include "store_changed_data.h" + +namespace DistributedDB { +class RelationalStoreChangedDataImpl : public StoreChangedData { +public: + explicit RelationalStoreChangedDataImpl(const std::string &changedDevice) : changedDevice_(changedDevice) {} + virtual ~RelationalStoreChangedDataImpl(); + + DISABLE_COPY_ASSIGN_MOVE(RelationalStoreChangedDataImpl); + + std::string GetDataChangeDevice() const override; + + void GetStoreProperty(StoreProperty &storeProperty) const override; + + void SetStoreProperty(const StoreProperty &storeProperty); +private: + mutable std::mutex mutex_; + mutable std::string changedDevice_; + StoreProperty storeProperty_; +}; +} // namespace DistributedDB + +#endif // RELATION_STORE_CHANGED_DATA_IMPL_H + diff --git a/mock/distributeddb/interfaces/src/relational/relational_store_delegate_impl.cpp b/mock/distributeddb/interfaces/src/relational/relational_store_delegate_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d79127f57dceacc07eba72ad247d5a5253d7874f --- /dev/null +++ b/mock/distributeddb/interfaces/src/relational/relational_store_delegate_impl.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "relational_store_delegate_impl.h" + +#include "db_errno.h" +#include "kv_store_errno.h" +#include "log_print.h" +#include "param_check_utils.h" +#include "relational_store_instance.h" +#include "sync_operation.h" + +namespace DistributedDB { +RelationalStoreDelegateImpl::RelationalStoreDelegateImpl(RelationalStoreConnection *conn, const std::string &path) + : conn_(conn), + storePath_(path) +{} + +RelationalStoreDelegateImpl::~RelationalStoreDelegateImpl() +{ + if (!releaseFlag_) { + LOGF("[RelationalStore Delegate] Can't release directly"); + return; + } + + conn_ = nullptr; +}; + +DBStatus RelationalStoreDelegateImpl::RemoveDeviceData(const std::string &device) +{ + return RemoveDeviceData(device, {}); +} + +DBStatus RelationalStoreDelegateImpl::CreateDistributedTable(const std::string &tableName) +{ + if (!ParamCheckUtils::CheckRelationalTableName(tableName)) { + LOGE("invalid table name."); + return INVALID_ARGS; + } + + if (conn_ == nullptr) { + LOGE("[RelationalStore Delegate] Invalid connection for operation!"); + return DB_ERROR; + } + + int errCode = conn_->CreateDistributedTable(tableName); + if (errCode != E_OK) { + LOGE("[RelationalStore Delegate] Create Distributed table failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus RelationalStoreDelegateImpl::Sync(const std::vector &devices, SyncMode mode, + const Query &query, const SyncStatusCallback &onComplete, bool wait) +{ + if (conn_ == nullptr) { + LOGE("Invalid connection for operation!"); + return DB_ERROR; + } + + RelationalStoreConnection::SyncInfo syncInfo{devices, mode, + std::bind(&RelationalStoreDelegateImpl::OnSyncComplete, std::placeholders::_1, onComplete), query, wait}; + int errCode = conn_->SyncToDevice(syncInfo); + if (errCode != E_OK) { + LOGW("[RelationalStore Delegate] sync data to device failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus RelationalStoreDelegateImpl::RemoveDeviceData(const std::string &device, const std::string &tableName) +{ + if (conn_ == nullptr) { + LOGE("Invalid connection for operation!"); + return DB_ERROR; + } + + if (device.empty() || device.length() > DBConstant::MAX_DEV_LENGTH || + !ParamCheckUtils::CheckRelationalTableName(tableName)) { + LOGE("[RelationalStore Delegate] Remove device data with invalid device name or table name."); + return INVALID_ARGS; + } + + int errCode = conn_->RemoveDeviceData(device, tableName); + if (errCode != E_OK) { + LOGW("[RelationalStore Delegate] remove device data failed:%d", errCode); + return TransferDBErrno(errCode); + } + return OK; +} + +DBStatus RelationalStoreDelegateImpl::Close() +{ + if (conn_ == nullptr) { + return OK; + } + + int errCode = RelationalStoreInstance::ReleaseDataBaseConnection(conn_); + if (errCode == -E_BUSY) { + LOGW("[RelationalStore Delegate] busy for close"); + return BUSY; + } + if (errCode != E_OK) { + LOGE("Release db connection error:%d", errCode); + return TransferDBErrno(errCode); + } + + LOGI("[RelationalStore Delegate] Close"); + conn_ = nullptr; + return OK; +} + +void RelationalStoreDelegateImpl::SetReleaseFlag(bool flag) +{ + releaseFlag_ = flag; +} + +void RelationalStoreDelegateImpl::OnSyncComplete(const std::map> &devicesStatus, + SyncStatusCallback &onComplete) +{ + const auto &statusMap = SyncOperation::DBStatusTransMap(); + std::map> res; + for (const auto &[device, tablesStatus] : devicesStatus) { + for (const auto &tableStatus : tablesStatus) { + TableStatus table; + table.tableName = tableStatus.tableName; + DBStatus status = DB_ERROR; + auto iterator = statusMap.find(tableStatus.status); + if (iterator != statusMap.end()) { + status = iterator->second; + } + table.status = status; + res[device].push_back(table); + } + } + if (onComplete) { + onComplete(res); + } +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/relational/relational_store_delegate_impl.h b/mock/distributeddb/interfaces/src/relational/relational_store_delegate_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..2a151f20db77041fec2ae9e56cb101e6f54b7ea7 --- /dev/null +++ b/mock/distributeddb/interfaces/src/relational/relational_store_delegate_impl.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_STORE_DELEGATE_IMPL_H +#define RELATIONAL_STORE_DELEGATE_IMPL_H +#ifdef RELATIONAL_STORE + +#include "macro_utils.h" +#include "relational_store_connection.h" + +namespace DistributedDB { +class RelationalStoreDelegateImpl final : public RelationalStoreDelegate { +public: + RelationalStoreDelegateImpl() = default; + ~RelationalStoreDelegateImpl() override; + + RelationalStoreDelegateImpl(RelationalStoreConnection *conn, const std::string &path); + + DISABLE_COPY_ASSIGN_MOVE(RelationalStoreDelegateImpl); + + DBStatus Sync(const std::vector &devices, SyncMode mode, + const Query &query, const SyncStatusCallback &onComplete, bool wait) override; + + DBStatus RemoveDeviceData(const std::string &device) override; + + DBStatus CreateDistributedTable(const std::string &tableName) override; + + DBStatus RemoveDeviceData(const std::string &device, const std::string &tableName) override; + + // For connection + DBStatus Close(); + + void SetReleaseFlag(bool flag); + +private: + static void OnSyncComplete(const std::map> &devicesStatus, + SyncStatusCallback &onComplete); + + RelationalStoreConnection *conn_ = nullptr; + std::string storePath_; + std::atomic releaseFlag_ = false; +}; +} // namespace DistributedDB +#endif +#endif // RELATIONAL_STORE_DELEGATE_IMPL_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/relational/relational_store_instance.h b/mock/distributeddb/interfaces/src/relational/relational_store_instance.h new file mode 100644 index 0000000000000000000000000000000000000000..c2be0738cf05411be57a09f0d5782ec15803c8f8 --- /dev/null +++ b/mock/distributeddb/interfaces/src/relational/relational_store_instance.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_STORE_INSTANCE_H +#define RELATIONAL_STORE_INSTANCE_H +#ifdef RELATIONAL_STORE + +#include +#include + +#include "irelational_store.h" +#include "relationaldb_properties.h" + +namespace DistributedDB { +class RelationalStoreInstance final { +public: + RelationalStoreInstance(); + ~RelationalStoreInstance() = default; + + static RelationalStoreConnection *GetDatabaseConnection(const RelationalDBProperties &properties, int &errCode); + static RelationalStoreInstance *GetInstance(); + + static int ReleaseDataBaseConnection(RelationalStoreConnection *connection); + + int CheckDatabaseFileStatus(const std::string &id); + + // public for test mock + static IRelationalStore *GetDataBase(const RelationalDBProperties &properties, int &errCode); + + void Dump(int fd); +private: + + IRelationalStore *OpenDatabase(const RelationalDBProperties &properties, int &errCode); + + void RemoveKvDBFromCache(const RelationalDBProperties &properties); + void SaveRelationalDBToCache(IRelationalStore *store, const RelationalDBProperties &properties); + + void EnterDBOpenCloseProcess(const std::string &identifier); + void ExitDBOpenCloseProcess(const std::string &identifier); + + static RelationalStoreInstance *instance_; + static std::mutex instanceLock_; + + std::string appId_; + std::string userId_; + + std::mutex relationalDBOpenMutex_; + std::condition_variable relationalDBOpenCondition_; + std::set relationalDBOpenSet_; +}; +} // namespace DistributedDB + +#endif +#endif // RELATIONAL_STORE_INSTANCE_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/relational/relational_store_manager.cpp b/mock/distributeddb/interfaces/src/relational/relational_store_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60e3d9f2887aa19db84dee8d7c4cd9b069dc530c --- /dev/null +++ b/mock/distributeddb/interfaces/src/relational/relational_store_manager.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "relational_store_manager.h" + +#include + +#include "auto_launch.h" +#include "relational_store_instance.h" +#include "db_common.h" +#include "db_dfx_adapter.h" +#include "param_check_utils.h" +#include "log_print.h" +#include "db_errno.h" +#include "kv_store_errno.h" +#include "relational_store_changed_data_impl.h" +#include "relational_store_delegate_impl.h" +#include "runtime_context.h" +#include "platform_specific.h" + +namespace DistributedDB { +namespace { +const int GET_CONNECT_RETRY = 3; +const int RETRY_GET_CONN_INTER = 30; + +void InitStoreProp(const std::string &storePath, const std::string &appId, const std::string &userId, + const std::string &storeId, RelationalDBProperties &properties) +{ + properties.SetStringProp(RelationalDBProperties::DATA_DIR, storePath); + properties.SetIdentifier(userId, appId, storeId); +} +} + +RelationalStoreManager::RelationalStoreManager(const std::string &appId, const std::string &userId) + : appId_(appId), + userId_(userId) +{} + +static RelationalStoreConnection *GetOneConnectionWithRetry(const RelationalDBProperties &properties, int &errCode) +{ + for (int i = 0; i < GET_CONNECT_RETRY; i++) { + auto conn = RelationalStoreInstance::GetDatabaseConnection(properties, errCode); + if (conn != nullptr) { + return conn; + } + if (errCode == -E_STALE) { + std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_GET_CONN_INTER)); + } else { + return nullptr; + } + } + return nullptr; +} + +DB_API DBStatus RelationalStoreManager::OpenStore(const std::string &path, const std::string &storeId, + const RelationalStoreDelegate::Option &option, RelationalStoreDelegate *&delegate) +{ + if (delegate != nullptr) { + LOGE("[RelationalStoreMgr] Invalid delegate!"); + return INVALID_ARGS; + } + + std::string canonicalDir; + if (!ParamCheckUtils::CheckDataDir(path, canonicalDir)) { + return INVALID_ARGS; + } + + if (!ParamCheckUtils::CheckStoreParameter(storeId, appId_, userId_) || path.empty()) { + return INVALID_ARGS; + } + + RelationalDBProperties properties; + InitStoreProp(canonicalDir, appId_, userId_, storeId, properties); + + int errCode = E_OK; + auto *conn = GetOneConnectionWithRetry(properties, errCode); + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + DBDfxAdapter::ReportFault( { DBDfxAdapter::EVENT_OPEN_DATABASE_FAILED, userId_, appId_, storeId, errCode } ); + } + if (conn == nullptr) { + return TransferDBErrno(errCode); + } + + delegate = new (std::nothrow) RelationalStoreDelegateImpl(conn, path); + if (delegate == nullptr) { + conn->Close(); + return DB_ERROR; + } + conn->RegisterObserverAction([option, storeId, this](const std::string &changedDevice) { + RelationalStoreChangedDataImpl data(changedDevice); + data.SetStoreProperty({userId_, appId_, storeId}); + if (option.observer) { + LOGD("begin to observer on changed, changedDevice=%s", STR_MASK(changedDevice)); + option.observer->OnChange(data); + } + }); + return OK; +} + +DBStatus RelationalStoreManager::CloseStore(RelationalStoreDelegate *store) +{ + if (store == nullptr) { + return INVALID_ARGS; + } + + auto storeImpl = static_cast(store); + DBStatus status = storeImpl->Close(); + if (status == BUSY) { + LOGD("NbDelegateImpl is busy now."); + return BUSY; + } + storeImpl->SetReleaseFlag(true); + delete store; + store = nullptr; + return OK; +} + +std::string RelationalStoreManager::GetDistributedTableName(const std::string &device, const std::string &tableName) +{ + if (device.empty() || tableName.empty()) { + return {}; + } + return DBCommon::GetDistributedTableName(device, tableName); +} + +void RelationalStoreManager::SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback) +{ + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(callback, DBType::DB_RELATION); +} + +std::string RelationalStoreManager::GetRelationalStoreIdentifier(const std::string &userId, const std::string &appId, + const std::string &storeId) +{ + if (!ParamCheckUtils::CheckStoreParameter(storeId, appId, userId)) { + return ""; + } + return DBCommon::TransferHashString(DBCommon::GenerateIdentifierId(storeId, appId, userId)); +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/relational/relational_store_sqlite_ext.cpp b/mock/distributeddb/interfaces/src/relational/relational_store_sqlite_ext.cpp new file mode 100644 index 0000000000000000000000000000000000000000..54b9bc04dfa63ce414b08372f6978fe1136cc48b --- /dev/null +++ b/mock/distributeddb/interfaces/src/relational/relational_store_sqlite_ext.cpp @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +// using the "sqlite3sym.h" in OHOS +#ifndef USE_SQLITE_SYMBOLS +#include "sqlite3.h" +#else +#include "sqlite3sym.h" +#endif + +namespace { +constexpr int E_OK = 0; +constexpr int E_ERROR = 1; + +class ValueHashCalc { +public: + ValueHashCalc() {}; + ~ValueHashCalc() + { + delete context_; + context_ = nullptr; + } + + int Initialize() + { + context_ = new (std::nothrow) SHA256_CTX; + if (context_ == nullptr) { + return -E_ERROR; + } + + int errCode = SHA256_Init(context_); + if (errCode == 0) { + return -E_ERROR; + } + return E_OK; + } + + int Update(const std::vector &value) + { + if (context_ == nullptr) { + return -E_ERROR; + } + int errCode = SHA256_Update(context_, value.data(), value.size()); + if (errCode == 0) { + return -E_ERROR; + } + return E_OK; + } + + int GetResult(std::vector &value) + { + if (context_ == nullptr) { + return -E_ERROR; + } + + value.resize(SHA256_DIGEST_LENGTH); + int errCode = SHA256_Final(value.data(), context_); + if (errCode == 0) { + return -E_ERROR; + } + + return E_OK; + } + +private: + SHA256_CTX *context_ = nullptr; +}; + + +const uint64_t MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS = 1000000; + +using Timestamp = uint64_t; +using TimeOffset = int64_t; + +class TimeHelper { +public: + constexpr static int64_t BASE_OFFSET = 10000LL * 365LL * 24LL * 3600LL * 1000LL * 1000LL * 10L; // 10000 year 100ns + + constexpr static int64_t MAX_VALID_TIME = BASE_OFFSET * 2; // 20000 year 100ns + + constexpr static uint64_t TO_100_NS = 10; // 1us to 100ns + + constexpr static Timestamp INVALID_TIMESTAMP = 0; + + // Get current system time + static Timestamp GetSysCurrentTime() + { + uint64_t curTime = 0; + int errCode = GetCurrentSysTimeInMicrosecond(curTime); + if (errCode != E_OK) { + return INVALID_TIMESTAMP; + } + + std::lock_guard lock(systemTimeLock_); + // If GetSysCurrentTime in 1us, we need increase the currentIncCount_ + if (curTime == lastSystemTimeUs_) { + // if the currentIncCount_ has been increased MAX_INC_COUNT, keep the currentIncCount_ + if (currentIncCount_ < MAX_INC_COUNT) { + currentIncCount_++; + } + } else { + lastSystemTimeUs_ = curTime; + currentIncCount_ = 0; + } + return (curTime * TO_100_NS) + currentIncCount_; // Currently Timestamp is uint64_t + } + + // Init the TimeHelper + static void Initialize(Timestamp maxTimestamp) + { + std::lock_guard lock(lastLocalTimeLock_); + if (lastSystemTimeUs_ < maxTimestamp) { + lastSystemTimeUs_ = maxTimestamp; + } + } + + static Timestamp GetTime(TimeOffset timeOffset) + { + Timestamp currentSysTime = GetSysCurrentTime(); + Timestamp currentLocalTime = currentSysTime + timeOffset; + std::lock_guard lock(lastLocalTimeLock_); + if (currentLocalTime <= lastLocalTime_ || currentLocalTime > MAX_VALID_TIME) { + lastLocalTime_++; + currentLocalTime = lastLocalTime_; + } else { + lastLocalTime_ = currentLocalTime; + } + return currentLocalTime; + } + +private: + static int GetCurrentSysTimeInMicrosecond(uint64_t &outTime) + { + struct timeval rawTime; + int errCode = gettimeofday(&rawTime, nullptr); + if (errCode < 0) { + return -E_ERROR; + } + outTime = static_cast(rawTime.tv_sec) * MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS + + static_cast(rawTime.tv_usec); + return E_OK; + } + + static std::mutex systemTimeLock_; + static Timestamp lastSystemTimeUs_; + static Timestamp currentIncCount_; + static const uint64_t MAX_INC_COUNT = 9; // last bit from 0-9 + + static Timestamp lastLocalTime_; + static std::mutex lastLocalTimeLock_; +}; + +std::mutex TimeHelper::systemTimeLock_; +Timestamp TimeHelper::lastSystemTimeUs_ = 0; +Timestamp TimeHelper::currentIncCount_ = 0; +Timestamp TimeHelper::lastLocalTime_ = 0; +std::mutex TimeHelper::lastLocalTimeLock_; + +struct TransactFunc { + void (*xFunc)(sqlite3_context*, int, sqlite3_value**) = nullptr; + void (*xStep)(sqlite3_context*, int, sqlite3_value**) = nullptr; + void (*xFinal)(sqlite3_context*) = nullptr; + void(*xDestroy)(void*) = nullptr; +}; + +int RegisterFunction(sqlite3 *db, const std::string &funcName, int nArg, void *uData, TransactFunc &func) +{ + if (db == nullptr) { + return -E_ERROR; + } + return sqlite3_create_function_v2(db, funcName.c_str(), nArg, SQLITE_UTF8 | SQLITE_DETERMINISTIC, uData, + func.xFunc, func.xStep, func.xFinal, func.xDestroy); +} + +int CalcValueHash(const std::vector &value, std::vector &hashValue) +{ + ValueHashCalc hashCalc; + int errCode = hashCalc.Initialize(); + if (errCode != E_OK) { + return -E_ERROR; + } + + errCode = hashCalc.Update(value); + if (errCode != E_OK) { + return -E_ERROR; + } + + errCode = hashCalc.GetResult(hashValue); + if (errCode != E_OK) { + return -E_ERROR; + } + + return E_OK; +} + +void CalcHashKey(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + // 1 means that the function only needs one parameter, namely key + if (ctx == nullptr || argc != 1 || argv == nullptr) { + return; + } + auto keyBlob = static_cast(sqlite3_value_blob(argv[0])); + if (keyBlob == nullptr) { + sqlite3_result_error(ctx, "Parameters is invalid.", -1); + return; + } + int blobLen = sqlite3_value_bytes(argv[0]); + std::vector value(keyBlob, keyBlob + blobLen); + std::vector hashValue; + int errCode = CalcValueHash(value, hashValue); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "Get hash value error.", -1); + return; + } + sqlite3_result_blob(ctx, hashValue.data(), hashValue.size(), SQLITE_TRANSIENT); + return; +} + +int RegisterCalcHash(sqlite3 *db) +{ + TransactFunc func; + func.xFunc = &CalcHashKey; + return RegisterFunction(db, "calc_hash", 1, nullptr, func); +} + +void GetSysTime(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + if (ctx == nullptr || argc != 1 || argv == nullptr) { // 1: function need one parameter + return; + } + int timeOffset = static_cast(sqlite3_value_int64(argv[0])); + sqlite3_result_int64(ctx, (sqlite3_int64)TimeHelper::GetTime(timeOffset)); +} + +int RegisterGetSysTime(sqlite3 *db) +{ + TransactFunc func; + func.xFunc = &GetSysTime; + return RegisterFunction(db, "get_sys_time", 1, nullptr, func); +} + +int ResetStatement(sqlite3_stmt *&stmt) +{ + if (stmt == nullptr || sqlite3_finalize(stmt) != SQLITE_OK) { + return -E_ERROR; + } + stmt = nullptr; + return E_OK; +} + +int GetStatement(sqlite3 *db, const std::string &sql, sqlite3_stmt *&stmt) +{ + int errCode = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr); + if (errCode != SQLITE_OK) { + (void)ResetStatement(stmt); + return -E_ERROR; + } + return E_OK; +} + +int ExecuteRawSQL(sqlite3 *db, const std::string &sql) +{ + if (db == nullptr) { + return -E_ERROR; + } + char *errMsg = nullptr; + int errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK) { + errCode = -E_ERROR; + } + + if (errMsg != nullptr) { + sqlite3_free(errMsg); + errMsg = nullptr; + } + return errCode; +} + +int StepWithRetry(sqlite3_stmt *stmt) +{ + if (stmt == nullptr) { + return -E_ERROR; + } + int errCode = sqlite3_step(stmt); + if (errCode != SQLITE_DONE && errCode != SQLITE_ROW) { + return -E_ERROR; + } + return errCode; +} + +int GetColumnTestValue(sqlite3_stmt *stmt, int index, std::string &value) +{ + if (stmt == nullptr) { + return -E_ERROR; + } + const unsigned char *val = sqlite3_column_text(stmt, index); + value = (val != nullptr) ? std::string(reinterpret_cast(val)) : std::string(); + return E_OK; +} + +int GetCurrentMaxTimestamp(sqlite3 *db, Timestamp &maxTimestamp) +{ + if (db == nullptr) { + return -E_ERROR; + } + std::string checkTableSql = "SELECT name FROM sqlite_master WHERE type = 'table' AND " \ + "name LIKE 'naturalbase_rdb_aux_%_log';"; + sqlite3_stmt *checkTableStmt = nullptr; + int errCode = GetStatement(db, checkTableSql, checkTableStmt); + if (errCode != E_OK) { + return -E_ERROR; + } + while ((errCode = StepWithRetry(checkTableStmt)) != SQLITE_DONE) { + std::string logTablename; + GetColumnTestValue(checkTableStmt, 0, logTablename); + if (logTablename.empty()) { + continue; + } + + std::string getMaxTimestampSql = "SELECT MAX(timestamp) FROM " + logTablename + ";"; + sqlite3_stmt *getTimeStmt = nullptr; + errCode = GetStatement(db, getMaxTimestampSql, getTimeStmt); + if (errCode != E_OK) { + continue; + } + errCode = StepWithRetry(getTimeStmt); + if (errCode != SQLITE_ROW) { + ResetStatement(getTimeStmt); + continue; + } + auto tableMaxTimestamp = static_cast(sqlite3_column_int64(getTimeStmt, 0)); + maxTimestamp = (maxTimestamp > tableMaxTimestamp) ? maxTimestamp : tableMaxTimestamp; + ResetStatement(getTimeStmt); + } + ResetStatement(checkTableStmt); + return E_OK; +} + +void PostHandle(sqlite3 *db) +{ + Timestamp currentMaxTimestamp = 0; + (void)GetCurrentMaxTimestamp(db, currentMaxTimestamp); + TimeHelper::Initialize(currentMaxTimestamp); + RegisterCalcHash(db); + RegisterGetSysTime(db); + + std::string recursiveTrigger = "PRAGMA recursive_triggers = ON;"; + (void)ExecuteRawSQL(db, recursiveTrigger); +} +} + +SQLITE_API int sqlite3_open_relational(const char *filename, sqlite3 **ppDb) +{ + int err = sqlite3_open(filename, ppDb); + if (err != SQLITE_OK) { + return err; + } + PostHandle(*ppDb); + return err; +} + +SQLITE_API int sqlite3_open16_relational(const void *filename, sqlite3 **ppDb) +{ + int err = sqlite3_open16(filename, ppDb); + if (err != SQLITE_OK) { + return err; + } + PostHandle(*ppDb); + return err; +} + +SQLITE_API int sqlite3_open_v2_relational(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) +{ + int err = sqlite3_open_v2(filename, ppDb, flags, zVfs); + if (err != SQLITE_OK) { + return err; + } + PostHandle(*ppDb); + return err; +} + +// hw export the symbols +#ifdef SQLITE_DISTRIBUTE_RELATIONAL +#if defined(__GNUC__) +# define EXPORT_SYMBOLS __attribute__ ((visibility ("default"))) +#elif defined(_MSC_VER) + # define EXPORT_SYMBOLS __declspec(dllexport) +#else +# define EXPORT_SYMBOLS +#endif + +struct sqlite3_api_routines_relational { + int (*open)(const char *, sqlite3 **); + int (*open16)(const void *, sqlite3 **); + int (*open_v2)(const char *, sqlite3 **, int, const char *); +}; + +typedef struct sqlite3_api_routines_relational sqlite3_api_routines_relational; +static const sqlite3_api_routines_relational sqlite3HwApis = { +#ifdef SQLITE_DISTRIBUTE_RELATIONAL + sqlite3_open_relational, + sqlite3_open16_relational, + sqlite3_open_v2_relational +#else + 0, + 0, + 0 +#endif +}; + +EXPORT_SYMBOLS const sqlite3_api_routines_relational *sqlite3_export_relational_symbols = &sqlite3HwApis; +#endif \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/relational/relational_sync_able_storage.h b/mock/distributeddb/interfaces/src/relational/relational_sync_able_storage.h new file mode 100644 index 0000000000000000000000000000000000000000..067bffc347c5a49504e002e25a7e648923dc3218 --- /dev/null +++ b/mock/distributeddb/interfaces/src/relational/relational_sync_able_storage.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_SYNC_ABLE_STORAGE_H +#define RELATIONAL_SYNC_ABLE_STORAGE_H +#ifdef RELATIONAL_STORE + +#include "relational_db_sync_interface.h" +#include "relationaldb_properties.h" +#include "runtime_context.h" +#include "sqlite_single_relational_storage_engine.h" + +#include "sqlite_single_ver_relational_continue_token.h" + +namespace DistributedDB { +using RelationalObserverAction = std::function; +class RelationalSyncAbleStorage : public RelationalDBSyncInterface, public virtual RefObject { +public: + explicit RelationalSyncAbleStorage(StorageEngine *engine); + ~RelationalSyncAbleStorage() override; + + // Get interface type of this kvdb. + int GetInterfaceType() const override; + + // Get the interface ref-count, in order to access asynchronously. + void IncRefCount() override; + + // Drop the interface ref-count. + void DecRefCount() override; + + // Get the identifier of this kvdb. + std::vector GetIdentifier() const override; + + // Get the max timestamp of all entries in database. + void GetMaxTimestamp(Timestamp &stamp) const override; + + // Get the max timestamp of one table. + int GetMaxTimestamp(const std::string &tableName, Timestamp &stamp) const override; + + // Get meta data associated with the given key. + int GetMetaData(const Key &key, Value &value) const override; + + // Put meta data as a key-value entry. + int PutMetaData(const Key &key, const Value &value) override; + + // Delete multiple meta data records in a transaction. + int DeleteMetaData(const std::vector &keys) override; + + // Delete multiple meta data records with key prefix in a transaction. + int DeleteMetaDataByPrefixKey(const Key &keyPrefix) const override; + + // Get all meta data keys. + int GetAllMetaKeys(std::vector &keys) const override; + + const KvDBProperties &GetDbProperties() const override; + + // Get the data which would be synced with query condition + int GetSyncData(QueryObject &query, const SyncTimeRange &timeRange, + const DataSizeSpecInfo &dataSizeInfo, ContinueToken &continueStmtToken, + std::vector &entries) const override; + + int GetSyncDataNext(std::vector &entries, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const override; + + int PutSyncDataWithQuery(const QueryObject &object, const std::vector &entries, + const DeviceID &deviceName) override; + + int RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) override; + + RelationalSchemaObject GetSchemaInfo() const override; + + int GetSecurityOption(SecurityOption &option) const override; + + void NotifyRemotePushFinished(const std::string &deviceId) const override; + + // Get the timestamp when database created or imported + int GetDatabaseCreateTimestamp(Timestamp &outTime) const override; + + // Get batch meta data associated with the given key. + int GetBatchMetaData(const std::vector &keys, std::vector &entries) const override; + // Put batch meta data as a key-value entry vector + int PutBatchMetaData(std::vector &entries) override; + + std::vector GetTablesQuery() override; + + int LocalDataChanged(int notifyEvent, std::vector &queryObj) override; + + int InterceptData(std::vector &entries, const std::string &sourceID, + const std::string &targetID) const override + { + return E_OK; + } + + int CheckAndInitQueryCondition(QueryObject &query) const override; + void RegisterObserverAction(const RelationalObserverAction &action); + void TriggerObserverAction(const std::string &deviceName); + + int CreateDistributedDeviceTable(const std::string &device, const RelationalSyncStrategy &syncStrategy) override; + + int RegisterSchemaChangedCallback(const std::function &callback) override; + + void NotifySchemaChanged(); + + void RegisterHeartBeatListener(const std::function &listener); + + int GetCompressionAlgo(std::set &algorithmSet) const override; + + bool CheckCompatible(const std::string &schema, uint8_t type) const override; + +private: + SQLiteSingleVerRelationalStorageExecutor *GetHandle(bool isWrite, int &errCode, + OperatePerm perm = OperatePerm::NORMAL_PERM) const; + void ReleaseHandle(SQLiteSingleVerRelationalStorageExecutor *&handle) const; + + // get + int GetSyncDataForQuerySync(std::vector &dataItems, SQLiteSingleVerRelationalContinueToken *&token, + const DataSizeSpecInfo &dataSizeInfo) const; + + // put + int PutSyncData(const QueryObject &object, std::vector &dataItems, const std::string &deviceName); + int SaveSyncDataItems(const QueryObject &object, std::vector &dataItems, const std::string &deviceName); + + // data + SQLiteSingleRelationalStorageEngine *storageEngine_ = nullptr; + KvDBProperties properties_; + + std::function onSchemaChanged_; + mutable std::mutex onSchemaChangedMutex_; + std::mutex dataChangeDeviceMutex_; + RelationalObserverAction dataChangeDeviceCallback_; + std::function heartBeatListener_; + mutable std::mutex heartBeatMutex_; +}; +} // namespace DistributedDB +#endif +#endif // RELATIONAL_SYNC_ABLE_STORAGE_H \ No newline at end of file diff --git a/mock/distributeddb/interfaces/src/relational/runtime_config.cpp b/mock/distributeddb/interfaces/src/relational/runtime_config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e8993390cf7b5c35940976713595a0bb9a2d039 --- /dev/null +++ b/mock/distributeddb/interfaces/src/relational/runtime_config.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "runtime_config.h" + +#include "db_constant.h" +#include "db_dfx_adapter.h" +#include "kvdb_manager.h" +#include "kv_store_errno.h" +#include "log_print.h" +#include "network_adapter.h" +#include "runtime_context.h" + +namespace DistributedDB { +std::mutex RuntimeConfig::communicatorMutex_; +std::shared_ptr RuntimeConfig::processCommunicator_ = nullptr; + +// Used to set the process userid and appId +DBStatus RuntimeConfig::SetProcessLabel(const std::string &appId, const std::string &userId) +{ + if (appId.size() > DBConstant::MAX_APP_ID_LENGTH || appId.empty() || + userId.size() > DBConstant::MAX_USER_ID_LENGTH || userId.empty()) { + LOGE("Invalid app or user info[%zu]-[%zu]", appId.length(), userId.length()); + return INVALID_ARGS; + } + + int errCode = KvDBManager::SetProcessLabel(appId, userId); + if (errCode != E_OK) { + LOGE("Failed to set the process label:%d", errCode); + return DB_ERROR; + } + return OK; +} + +// Set process communicator. +DBStatus RuntimeConfig::SetProcessCommunicator(const std::shared_ptr &inCommunicator) +{ + std::lock_guard lock(communicatorMutex_); + if (processCommunicator_ != nullptr) { + LOGE("processCommunicator_ is not null!"); + return DB_ERROR; + } + + std::string processLabel = RuntimeContext::GetInstance()->GetProcessLabel(); + if (processLabel.empty()) { + LOGE("ProcessLabel is not set!"); + return DB_ERROR; + } + + auto *adapter = new (std::nothrow) NetworkAdapter(processLabel, inCommunicator); + if (adapter == nullptr) { + LOGE("New NetworkAdapter failed!"); + return DB_ERROR; + } + processCommunicator_ = inCommunicator; + if (RuntimeContext::GetInstance()->SetCommunicatorAdapter(adapter) != E_OK) { + LOGE("SetProcessCommunicator not support!"); + delete adapter; + return DB_ERROR; + } + KvDBManager::RestoreSyncableKvStore(); + return OK; +} + +DBStatus RuntimeConfig::SetPermissionCheckCallback(const PermissionCheckCallbackV2 &callback) +{ + int errCode = RuntimeContext::GetInstance()->SetPermissionCheckCallback(callback); + return TransferDBErrno(errCode); +} + +DBStatus RuntimeConfig::SetProcessSystemAPIAdapter(const std::shared_ptr &adapter) +{ + return TransferDBErrno(RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(adapter)); +} + +void RuntimeConfig::Dump(int fd, const std::vector &args) +{ + DBDfxAdapter::Dump(fd, args); +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/include/db_properties.h b/mock/distributeddb/storage/include/db_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..6b7be4a0bd3ff3d25c4bed2591067cc5fe8e472c --- /dev/null +++ b/mock/distributeddb/storage/include/db_properties.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef DB_PROPERTIES_H +#define DB_PROPERTIES_H + +#include +#include + +namespace DistributedDB { +class DBProperties { +public: + // Get the string property according the name + std::string GetStringProp(const std::string &name, const std::string &defaultValue) const; + + // Set the string property for the name + void SetStringProp(const std::string &name, const std::string &value); + + // Get the bool property according the name + bool GetBoolProp(const std::string &name, bool defaultValue) const; + + // Set the bool property for the name + void SetBoolProp(const std::string &name, bool value); + + // Get the bool property according the name + int GetIntProp(const std::string &name, int defaultValue) const; + + // Set the integer property for the name + void SetIntProp(const std::string &name, int value); + + // Set all indentifers + void SetIdentifier(const std::string &userId, const std::string &appId, const std::string &storeId); + + static const std::string CREATE_IF_NECESSARY; + static const std::string DATABASE_TYPE; + static const std::string DATA_DIR; + static const std::string USER_ID; + static const std::string APP_ID; + static const std::string STORE_ID; + static const std::string IDENTIFIER_DATA; + static const std::string IDENTIFIER_DIR; + static const std::string DUAL_TUPLE_IDENTIFIER_DATA; + static const std::string SYNC_DUAL_TUPLE_MODE; + +protected: + DBProperties() = default; + virtual ~DBProperties() = default; + + std::map stringProperties_; + std::map boolProperties_; + std::map intProperties_; +}; +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/include/iconnection.h b/mock/distributeddb/storage/include/iconnection.h new file mode 100644 index 0000000000000000000000000000000000000000..dbe63aac573ea529b0606f1537b5dd1e77d16bf6 --- /dev/null +++ b/mock/distributeddb/storage/include/iconnection.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_CONNECTION_H +#define I_CONNECTION_H + +#include +#include + +#include "macro_utils.h" + +namespace DistributedDB { +class IConnection { +public: + IConnection(); + virtual ~IConnection() {}; + + DISABLE_COPY_ASSIGN_MOVE(IConnection); + +protected: + uint64_t GetConnectionId(); + + std::mutex connectionIdLock_; + std::atomic connectionId_; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_CONNECTION_H diff --git a/mock/distributeddb/storage/include/ikvdb.h b/mock/distributeddb/storage/include/ikvdb.h new file mode 100644 index 0000000000000000000000000000000000000000..05de94092a28ae6d47018b429520f6337139def8 --- /dev/null +++ b/mock/distributeddb/storage/include/ikvdb.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KV_DB_H +#define I_KV_DB_H + +#include +#include + +#include "ref_object.h" +#include "macro_utils.h" +#include "kvdb_properties.h" +#include "ikvdb_connection.h" + +namespace DistributedDB { +class IKvDB : public virtual RefObject { +public: + IKvDB() = default; + ~IKvDB() override {} + DISABLE_COPY_ASSIGN_MOVE(IKvDB); + + // Open the database. + virtual int Open(const KvDBProperties &kvDBProp) = 0; + + // Get the properties object of this database. + virtual const KvDBProperties &GetMyProperties() const = 0; + + // Create a db connection. + virtual IKvDBConnection *GetDBConnection(int &errCode) = 0; + + // Register callback invoked when all connections released. + virtual void OnClose(const std::function &func) = 0; + + virtual void OpenPerformanceAnalysis() = 0; + + virtual void ClosePerformanceAnalysis() = 0; + + virtual void WakeUpSyncer() = 0; + + virtual void SetCorruptHandler(const DatabaseCorruptHandler &handler) = 0; + + virtual int RemoveKvDB(const KvDBProperties &properties) = 0; + virtual int GetKvDBSize(const KvDBProperties &properties, uint64_t &size) const = 0; + + virtual void EnableAutonomicUpgrade() = 0; + + virtual int CheckIntegrity() const = 0; + + virtual std::string GetStorePath() const = 0; + + virtual void Dump(int fd) = 0; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_H diff --git a/mock/distributeddb/storage/include/ikvdb_connection.h b/mock/distributeddb/storage/include/ikvdb_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..b051f95bd2a3972fc9e657a8f6ff606e7be97d4d --- /dev/null +++ b/mock/distributeddb/storage/include/ikvdb_connection.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KV_DB_CONNECTION_H +#define I_KV_DB_CONNECTION_H + +#include +#include + +#include "db_types.h" +#include "iconnection.h" +#include "macro_utils.h" +#include "query.h" +#include "store_types.h" + +namespace DistributedDB { +class IKvDB; +class IKvDBSnapshot; +class KvDBObserverHandle; +class KvDBCommitNotifyData; +class IKvDBResultSet; + +using KvDBObserverAction = std::function; +using KvDBConflictAction = std::function; + +class IKvDBConnection : public IConnection { +public: + IKvDBConnection() = default; + virtual ~IKvDBConnection() {}; + + DISABLE_COPY_ASSIGN_MOVE(IKvDBConnection); + + // Get the value from the database. + virtual int Get(const IOption &option, const Key &key, Value &value) const = 0; + + // Put the value to the database. + virtual int Put(const IOption &option, const Key &key, const Value &value) = 0; + + // Delete the value from the database. + virtual int Delete(const IOption &option, const Key &key) = 0; + + // Clear all the data from the database. + virtual int Clear(const IOption &option) = 0; + + // Get all the data from the database. + virtual int GetEntries(const IOption &option, const Key &keyPrefix, std::vector &entries) const = 0; + + virtual int GetEntries(const IOption &option, const Query &query, std::vector &entries) const = 0; + + virtual int GetCount(const IOption &option, const Query &query, int &count) const = 0; + + // Put the batch values to the database. + virtual int PutBatch(const IOption &option, const std::vector &entries) = 0; + + // Delete the batch values from the database. + virtual int DeleteBatch(const IOption &option, const std::vector &keys) = 0; + + // Get the snapshot. + virtual int GetSnapshot(IKvDBSnapshot *&snapshot) const = 0; + + // Release the created snapshot. + virtual void ReleaseSnapshot(IKvDBSnapshot *&snapshot) = 0; + + // Start the transaction. + virtual int StartTransaction() = 0; + + // Commit the transaction. + virtual int Commit() = 0; + + // Roll back the transaction. + virtual int RollBack() = 0; + + // Check if the transaction already started manually + virtual bool IsTransactionStarted() const = 0; + + // Register observer. + virtual KvDBObserverHandle *RegisterObserver(unsigned mode, const Key &key, + const KvDBObserverAction &action, int &errCode) = 0; + + // Unregister observer. + virtual int UnRegisterObserver(const KvDBObserverHandle *observerHandle) = 0; + + // Register a conflict notifier. + virtual int SetConflictNotifier(int conflictType, const KvDBConflictAction &action) = 0; + + // Close and release the connection. + virtual int Close() = 0; + + virtual std::string GetIdentifier() const = 0; + + // Pragma interface. + virtual int Pragma(int cmd, void *parameter) = 0; + + // Rekey the database. + virtual int Rekey(const CipherPassword &passwd) = 0; + + // Empty passwords represent non-encrypted files. + // Export existing database files to a specified database file in the specified directory. + virtual int Export(const std::string &filePath, const CipherPassword &passwd) = 0; + + // Import the existing database files to the specified database file in the specified directory. + virtual int Import(const std::string &filePath, const CipherPassword &passwd) = 0; + + // Get the result set + virtual int GetResultSet(const IOption &option, const Key &keyPrefix, IKvDBResultSet *&resultSet) const = 0; + + virtual int GetResultSet(const IOption &option, const Query &query, IKvDBResultSet *&resultSet) const = 0; + + // Release the result set + virtual void ReleaseResultSet(IKvDBResultSet *&resultSet) = 0; + + virtual int RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) = 0; + + // Get the securityLabel and securityFlag + virtual int GetSecurityOption(int &securityLabel, int &securityFlag) const = 0; + + virtual int CheckIntegrity() const = 0; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_CONNECTION_H diff --git a/mock/distributeddb/storage/include/ikvdb_factory.h b/mock/distributeddb/storage/include/ikvdb_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..eaf5600bcc18875140769cfdafbb89cbb777c2f8 --- /dev/null +++ b/mock/distributeddb/storage/include/ikvdb_factory.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KV_DB_FACTORY_H +#define I_KV_DB_FACTORY_H + +#include + +#include "ikvdb.h" +#include "db_types.h" +#ifndef OMIT_MULTI_VER +#include "ikvdb_multi_ver_data_storage.h" +#include "ikvdb_commit_storage.h" +#endif + +namespace DistributedDB { +enum KvDBType { + LOCAL_KVDB = 0, + SINGER_VER_KVDB, + MULTI_VER_KVDB, + UNSUPPORT_KVDB_TYPE, +}; + +class IKvDBFactory { +public: + virtual ~IKvDBFactory() {} + + // Get current factory object. + static IKvDBFactory *GetCurrent(); + + // Set the factory object to 'current' + static void Register(IKvDBFactory *factory); + + virtual IKvDB *CreateKvDb(KvDBType kvDbType, int &errCode) = 0; + + // Create a key-value database for commit storage module. + virtual IKvDB *CreateCommitStorageDB(int &errCode) = 0; + +#ifndef OMIT_MULTI_VER + // Create the multi version storage for multi version natural store + virtual IKvDBMultiVerDataStorage *CreateMultiVerStorage(int &errCode) = 0; + + // Create the commit storage. The object can be deleted directly when it is not needed. + virtual IKvDBCommitStorage *CreateMultiVerCommitStorage(int &errCode) = 0; +#endif +private: + static IKvDBFactory *factory_; + static std::mutex instanceLock_; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_FACTORY_H diff --git a/mock/distributeddb/storage/include/ikvdb_result_set.h b/mock/distributeddb/storage/include/ikvdb_result_set.h new file mode 100644 index 0000000000000000000000000000000000000000..b0aa5de38b2e47e3cd182b6ee7cd4e1866237c98 --- /dev/null +++ b/mock/distributeddb/storage/include/ikvdb_result_set.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KV_DB_RESULT_SET_H +#define I_KV_DB_RESULT_SET_H + +#include "macro_utils.h" +#include "db_types.h" + +namespace DistributedDB { +class IKvDBResultSet { +public: + IKvDBResultSet() = default; + virtual ~IKvDBResultSet() {} + + DISABLE_COPY_ASSIGN_MOVE(IKvDBResultSet); + + // Initialize logic + virtual int Open(bool isMemDb) = 0; + + // Get total entries count. + // >= 0: count, < 0: errCode. + virtual int GetCount() const = 0; + + // Get current read position. + // >= 0: position, < 0: errCode + virtual int GetPosition() const = 0; + + // Move the read position to an absolute position value. + virtual int MoveTo(int position) const = 0; + + // Get the entry of current position. + virtual int GetEntry(Entry &entry) const = 0; + + // Finalize logic + virtual void Close() = 0; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_RESULT_SET_H diff --git a/mock/distributeddb/storage/include/ikvdb_snapshot.h b/mock/distributeddb/storage/include/ikvdb_snapshot.h new file mode 100644 index 0000000000000000000000000000000000000000..17d3fc82afc5c0a237015cdf4c055e0e475aacfe --- /dev/null +++ b/mock/distributeddb/storage/include/ikvdb_snapshot.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KV_DB_SNAP_SHOT_H +#define I_KV_DB_SNAP_SHOT_H + +#include "db_types.h" +#include "kv_store_changed_data.h" + +namespace DistributedDB { +class IKvDBSnapshot { +public: + virtual ~IKvDBSnapshot() {} + + // Get the value according the key in the snapshot + virtual int Get(const Key &key, Value &value) const = 0; + + // Get the data according the prefix key in the snapshot + virtual int GetEntries(const Key &keyPrefix, std::vector &entries) const = 0; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_SNAP_SHOT_H diff --git a/mock/distributeddb/storage/include/ikvdb_sync_interface.h b/mock/distributeddb/storage/include/ikvdb_sync_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..572add415c36ac39eb9a88cfba2c2d1135eda082 --- /dev/null +++ b/mock/distributeddb/storage/include/ikvdb_sync_interface.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KVDB_SYNC_INTERFACE_H +#define I_KVDB_SYNC_INTERFACE_H + +#include + +#include "db_types.h" +#include "kvdb_properties.h" +#include "sync_generic_interface.h" + +namespace DistributedDB { +class IKvDBSyncInterface : public SyncGenericInterface { +public: + + // Constructor/Destructor. + IKvDBSyncInterface() = default; + ~IKvDBSyncInterface() override = default; +}; +} // namespace DistributedDB + +#endif // I_KVDB_SYNC_INTERFACE_H diff --git a/mock/distributeddb/storage/include/isync_interface.h b/mock/distributeddb/storage/include/isync_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..0a4e0e70426df6dd3e54a209858a52486a34cc9b --- /dev/null +++ b/mock/distributeddb/storage/include/isync_interface.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_SYNC_INTERFACE_H +#define I_SYNC_INTERFACE_H + +#include + +#include "db_types.h" +#include "kvdb_properties.h" + +namespace DistributedDB { +class ISyncInterface { +public: + enum { + SYNC_SVD = 1, // Single version data + SYNC_MVD, // Multi version data + SYNC_RELATION, // Relation version data + }; + + // Constructor/Destructor. + ISyncInterface() = default; + virtual ~ISyncInterface() = default; + + // Get interface type of this kvdb. + virtual int GetInterfaceType() const = 0; + + // Get the interface ref-count, in order to access asynchronously. + virtual void IncRefCount() = 0; + + // Drop the interface ref-count. + virtual void DecRefCount() = 0; + + // Get the identifier of this kvdb. + virtual std::vector GetIdentifier() const = 0; + // Get the dual tuple identifier of this kvdb. + virtual std::vector GetDualTupleIdentifier() const = 0; + + // Get the max timestamp of all entries in database. + virtual void GetMaxTimestamp(Timestamp &stamp) const = 0; + + // Get meta data associated with the given key. + virtual int GetMetaData(const Key &key, Value &value) const = 0; + + // Put meta data as a key-value entry. + virtual int PutMetaData(const Key &key, const Value &value) = 0; + + // Delete multiple meta data records in a transaction. + virtual int DeleteMetaData(const std::vector &keys) = 0; + + // Delete multiple meta data records with key prefix in a transaction. + virtual int DeleteMetaDataByPrefixKey(const Key &keyPrefix) const = 0; + + // Get all meta data keys. + virtual int GetAllMetaKeys(std::vector &keys) const = 0; + + virtual const KvDBProperties &GetDbProperties() const = 0; +}; +} // namespace DistributedDB + +#endif // I_SYNC_INTERFACE_H \ No newline at end of file diff --git a/mock/distributeddb/storage/include/kvdb_commit_notify_data.h b/mock/distributeddb/storage/include/kvdb_commit_notify_data.h new file mode 100644 index 0000000000000000000000000000000000000000..2f82539a3d05fad8a81e856120ecdc9a90a905a9 --- /dev/null +++ b/mock/distributeddb/storage/include/kvdb_commit_notify_data.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KVDB_COMMIT_NOTIFY_DATA_H +#define KVDB_COMMIT_NOTIFY_DATA_H + +#include + +#include "db_types.h" +#include "ref_object.h" +#include "macro_utils.h" +#include "kvdb_conflict_entry.h" + +namespace DistributedDB { +// Data from local commit or syncer commit. +class KvDBCommitNotifyData : public RefObject { +public: + KvDBCommitNotifyData() = default; + virtual ~KvDBCommitNotifyData() {} + DISABLE_COPY_ASSIGN_MOVE(KvDBCommitNotifyData); + + // get the new inserted entries. + virtual const std::list GetInsertedEntries(int &errCode) const = 0; + + // get the new updated entries. + virtual const std::list GetUpdatedEntries(int &errCode) const = 0; + + // get the new deleted entries. + virtual const std::list GetDeletedEntries(int &errCode) const = 0; + + // get all conflict entries when commit. + virtual const std::list GetCommitConflicts(int &errCode) const = 0; + + // database is cleared by user in the commit. + virtual bool IsCleared() const = 0; + + // test if the inserted/updated/deleted data is empty or not. + virtual bool IsChangedDataEmpty() const = 0; + + // test if the conflict data is empty or not. + virtual bool IsConflictedDataEmpty() const = 0; +}; +} // namespace DistributedDB + +#endif // KVDB_COMMIT_NOTIFY_DATA_H diff --git a/mock/distributeddb/storage/include/kvdb_conflict_entry.h b/mock/distributeddb/storage/include/kvdb_conflict_entry.h new file mode 100644 index 0000000000000000000000000000000000000000..79f83c3ea2b7965f51330ece85cee9a7ed685621 --- /dev/null +++ b/mock/distributeddb/storage/include/kvdb_conflict_entry.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KVDB_CONFLICT_ENTRY_H +#define KVDB_CONFLICT_ENTRY_H + +#include "db_types.h" + +namespace DistributedDB { +struct ConflictData { + Value value; + bool isDeleted = false; + bool isLocal = false; +}; + +struct KvDBConflictEntry { + int type = 1; + Key key; + ConflictData oldData; + ConflictData newData; +}; +} // namespace DistributedDB + +#endif // KVDB_CONFLICT_ENTRY_H diff --git a/mock/distributeddb/storage/include/kvdb_manager.h b/mock/distributeddb/storage/include/kvdb_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..34dbad5bc6d4e83eb586c8f6daab3112de7eb2a2 --- /dev/null +++ b/mock/distributeddb/storage/include/kvdb_manager.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_DB_MANAGER_H +#define KV_DB_MANAGER_H + +#include +#include +#include +#include +#include + +#include "db_errno.h" +#include "ikvdb.h" +#include "ikvdb_factory.h" +#include "platform_specific.h" + +namespace DistributedDB { +class KvDBManager final { +public: + // used to generate process label + static const std::string PROCESS_LABEL_CONNECTOR; + + // used to open a kvdb with the given property + static IKvDB *OpenDatabase(const KvDBProperties &property, int &errCode); + + // used to open a kvdb with the given property + static IKvDBConnection *GetDatabaseConnection(const KvDBProperties &property, int &errCode, + bool isNeedIfOpened = true); + + // used to close the connection. + static int ReleaseDatabaseConnection(IKvDBConnection *connection); + + // used to delete a kvdb with the given property. + static int RemoveDatabase(const KvDBProperties &property); + + // Used to set the process userid and appid + static int SetProcessLabel(const std::string &appId, const std::string &userId); + + static int CalculateKvStoreSize(const KvDBProperties &property, uint64_t &size); + + // used to restore the sync module of the store. + static void RestoreSyncableKvStore(); + + // used to set the corruption handler. + static void SetDatabaseCorruptionHandler(const KvStoreCorruptionHandler &handler); + + // Attention. After call FindKvDB and kvdb is not null, you need to call DecObjRef. + IKvDB* FindKvDB(const std::string &identifier) const; + + // Get a KvDBManager instance, Singleton mode + static KvDBManager *GetInstance(); + + // Dump all db message in cache + void Dump(int fd); +private: + // Generate a KvDB unique Identifier + static std::string GenerateKvDBIdentifier(const KvDBProperties &property); + + // used to judge Db opened, can not remove Db file + static int CheckDatabaseFileStatus(const KvDBProperties &properties); + + IKvDB *OpenNewDatabase(const KvDBProperties &property, int &errCode); + + // Save to IKvDB to the global map + IKvDB *SaveKvDBToCache(IKvDB *kvDB); + + // Get IKvdb From global map + IKvDB *FindAndGetKvDBFromCache(const KvDBProperties &property, int &errCode) const; + + // Get IKvdb From global map + void RemoveKvDBFromCache(const IKvDB *kvDB); + + // Find a IKvdb From the given cache. the IKvDB will IncObjRef if found. + IKvDB *FindKvDBFromCache(const KvDBProperties &property, + const std::map &cache, bool isNeedCheckPasswd, int &errCode) const; + + bool IsOpenMemoryDb(const KvDBProperties &properties, const std::map &cache) const; + + void RestoreSyncerOfAllKvStore(); + + void SetAllDatabaseCorruptionHander(const KvStoreCorruptionHandler &handler); + + IKvDB *CreateDataBase(const KvDBProperties &property, int &errCode); + + IKvDB *GetDataBase(const KvDBProperties &property, int &errCode, bool isNeedIfOpened); + + void DataBaseCorruptNotify(const std::string &appId, const std::string &userId, const std::string &storeId); + + void DataBaseCorruptNotifyAsync(const std::string &appId, const std::string &userId, const std::string &storeId); + + void EnterDBOpenCloseProcess(const std::string &identifier); + + void ExitDBOpenCloseProcess(const std::string &identifier); + + void SetCorruptHandlerForDatabases(const std::map &kvDBMap); + + // Compare two schema objects and return true if both are empty, + // or both are not empty and the schemas are equal, otherwise return false. + static bool CompareSchemaObject(const SchemaObject &newSchema, const SchemaObject &oldSchema); + + // check schema is valid + static int CheckSchema(const IKvDB *kvDB, const KvDBProperties &properties); + + static int ExecuteRemoveDatabase(const KvDBProperties &properties); + + static void RemoveDBDirectory(const KvDBProperties &properties); + + int CheckKvDBProperties(const IKvDB *kvDB, const KvDBProperties &properties, + bool isNeedCheckPasswd) const; + + IKvDB *GetKvDBFromCacheByIdentify(const std::string &identifier, const std::map &cache) const; + + static int CheckRemoveStateAndRetry(const KvDBProperties &property); + + static int TryLockDB(const KvDBProperties &kvDBProp, int retryTimes); + static int UnlockDB(const KvDBProperties &kvDBProp); + + static std::atomic instance_; + static std::mutex kvDBLock_; + static std::mutex instanceLock_; + static std::map locks_; + + std::map localKvDBs_; + std::map multiVerNaturalStores_; + std::map singleVerNaturalStores_; + + std::mutex corruptMutex_; + std::mutex kvDBOpenMutex_; + std::condition_variable kvDBOpenCondition_; + std::set kvDBOpenSet_; + KvStoreCorruptionHandler corruptHandler_; +}; +} // namespace DistributedDB + +#endif // KV_DB_MANAGER_H diff --git a/mock/distributeddb/storage/include/kvdb_pragma.h b/mock/distributeddb/storage/include/kvdb_pragma.h new file mode 100644 index 0000000000000000000000000000000000000000..649ae2c15a216ac2a6416dbf80c4629c14b1b482 --- /dev/null +++ b/mock/distributeddb/storage/include/kvdb_pragma.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_DB_PRAGMA_H +#define KV_DB_PRAGMA_H + +#include +#include +#include + +#include "store_types.h" +#include "query_sync_object.h" + +namespace DistributedDB { +enum : int { + PRAGMA_AUTO_SYNC = 1, + PRAGMA_SYNC_DEVICES, + PRAGMA_RM_DEVICE_DATA, // remove the device data synced from remote by device name + PRAGMA_PERFORMANCE_ANALYSIS_GET_REPORT, + PRAGMA_PERFORMANCE_ANALYSIS_OPEN, + PRAGMA_PERFORMANCE_ANALYSIS_CLOSE, + PRAGMA_PERFORMANCE_ANALYSIS_SET_REPORTFILENAME, + PRAGMA_GET_IDENTIFIER_OF_DEVICE, + PRAGMA_GET_DEVICE_IDENTIFIER_OF_ENTRY, + PRAGMA_GET_QUEUED_SYNC_SIZE, + PRAGMA_SET_QUEUED_SYNC_LIMIT, + PRAGMA_GET_QUEUED_SYNC_LIMIT, + PRAGMA_SET_WIPE_POLICY, + PRAGMA_PUBLISH_LOCAL, + PRAGMA_UNPUBLISH_SYNC, + PRAGMA_SET_AUTO_LIFE_CYCLE, + PRAGMA_RESULT_SET_CACHE_MODE, + PRAGMA_RESULT_SET_CACHE_MAX_SIZE, + PRAGMA_TRIGGER_TO_MIGRATE_DATA, + PRAGMA_REMOTE_PUSH_FINISHED_NOTIFY, + PRAGMA_SET_SYNC_RETRY, + PRAGMA_ADD_EQUAL_IDENTIFIER, + PRAGMA_INTERCEPT_SYNC_DATA, + PRAGMA_SUBSCRIBE_QUERY, + PRAGMA_SET_MAX_LOG_LIMIT, + PRAGMA_EXEC_CHECKPOINT, +}; + +struct PragmaSync { + PragmaSync(const std::vector &devices, int mode, const QuerySyncObject &query, + const std::function &devicesMap)> &onComplete, + bool wait = false) + : devices_(devices), + mode_(mode), + onComplete_(onComplete), + wait_(wait), + isQuerySync_(true), + query_(query) + { + } + + PragmaSync(const std::vector &devices, int mode, + const std::function &devicesMap)> &onComplete, + bool wait = false) + : devices_(devices), + mode_(mode), + onComplete_(onComplete), + wait_(wait), + isQuerySync_(false), + query_(Query::Select()) + { + } + + std::vector devices_; + int mode_; + std::function &devicesMap)> onComplete_; + bool wait_; + bool isQuerySync_; + QuerySyncObject query_; +}; + +struct PragmaRemotePushNotify { + explicit PragmaRemotePushNotify(RemotePushFinishedNotifier notifier) : notifier_(notifier) {} + + RemotePushFinishedNotifier notifier_; +}; + +struct PragmaSetEqualIdentifier { + PragmaSetEqualIdentifier(const std::string &identifier, const std::vector &targets) + : identifier_(identifier), + targets_(targets) {} + + std::string identifier_; + std::vector targets_; +}; +} // namespace DistributedDB + +#endif // KV_DB_PRAGMA_H diff --git a/mock/distributeddb/storage/include/kvdb_properties.h b/mock/distributeddb/storage/include/kvdb_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..c985b0d832154b80f63d72c7d10f5298b3c4917f --- /dev/null +++ b/mock/distributeddb/storage/include/kvdb_properties.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_DB_PROPERTIES_H +#define KV_DB_PROPERTIES_H + +#include +#include +#include + +#include "db_properties.h" +#include "schema_object.h" + +namespace DistributedDB { +class KvDBProperties final : public DBProperties { +public: + KvDBProperties(); + ~KvDBProperties() override; + + // Get the sub directory for different type database. + static std::string GetStoreSubDirectory(int type); + + // Get the password + void GetPassword(CipherType &type, CipherPassword &password) const; + + // Set the password + void SetPassword(CipherType type, const CipherPassword &password); + + // is schema exist + bool IsSchemaExist() const; + + // set schema + void SetSchema(const SchemaObject &schema); + + // get schema + SchemaObject GetSchema() const; + + // If it does not exist, use the int map default value 0 + int GetSecLabel() const; + + int GetSecFlag() const; + + // Get schema const reference if you can guarantee the lifecycle of this KvDBProperties + // The upper code will not change the schema if it is already set + const SchemaObject &GetSchemaConstRef() const; + + static const std::string FILE_NAME; + static const std::string SYNC_MODE; + static const std::string MEMORY_MODE; + static const std::string ENCRYPTED_MODE; + static const std::string FIRST_OPEN_IS_READ_ONLY; + static const std::string CREATE_DIR_BY_STORE_ID_ONLY; + static const std::string SECURITY_LABEL; + static const std::string SECURITY_FLAG; + static const std::string CONFLICT_RESOLVE_POLICY; + static const std::string CHECK_INTEGRITY; + static const std::string RM_CORRUPTED_DB; + static const std::string COMPRESS_ON_SYNC; + static const std::string COMPRESSION_RATE; + + static const int LOCAL_TYPE = 1; + static const int MULTI_VER_TYPE = 2; + static const int SINGLE_VER_TYPE = 3; + +private: + CipherType cipherType_; + CipherPassword password_; + SchemaObject schema_; +}; +} // namespace DistributedDB + +#endif // KV_DB_PROPERTIES_H diff --git a/mock/distributeddb/storage/include/multi_ver_def.h b/mock/distributeddb/storage/include/multi_ver_def.h new file mode 100644 index 0000000000000000000000000000000000000000..64dd3c6b94eeb3485f5fbc90e483551118031716 --- /dev/null +++ b/mock/distributeddb/storage/include/multi_ver_def.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_DEF_H +#define MULTI_VER_DEF_H + +#include +#include +#include + +#include "db_types.h" + +namespace DistributedDB { +using CommitID = std::vector; +using Version = uint64_t; +static const size_t MULTI_VER_TAG_SIZE = 8; + +struct MultiVerCommitNode { + static const uint64_t LOCAL_FLAG = 1; + static const uint64_t NON_LOCAL_FLAG = 0; + std::vector commitId; + std::vector leftParent; + std::vector rightParent; + uint64_t timestamp = 0; + uint64_t version = 0; // version for storage + uint64_t isLocal = 0; // merge node or native node + std::string deviceInfo; // device name +}; + +struct MultiVerEntryAuxData { + uint64_t operFlag = 0; + uint64_t timestamp = 0; + uint64_t oriTimestamp = 0; +}; + +struct MultiVerEntryData { + Key key; + Value value; + MultiVerEntryAuxData auxData; // auxiliaries +}; + +struct MultiVerTrimedVersionData { + Key key; // hash key + uint64_t operFlag = 0; + uint64_t version = 0; +}; + +struct MultiVerDiffData { + std::list inserted; + std::list updated; + std::list deleted; + bool isCleared = false; + void Reset() + { + inserted.clear(); + updated.clear(); + deleted.clear(); + isCleared = false; + } +}; + +enum class MultiVerDataType { + NATIVE_TYPE, + ALL_TYPE, +}; +} + +#endif // MULTI_VER_DEF_H diff --git a/mock/distributeddb/storage/include/multi_ver_kvdb_sync_interface.h b/mock/distributeddb/storage/include/multi_ver_kvdb_sync_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..83e985b5cdb4a0e5cd8741d8089e0c3a6c2d0477 --- /dev/null +++ b/mock/distributeddb/storage/include/multi_ver_kvdb_sync_interface.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_KVDB_SYNC_INTERFACE_H +#define MULTI_VER_KVDB_SYNC_INTERFACE_H + +#include +#include + +#include "multi_ver_def.h" +#include "multi_ver_kv_entry.h" +#include "ikvdb_sync_interface.h" + +namespace DistributedDB { +class MultiVerKvDBSyncInterface : public IKvDBSyncInterface { +public: + // Judge whether the commit existed. + virtual bool IsCommitExisted(const MultiVerCommitNode &commit) const = 0; + + // Get the latest commits of all devices in the current device. + virtual int GetDeviceLatestCommit(std::map &commitMap) const = 0; + + // Get the commit tree and exclude the existed commits from the remote device. + virtual int GetCommitTree(const std::map &commitMap, + std::vector &commits) const = 0; + + // Get all the data from one commit. + virtual int GetCommitData(const MultiVerCommitNode &commit, std::vector &entries) const = 0; + + // Create one kv entry from the serialized data from remote device. + virtual MultiVerKvEntry *CreateKvEntry(const std::vector &data) = 0; + + // Release the kv entry created from the interface of CreateKvEntry. + virtual void ReleaseKvEntry(const MultiVerKvEntry *entry) = 0; + + // Judge whether the slice hash-value existed. + virtual bool IsValueSliceExisted(const ValueSliceHash &value) const = 0; + + // Get the value according the slice hash value, and push the value to the remote. + virtual int GetValueSlice(const ValueSliceHash &hashValue, ValueSlice &sliceValue) const = 0; + + // Put the value when put the remote data into the local database. + virtual int PutValueSlice(const ValueSliceHash &hashValue, const ValueSlice &sliceValue) const = 0; + + // Put all the kv entries of one commit received from the remote. + virtual int PutCommitData(const MultiVerCommitNode &commit, const std::vector &entries, + const std::string &deviceName) = 0; + + // Merge the remote commit into the local tree. + virtual int MergeSyncCommit(const MultiVerCommitNode &commit, const std::vector &commits) = 0; + + virtual void NotifyStartSyncOperation() = 0; + + virtual void NotifyFinishSyncOperation() = 0; + + virtual int TransferSyncCommitDevInfo(MultiVerCommitNode &commit, const std::string &devId, + bool isSyncedIn) const = 0; +}; +} + +#endif // MULTI_VER_KVDB_SYNC_INTERFACE_H diff --git a/mock/distributeddb/storage/include/multi_ver_vacuum.h b/mock/distributeddb/storage/include/multi_ver_vacuum.h new file mode 100644 index 0000000000000000000000000000000000000000..fbc70e7b63913b614d192ec2535cee4139e593ee --- /dev/null +++ b/mock/distributeddb/storage/include/multi_ver_vacuum.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_VACUUM_H +#define MULTI_VER_VACUUM_H + +#ifndef OMIT_MULTI_VER +#include +#include +#include +#include +#include +#include +#include +#include +#include "macro_utils.h" +#include "multi_ver_vacuum_executor.h" + +namespace DistributedDB { +enum class VacuumTaskStatus { + RUN_WAIT, + RUN_NING, + PAUSE_WAIT, + PAUSE_DONE, + ABORT_WAIT, + ABORT_DONE, + FINISH, +}; + +struct VacuumTaskContext { + VacuumTaskStatus status = VacuumTaskStatus::RUN_WAIT; + bool launchErrorHappen = false; + bool autoRelaunchOnce = false; + bool immediatelyRelaunchable = true; + uint64_t runWaitOrder = 0; + uint64_t pauseNeedCount = 0; + MultiVerVacuumExecutor *databaseHandle = nullptr; + // Information to conduct the vacuum task and record the progress. + // When in RUN_NING, PAUSE_WAIT, ABORT_WAIT status, no other thread except the only one background task thread + // will access and change these field, so there is no concurrency risk accessing and changing it without a lock. + std::list leftBranchCommits; + std::list rightBranchCommits; + std::list vacuumNeedRecords; + std::list shadowRecords; + bool isTransactionStarted = false; +}; + +// Pause and Continue should be call in pair for the same database. If Pause called more than Continue, then the task +// will stay in "inactive status". If Continue called more than Pause, then the excessive Continue will be neglected. +// It expected that every Pause following by a Continue sooner or later before Abort is called. +class MultiVerVacuum { +public: + // Default is enable, should be called at very first to change it, take effect only on instances created after it. + static void Enable(bool isEnable); + + DISABLE_COPY_ASSIGN_MOVE(MultiVerVacuum); + + // Call it when database firstly open + int Launch(const std::string &dbIdentifier, MultiVerVacuumExecutor *dbHandle); + + // Call it before database do write operation, it may block for a while if task is running + // It is guaranteed that no write transaction of this database will be used by vacuum after pause return + int Pause(const std::string &dbIdentifier); + + // Call it after database do write operation, and write transaction of this database may be used by vacuum. + // If autoRelaunchOnce true, the task will relaunch itself when finish, set false will not override previous true + int Continue(const std::string &dbIdentifier, bool autoRelaunchOnce); + + // Call it when database is about to close, it may block for a while if task is running + int Abort(const std::string &dbIdentifier); + + // Call it when observer_callback done or release an snapshot, to relaunch with some newer vacuumable commits + int AutoRelaunchOnce(const std::string &dbIdentifier); + + int QueryStatus(const std::string &dbIdentifier, VacuumTaskStatus &outStatus) const; + + MultiVerVacuum() = default; + ~MultiVerVacuum(); +private: + void VacuumTaskExecutor(); + void ExecuteSpecificVacuumTask(VacuumTaskContext &inTask); + + int DealWithLeftBranchCommit(VacuumTaskContext &inTask); + int DealWithLeftBranchVacuumNeedRecord(VacuumTaskContext &inTask); + int DealWithLeftBranchShadowRecord(VacuumTaskContext &inTask); + int DealWithRightBranchCommit(VacuumTaskContext &inTask); + int DealWithRightBranchVacuumNeedRecord(VacuumTaskContext &inTask); + + // Reducing duplicated code by merging similar code procedure of "DealLeftCommit" and "DealRightCommit" + int DoDealCommitOfLeftOrRight(VacuumTaskContext &inTask, std::list &commitList, bool isLeft); + + // Reducing duplicated code by merging similar code procedure of "DealLeftShadow" and "DealRightVacuumNeed" + int DoDeleteRecordOfLeftShadowOrRightVacuumNeed(VacuumTaskContext &inTask, + std::list &recordList); + + // Only for reducing duplicated code + void DoRollBackAndFinish(VacuumTaskContext &inTask); + int DoCommitAndQuitIfWaitStatusObserved(VacuumTaskContext &inTask); // Return E_OK continue otherwise quit + + // Call this immediately before changing the database + int StartTransactionIfNotYet(VacuumTaskContext &inTask); + + // Call this immediately before normally quit + int CommitTransactionIfNeed(VacuumTaskContext &inTask); + + // Call this immediately before abnormally quit, return void since already in abnormal. + void RollBackTransactionIfNeed(VacuumTaskContext &inTask); + + // All these following functions should be protected by the vacuumTaskMutex_ when called + void FinishVaccumTask(VacuumTaskContext &inTask); + void RelaunchVacuumTask(VacuumTaskContext &inTask); + void AbortVacuumTask(VacuumTaskContext &inTask); + void ResetNodeAndRecordContextInfo(VacuumTaskContext &inTask); + int SearchVacuumTaskToExecute(std::string &outDbIdentifier); + void ActivateBackgroundVacuumTaskExecution(); + void IncPauseNeedCount(VacuumTaskContext &inTask); + void DecPauseNeedCount(VacuumTaskContext &inTask); + bool IsPauseNotNeed(VacuumTaskContext &inTask); + + static std::atomic enabled_; + + mutable std::mutex vacuumTaskMutex_; + std::condition_variable vacuumTaskCv_; + uint64_t incRunWaitOrder_ = 0; + std::map dbMapVacuumTask_; + + // the search of available vacuumtask, the change of isBackgroundVacuumTaskInExecution_, and the activation of + // background execution, should all be protected by vacuumTaskMutex_, In order to avoid malfunction caused by + // concurrency situation which is described below: + // 1:Background search vacuumtask return none so decided to exit. + // 2:Foreground make vacuumtask available. + // 3:Foreground check isBackgroundVacuumTaskInExecution_ true so decided to nothing. + // 4:Background set isBackgroundVacuumTaskInExecution_ to false and exit. + // In this situation, no background execution running with available vacuumtask needs to be done. + bool isBackgroundVacuumTaskInExecution_ = false; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_VACUUM_H +#endif diff --git a/mock/distributeddb/storage/include/multi_ver_vacuum_executor.h b/mock/distributeddb/storage/include/multi_ver_vacuum_executor.h new file mode 100644 index 0000000000000000000000000000000000000000..011260654b35028e90b8e508c4031b6a9f484551 --- /dev/null +++ b/mock/distributeddb/storage/include/multi_ver_vacuum_executor.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_VACUUM_EXECUTOR_H +#define MULTI_VER_VACUUM_EXECUTOR_H + +#include +#include +#include +#include + +namespace DistributedDB { +enum class RecordType { + CLEAR, + DELETE, + VALID, // Not clear nor delete +}; + +struct MultiVerRecordInfo { + RecordType type; + uint64_t version; + std::vector hashKey; +}; + +struct MultiVerCommitInfo { + uint64_t version; + std::vector commitId; +}; + +// All functions will not be concurrently called +class MultiVerVacuumExecutor { +public: + // Call this always beyond transaction + virtual int GetVacuumAbleCommits(std::list &leftBranchCommits, + std::list &rightBranchCommits) const = 0; + + // Call this within or beyond transaction + virtual int GetVacuumNeedRecordsByVersion(uint64_t version, std::list &vacuumNeedRecords) = 0; + + // Call this within or beyond transaction + virtual int GetShadowRecordsOfClearTypeRecord(uint64_t version, const std::vector &hashKey, + std::list &shadowRecords) = 0; + + // Call this within or beyond transaction + virtual int GetShadowRecordsOfNonClearTypeRecord(uint64_t version, const std::vector &hashKey, + std::list &shadowRecords) = 0; + + // Call this before change the database + virtual int StartTransactionForVacuum() = 0; + + // Call this if nothing error happened, if this itself failed, do not need to call rollback + virtual int CommitTransactionForVacuum() = 0; + + // Call this if anything wrong happened after start transaction except commit fail + virtual int RollBackTransactionForVacuum() = 0; + + // Call this always within transaction + virtual int DeleteRecordTotally(uint64_t version, const std::vector &hashKey) = 0; + + // Call this always within transaction + virtual int MarkRecordAsVacuumDone(uint64_t version, const std::vector &hashKey) = 0; + + // Call this always within transaction + virtual int MarkCommitAsVacuumDone(const std::vector &commitId) = 0; + + virtual ~MultiVerVacuumExecutor() {}; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_VACUUM_EXECUTOR_H diff --git a/mock/distributeddb/storage/include/relational_db_sync_interface.h b/mock/distributeddb/storage/include/relational_db_sync_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..a94488e9df5e48a10fbf01cec7ccfe4b9399a297 --- /dev/null +++ b/mock/distributeddb/storage/include/relational_db_sync_interface.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_DB_SYNC_INTERFACE_H +#define RELATIONAL_DB_SYNC_INTERFACE_H +#ifdef RELATIONAL_STORE + +#include "query_sync_object.h" +#include "relational_schema_object.h" +#include "single_ver_kv_entry.h" +#include "sync_generic_interface.h" +#include "schema_negotiate.h" + +namespace DistributedDB { +class RelationalDBSyncInterface : public SyncGenericInterface { +public: + ~RelationalDBSyncInterface() override {}; + + virtual RelationalSchemaObject GetSchemaInfo() const = 0; + + // Get batch meta data associated with the given key. + virtual int GetBatchMetaData(const std::vector &keys, std::vector &entries) const = 0; + // Put batch meta data as a key-value entry vector + virtual int PutBatchMetaData(std::vector &entries) = 0; + + virtual std::vector GetTablesQuery() = 0; + + virtual int LocalDataChanged(int notifyEvent, std::vector &queryObj) = 0; + + virtual int CreateDistributedDeviceTable(const std::string &device, const RelationalSyncStrategy &syncStrategy) = 0; + + virtual int RegisterSchemaChangedCallback(const std::function &callback) = 0; + + using ISyncInterface::GetMaxTimestamp; + virtual int GetMaxTimestamp(const std::string &tableName, Timestamp ×tamp) const = 0; +}; +} +#endif // RELATIONAL_STORE +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/include/relational_store_connection.h b/mock/distributeddb/storage/include/relational_store_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..4c9ab7b6d22f5ee7431f63c71d8f1104cad0ea58 --- /dev/null +++ b/mock/distributeddb/storage/include/relational_store_connection.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_STORE_CONNECTION_H +#define RELATIONAL_STORE_CONNECTION_H +#ifdef RELATIONAL_STORE + +#include +#include + +#include "db_types.h" +#include "iconnection.h" +#include "macro_utils.h" +#include "ref_object.h" +#include "relational_store_delegate.h" + +namespace DistributedDB { +class IRelationalStore; +using RelationalObserverAction = std::function; +class RelationalStoreConnection : public IConnection, public virtual RefObject { +public: + struct SyncInfo { + const std::vector &devices; + SyncMode mode = SYNC_MODE_PUSH_PULL; + const SyncStatusCallback &onComplete; + const Query &query; + bool wait = true; + }; + + RelationalStoreConnection(); + + explicit RelationalStoreConnection(IRelationalStore *store); + + virtual ~RelationalStoreConnection() = default; + + DISABLE_COPY_ASSIGN_MOVE(RelationalStoreConnection); + + // Close and release the connection. + virtual int Close() = 0; + virtual int TriggerAutoSync() = 0; + virtual int SyncToDevice(SyncInfo &info) = 0; + virtual std::string GetIdentifier() = 0; + virtual int CreateDistributedTable(const std::string &tableName) = 0; + virtual int RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) = 0; + + virtual int RemoveDeviceData(const std::string &device) = 0; + virtual int RemoveDeviceData(const std::string &device, const std::string &tableName) = 0; + virtual void RegisterObserverAction(const RelationalObserverAction &action) = 0; + +protected: + // Get the stashed 'RelationalDB_ pointer' without ref. + template + DerivedDBType *GetDB() const + { + return static_cast(store_); + } + + virtual int Pragma(int cmd, void *parameter); + IRelationalStore *store_ = nullptr; + std::atomic isExclusive_; +}; +} // namespace DistributedDB +#endif +#endif // RELATIONAL_STORE_CONNECTION_H \ No newline at end of file diff --git a/mock/distributeddb/storage/include/relationaldb_properties.h b/mock/distributeddb/storage/include/relationaldb_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..2b60fede5473ae2537aafa7911d4cf67573f4db9 --- /dev/null +++ b/mock/distributeddb/storage/include/relationaldb_properties.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONALDB_PROPERTIES_H +#define RELATIONALDB_PROPERTIES_H +#ifdef RELATIONAL_STORE +#include +#include +#include + +#include "db_properties.h" +#include "relational_schema_object.h" + +namespace DistributedDB { +class RelationalDBProperties final : public DBProperties { +public: + RelationalDBProperties(); + ~RelationalDBProperties() override; + + // is schema exist + bool IsSchemaExist() const; + + // set schema + void SetSchema(const RelationalSchemaObject &schema); + + // get schema + RelationalSchemaObject GetSchema() const; + +private: + RelationalSchemaObject schema_; +}; +} +#endif +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/include/single_ver_kv_entry.h b/mock/distributeddb/storage/include/single_ver_kv_entry.h new file mode 100644 index 0000000000000000000000000000000000000000..626ad2375f5cfd5a216deac04f2e9a3b3aeb9404 --- /dev/null +++ b/mock/distributeddb/storage/include/single_ver_kv_entry.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_KV_ENTRY_H +#define SINGLE_VER_KV_ENTRY_H + +#include +#include "parcel.h" + +namespace DistributedDB { +class SingleVerKvEntry { +public: + virtual ~SingleVerKvEntry() {}; + virtual std::string GetOrigDevice() const = 0; + virtual void SetOrigDevice(const std::string &device) = 0; + virtual Timestamp GetTimestamp() const = 0; + virtual void SetTimestamp(Timestamp timestamp) = 0; + virtual Timestamp GetWriteTimestamp() const = 0; + virtual void SetWriteTimestamp(Timestamp timestamp) = 0; + virtual uint64_t GetFlag() const = 0; + virtual int SerializeData(Parcel &parcel, uint32_t softWareVersion) = 0; + virtual int DeSerializeData(Parcel &parcel) = 0; + virtual uint32_t CalculateLen(uint32_t softWareVersion) = 0; + virtual const Key &GetKey() const = 0; + virtual const Value &GetValue() const = 0; + virtual void SetKey(const Key &key) = 0; + virtual void SetValue(const Value &value) = 0; + virtual void SetHashKey(const Key &hashKey) = 0; + + static void Release(std::vector &entries) + { + for (auto &entry : entries) { + delete entry; + entry = nullptr; + } + entries.clear(); + }; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_KV_ENTRY_H diff --git a/mock/distributeddb/storage/include/single_ver_kvdb_sync_interface.h b/mock/distributeddb/storage/include/single_ver_kvdb_sync_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..dc9cdf29894a96c0a8c05bb66455b7df3518b1f2 --- /dev/null +++ b/mock/distributeddb/storage/include/single_ver_kvdb_sync_interface.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_KVDB_SYNC_INTERFACE_H +#define SINGLE_VER_KVDB_SYNC_INTERFACE_H + +#include "ikvdb_sync_interface.h" +#include "single_ver_kv_entry.h" +#include "iprocess_system_api_adapter.h" +#include "query_object.h" +#include "intercepted_data.h" + +namespace DistributedDB { +using MulDevTimeRanges = std::map>; +using MulDevSinVerKvEntry = std::map>; +using MulDevDataItems = std::map>; + +class SingleVerKvDBSyncInterface : public IKvDBSyncInterface { +public: + SingleVerKvDBSyncInterface() = default; + ~SingleVerKvDBSyncInterface() override = default; + + virtual SchemaObject GetSchemaInfo() const = 0; +}; +} + +#endif // SINGLE_VER_KVDB_SYNC_INTERFACE_H diff --git a/mock/distributeddb/storage/include/storage_engine_manager.h b/mock/distributeddb/storage/include/storage_engine_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..717cc0ed4363f9fa50b2d0045a58ad7bc74b7b0a --- /dev/null +++ b/mock/distributeddb/storage/include/storage_engine_manager.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STORAGE_ENGINE_MANAGER_H +#define STORAGE_ENGINE_MANAGER_H + +#include +#include +#include +#include +#include + +#include "storage_engine.h" + +namespace DistributedDB { +class StorageEngineManager final { +public: + static StorageEngine *GetStorageEngine(const KvDBProperties &property, int &errCode); + + static int ReleaseStorageEngine(StorageEngine *storageEngine); + + static int ForceReleaseStorageEngine(const std::string &identifier); + + static int ExecuteMigration(StorageEngine *storageEngine); + + DISABLE_COPY_ASSIGN_MOVE(StorageEngineManager); + +private: + StorageEngineManager(); + ~StorageEngineManager(); + + // Get a StorageEngineManager instance, Singleton mode + static StorageEngineManager *GetInstance(); + + int RegisterLockStatusListener(); + + void LockStatusNotifier(bool isAccessControlled); + + void RemoveEngineFromCache(const std::string &identifier); + + StorageEngine *CreateStorageEngine(const KvDBProperties &property, int &errCode); + + StorageEngine *FindStorageEngine(const std::string &identifier); + + void InsertStorageEngine(const std::string &identifier, StorageEngine *&storageEngine); + + void EraseStorageEngine(const std::string &identifier); + + void ReleaseResources(const std::string &identifier); + + int ReleaseEngine(StorageEngine *releaseEngine); + + void EnterGetEngineProcess(const std::string &identifier); + + void ExitGetEngineProcess(const std::string &identifier); + + static std::mutex instanceLock_; + static std::atomic instance_; + static bool isRegLockStatusListener_; + + static std::mutex storageEnginesLock_; + std::map storageEngines_; + + std::mutex getEngineMutex_; + std::condition_variable getEngineCondition_; + std::set getEngineSet_; + + NotificationChain::Listener *lockStatusListener_; +}; +} // namespace DistributedDB + +#endif // STORAGE_ENGINE_MANAGER_H diff --git a/mock/distributeddb/storage/include/sync_generic_interface.h b/mock/distributeddb/storage/include/sync_generic_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..1fc895c431ee671661091548d446b2c2f2f9b962 --- /dev/null +++ b/mock/distributeddb/storage/include/sync_generic_interface.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SYNC_GENERIC_INTERFACE_H +#define SYNC_GENERIC_INTERFACE_H + +#include "isync_interface.h" +#include "single_ver_kv_entry.h" +#include "query_object.h" + +namespace DistributedDB { +class SyncGenericInterface : public ISyncInterface { +public: + // Constructor/Destructor. + SyncGenericInterface() = default; + ~SyncGenericInterface() override = default; + + virtual int GetSyncData(Timestamp begin, Timestamp end, std::vector &dataItems, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const + { + LOGE("GetSyncData not support!"); + return -E_NOT_SUPPORT; + } + + // Get the data which would be synced to other devices according the timestamp. + // if the data size is over than the blockSize, It would alloc one token and assign to continueStmtToken, + // it should be released when the read operation terminate. + virtual int GetSyncData(Timestamp begin, Timestamp end, std::vector &entries, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const + { + LOGE("GetSyncData not support!"); + return -E_NOT_SUPPORT; + } + + // Get the data which would be synced with query condition + virtual int GetSyncData(QueryObject &query, const SyncTimeRange &timeRange, + const DataSizeSpecInfo &dataSizeInfo, ContinueToken &continueStmtToken, + std::vector &entries) const + { + return -E_NOT_SUPPORT; + } + + virtual int GetSyncDataNext(std::vector &dataItems, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const + { + return -E_NOT_SUPPORT; + } + + virtual int GetSyncDataNext(std::vector &entries, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const + { + return -E_NOT_SUPPORT; + } + + virtual int GetCompressionOption(bool &needCompressOnSync, uint8_t &compressionRate) const + { + return -E_NOT_SUPPORT; + } + + // Release the continue token of getting data. + virtual void ReleaseContinueToken(ContinueToken &continueStmtToken) const + { + } + + virtual int RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) + { + return -E_NOT_SUPPORT; + } + + virtual bool IsReadable() const + { + return true; + } + + virtual int GetSecurityOption(SecurityOption &option) const + { + return -E_NOT_SUPPORT; + } + + virtual void NotifyRemotePushFinished(const std::string &targetId) const + { + } + + // Get the timestamp when database created or imported + virtual int GetDatabaseCreateTimestamp(Timestamp &outTime) const + { + return -E_NOT_SUPPORT; + } + + virtual int PutSyncDataWithQuery(const QueryObject &query, const std::vector &entries, + const std::string &deviceName) + { + return -E_NOT_SUPPORT; + } + + virtual int CheckAndInitQueryCondition(QueryObject &query) const + { + return -E_NOT_SUPPORT; + } + + virtual int InterceptData(std::vector &entries, const std::string &sourceID, + const std::string &targetID) const + { + return -E_NOT_SUPPORT; + } + + virtual int AddSubscribe(const std::string &subscribeId, const QueryObject &query, bool needCacheSubscribe) + { + return -E_NOT_SUPPORT; + } + + virtual int RemoveSubscribe(const std::string &subscribeId) + { + return -E_NOT_SUPPORT; + } + + virtual int RemoveSubscribe(const std::vector &subscribeIds) + { + return -E_NOT_SUPPORT; + } + + virtual int GetCompressionAlgo(std::set &algorithmSet) const + { + return -E_NOT_SUPPORT; + } + + virtual bool CheckCompatible(const std::string &schema, uint8_t type) const + { + return false; + } + + std::vector GetDualTupleIdentifier() const override + { + return {}; + } +}; +} +#endif // SYNC_GENERIC_INTERFACE_H diff --git a/mock/distributeddb/storage/src/data_transformer.cpp b/mock/distributeddb/storage/src/data_transformer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5d1ff990815ef3d43f836cf89a7b9fdea7e7f5b5 --- /dev/null +++ b/mock/distributeddb/storage/src/data_transformer.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "data_transformer.h" + +#include "db_common.h" +#include "db_errno.h" +#include "log_print.h" +#include "parcel.h" +#include "relational_schema_object.h" + +namespace DistributedDB { +int DataTransformer::TransformTableData(const TableDataWithLog &tableDataWithLog, + const std::vector &fieldInfoList, std::vector &dataItems) +{ + if (tableDataWithLog.dataList.empty()) { + return E_OK; + } + for (const RowDataWithLog& data : tableDataWithLog.dataList) { + DataItem dataItem; + int errCode = SerializeDataItem(data, fieldInfoList, dataItem); + if (errCode != E_OK) { + return errCode; + } + dataItems.push_back(std::move(dataItem)); + } + return E_OK; +} + +int DataTransformer::TransformDataItem(const std::vector &dataItems, + const std::vector &remoteFieldInfo, const std::vector &localFieldInfo, + OptTableDataWithLog &tableDataWithLog) +{ + if (dataItems.empty()) { + return E_OK; + } + std::vector indexMapping; + ReduceMapping(remoteFieldInfo, localFieldInfo); + for (const DataItem &dataItem : dataItems) { + OptRowDataWithLog dataWithLog; + int errCode = DeSerializeDataItem(dataItem, dataWithLog, remoteFieldInfo); + if (errCode != E_OK) { + return errCode; + } + tableDataWithLog.dataList.push_back(std::move(dataWithLog)); + } + return E_OK; +} + +int DataTransformer::SerializeDataItem(const RowDataWithLog &data, + const std::vector &fieldInfo, DataItem &dataItem) +{ + int errCode = SerializeValue(dataItem.value, data.rowData, fieldInfo); + if (errCode != E_OK) { + return errCode; + } + const LogInfo &logInfo = data.logInfo; + dataItem.timestamp = logInfo.timestamp; + dataItem.dev = logInfo.device; + dataItem.origDev = logInfo.originDev; + dataItem.writeTimestamp = logInfo.wTimestamp; + dataItem.flag = logInfo.flag; + dataItem.hashKey = logInfo.hashKey; + return E_OK; +} + +int DataTransformer::DeSerializeDataItem(const DataItem &dataItem, OptRowDataWithLog &data, + const std::vector &remoteFieldInfo) +{ + if ((dataItem.flag & DataItem::DELETE_FLAG) == 0 && + (dataItem.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) == 0) { + int errCode = DeSerializeValue(dataItem.value, data.optionalData, remoteFieldInfo); + if (errCode != E_OK) { + return errCode; + } + } + + LogInfo &logInfo = data.logInfo; + logInfo.timestamp = dataItem.timestamp; + logInfo.device = dataItem.dev; + logInfo.originDev = dataItem.origDev; + logInfo.wTimestamp = dataItem.writeTimestamp; + logInfo.flag = dataItem.flag; + logInfo.hashKey = dataItem.hashKey; + return E_OK; +} + +uint32_t DataTransformer::CalDataValueLength(const DataValue &dataValue) +{ + static std::map lengthMap = { + { StorageType::STORAGE_TYPE_NULL, Parcel::GetUInt32Len()}, + { StorageType::STORAGE_TYPE_INTEGER, Parcel::GetInt64Len()}, + { StorageType::STORAGE_TYPE_REAL, Parcel::GetDoubleLen()} + }; + if (lengthMap.find(dataValue.GetType()) != lengthMap.end()) { + return lengthMap[dataValue.GetType()]; + } + if (dataValue.GetType() != StorageType::STORAGE_TYPE_BLOB && + dataValue.GetType() != StorageType::STORAGE_TYPE_TEXT) { + return 0u; + } + uint32_t length = 0; + switch (dataValue.GetType()) { + case StorageType::STORAGE_TYPE_BLOB: + case StorageType::STORAGE_TYPE_TEXT: + (void)dataValue.GetBlobLength(length); + length = Parcel::GetEightByteAlign(length); + length += Parcel::GetUInt32Len(); // record data length + break; + default: + break; + } + return length; +} + +void DataTransformer::ReduceMapping(const std::vector &remoteFieldInfo, + const std::vector &localFieldInfo) +{ + std::map fieldMap; + for (int i = 0; i < static_cast(remoteFieldInfo.size()); ++i) { + const auto &fieldInfo = remoteFieldInfo[i]; + fieldMap[fieldInfo.GetFieldName()] = i; + } +} + +namespace { +int SerializeNullValue(const DataValue &dataValue, Parcel &parcel) +{ + return parcel.WriteUInt32(0u); +} + +int DeSerializeNullValue(DataValue &dataValue, Parcel &parcel) +{ + uint32_t dataLength = -1; + (void)parcel.ReadUInt32(dataLength); + if (parcel.IsError() || dataLength != 0) { + return -E_PARSE_FAIL; + } + dataValue.ResetValue(); + return E_OK; +} + +int SerializeIntValue(const DataValue &dataValue, Parcel &parcel) +{ + int64_t val = 0; + (void)dataValue.GetInt64(val); + return parcel.WriteInt64(val); +} + +int DeSerializeIntValue(DataValue &dataValue, Parcel &parcel) +{ + int64_t val = 0; + (void)parcel.ReadInt64(val); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + dataValue = val; + return E_OK; +} + +int SerializeDoubleValue(const DataValue &dataValue, Parcel &parcel) +{ + double val = 0; + (void)dataValue.GetDouble(val); + return parcel.WriteDouble(val); +} + +int DeSerializeDoubleValue(DataValue &dataValue, Parcel &parcel) +{ + double val = 0; + (void)parcel.ReadDouble(val); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + dataValue = val; + return E_OK; +} + +int SerializeBlobValue(const DataValue &dataValue, Parcel &parcel) +{ + Blob val; + (void)dataValue.GetBlob(val); + uint32_t size = val.GetSize(); + if (size == 0) { + return SerializeNullValue(dataValue, parcel); + } + int errCode = parcel.WriteUInt32(size); + if (errCode != E_OK) { + return errCode; + } + return parcel.WriteBlob(reinterpret_cast(val.GetData()), size); +} + +int DeSerializeBlobByType(DataValue &dataValue, Parcel &parcel, StorageType type) +{ + uint32_t blobLength = 0; + (void)parcel.ReadUInt32(blobLength); + if (blobLength == 0) { + dataValue.ResetValue(); + return E_OK; + } + if (blobLength >= DBConstant::MAX_VALUE_SIZE || parcel.IsError()) { // One blob cannot be over one value size. + return -E_PARSE_FAIL; + } + auto array = new (std::nothrow) char[blobLength](); + if (array == nullptr) { + return -E_OUT_OF_MEMORY; + } + (void)parcel.ReadBlob(array, blobLength); + if (parcel.IsError()) { + delete []array; + return -E_PARSE_FAIL; + } + int errCode = -E_NOT_SUPPORT; + if (type == StorageType::STORAGE_TYPE_TEXT) { + errCode = dataValue.SetText(reinterpret_cast(array), blobLength); + } else if (type == StorageType::STORAGE_TYPE_BLOB) { + Blob val; + errCode = val.WriteBlob(reinterpret_cast(array), blobLength); + if (errCode == E_OK) { + errCode = dataValue.SetBlob(val); + } + } + delete []array; + return errCode; +} + +int DeSerializeBlobValue(DataValue &dataValue, Parcel &parcel) +{ + return DeSerializeBlobByType(dataValue, parcel, StorageType::STORAGE_TYPE_BLOB); +} + +int SerializeTextValue(const DataValue &dataValue, Parcel &parcel) +{ + return SerializeBlobValue(dataValue, parcel); +} + +int DeSerializeTextValue(DataValue &dataValue, Parcel &parcel) +{ + return DeSerializeBlobByType(dataValue, parcel, StorageType::STORAGE_TYPE_TEXT); +} + +int SerializeDataValue(const DataValue &dataValue, Parcel &parcel) +{ + static const std::function funcs[] = { + SerializeNullValue, SerializeIntValue, + SerializeDoubleValue, SerializeTextValue, SerializeBlobValue, + }; + StorageType type = dataValue.GetType(); + parcel.WriteUInt32(static_cast(type)); + if (type < StorageType::STORAGE_TYPE_NULL || type > StorageType::STORAGE_TYPE_BLOB) { + LOGE("Cannot serialize %u", static_cast(type)); + return -E_NOT_SUPPORT; + } + return funcs[static_cast(type) - 1](dataValue, parcel); +} + +int DeserializeDataValue(DataValue &dataValue, Parcel &parcel) +{ + static const std::function funcs[] = { + DeSerializeNullValue, DeSerializeIntValue, + DeSerializeDoubleValue, DeSerializeTextValue, DeSerializeBlobValue, + }; + uint32_t type = 0; + parcel.ReadUInt32(type); + if (type < static_cast(StorageType::STORAGE_TYPE_NULL) || + type > static_cast(StorageType::STORAGE_TYPE_BLOB)) { + LOGE("Cannot deserialize %u", type); + return -E_PARSE_FAIL; + } + return funcs[type - 1](dataValue, parcel); +} +} + +int DataTransformer::SerializeValue(Value &value, const RowData &rowData, const std::vector &fieldInfoList) +{ + if (rowData.size() != fieldInfoList.size()) { + LOGE("[DataTransformer][SerializeValue] unequal field counts!"); + return -E_INVALID_ARGS; + } + + uint32_t totalLength = Parcel::GetUInt64Len(); // first record field count + for (uint32_t i = 0; i < rowData.size(); ++i) { + const auto &dataValue = rowData[i]; + totalLength += Parcel::GetUInt32Len(); // For save the dataValue's type. + uint32_t dataLength = CalDataValueLength(dataValue); + totalLength += dataLength; + } + value.resize(totalLength); + if (value.size() != totalLength) { + return -E_OUT_OF_MEMORY; + } + Parcel parcel(value.data(), value.size()); + (void)parcel.WriteUInt64(rowData.size()); + for (const auto &dataValue : rowData) { + int errCode = SerializeDataValue(dataValue, parcel); + if (errCode != E_OK) { + value.clear(); + return errCode; + } + } + return E_OK; +} + +int DataTransformer::DeSerializeValue(const Value &value, OptRowData &optionalData, + const std::vector &remoteFieldInfo) +{ + Parcel parcel(const_cast(value.data()), value.size()); + uint64_t fieldCount = 0; + (void)parcel.ReadUInt64(fieldCount); + if (fieldCount > DBConstant::MAX_COLUMN || parcel.IsError()) { + return -E_PARSE_FAIL; + } + for (size_t i = 0; i < fieldCount; ++i) { + DataValue dataValue; + int errCode = DeserializeDataValue(dataValue, parcel); + if (errCode != E_OK) { + LOGD("[DataTransformer][DeSerializeValue] deSerialize failed"); + return errCode; + } + optionalData.push_back(std::move(dataValue)); + } + return E_OK; +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/data_transformer.h b/mock/distributeddb/storage/src/data_transformer.h new file mode 100644 index 0000000000000000000000000000000000000000..7a48276f74b292b7486ada8c0df2a5833a9c59d9 --- /dev/null +++ b/mock/distributeddb/storage/src/data_transformer.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef DATA_TRANSFORMER_H +#define DATA_TRANSFORMER_H +#ifdef RELATIONAL_STORE + +#include +#include "data_value.h" +#include "db_types.h" +#include "relational_schema_object.h" + +namespace DistributedDB { +using RowData = std::vector; +using OptRowData = std::vector; + +struct LogInfo { + int64_t dataKey = -1; + std::string device; + std::string originDev; + Timestamp timestamp = 0; + Timestamp wTimestamp = 0; + uint64_t flag = 0; + Key hashKey; // primary key hash value +}; + +struct RowDataWithLog { + LogInfo logInfo; + RowData rowData; +}; + +struct OptRowDataWithLog { + LogInfo logInfo; + OptRowData optionalData; +}; + +struct TableDataWithLog { + std::string tableName; + std::vector dataList; +}; + +struct OptTableDataWithLog { + std::string tableName; + std::vector dataList; +}; + +class DataTransformer { +public: + static int TransformTableData(const TableDataWithLog &tableDataWithLog, + const std::vector &fieldInfoList, std::vector &dataItems); + static int TransformDataItem(const std::vector &dataItems, const std::vector &remoteFieldInfo, + const std::vector &localFieldInfo, OptTableDataWithLog &tableDataWithLog); + + static int SerializeDataItem(const RowDataWithLog &data, const std::vector &fieldInfo, + DataItem &dataItem); + static int DeSerializeDataItem(const DataItem &dataItem, OptRowDataWithLog &data, + const std::vector &remoteFieldInfo); + static void ReduceMapping(const std::vector &remoteFieldInfo, + const std::vector &localFieldInfo); + +private: + static int SerializeValue(Value &value, const RowData &rowData, const std::vector &fieldInfoList); + static int DeSerializeValue(const Value &value, OptRowData &optionalData, + const std::vector &remoteFieldInfo); + + static uint32_t CalDataValueLength(const DataValue &dataValue); +}; +} + +#endif +#endif // DATA_TRANSFORMER_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/db_properties.cpp b/mock/distributeddb/storage/src/db_properties.cpp new file mode 100644 index 0000000000000000000000000000000000000000..86fd161bd81a96391195ee8c172817bea6c7e422 --- /dev/null +++ b/mock/distributeddb/storage/src/db_properties.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "db_common.h" +#include "db_properties.h" + +namespace DistributedDB { +const std::string DBProperties::CREATE_IF_NECESSARY = "createIfNecessary"; +const std::string DBProperties::DATABASE_TYPE = "databaseType"; +const std::string DBProperties::DATA_DIR = "dataDir"; +const std::string DBProperties::USER_ID = "userId"; +const std::string DBProperties::APP_ID = "appId"; +const std::string DBProperties::STORE_ID = "storeId"; +const std::string DBProperties::IDENTIFIER_DATA = "identifier"; +const std::string DBProperties::IDENTIFIER_DIR = "identifierDir"; +const std::string DBProperties::DUAL_TUPLE_IDENTIFIER_DATA = "dualTupleIdentifier"; +const std::string DBProperties::SYNC_DUAL_TUPLE_MODE = "syncDualTuple"; + +std::string DBProperties::GetStringProp(const std::string &name, const std::string &defaultValue) const +{ + auto iter = stringProperties_.find(name); + if (iter != stringProperties_.end()) { + return iter->second; + } + return defaultValue; +} + +void DBProperties::SetStringProp(const std::string &name, const std::string &value) +{ + stringProperties_[name] = value; +} + +bool DBProperties::GetBoolProp(const std::string &name, bool defaultValue) const +{ + auto iter = boolProperties_.find(name); + if (iter != boolProperties_.end()) { + return iter->second; + } + return defaultValue; +} + +void DBProperties::SetBoolProp(const std::string &name, bool value) +{ + boolProperties_[name] = value; +} + +int DBProperties::GetIntProp(const std::string &name, int defaultValue) const +{ + auto iter = intProperties_.find(name); + if (iter != intProperties_.end()) { + return iter->second; + } + return defaultValue; +} + +void DBProperties::SetIntProp(const std::string &name, int value) +{ + intProperties_[name] = value; +} + +void DBProperties::SetIdentifier(const std::string &userId, const std::string &appId, const std::string &storeId) +{ + SetStringProp(DBProperties::APP_ID, appId); + SetStringProp(DBProperties::USER_ID, userId); + SetStringProp(DBProperties::STORE_ID, storeId); + std::string hashIdentifier = DBCommon::TransferHashString(DBCommon::GenerateIdentifierId(storeId, appId, userId)); + SetStringProp(DBProperties::IDENTIFIER_DATA, hashIdentifier); + std::string dualIdentifier = DBCommon::TransferHashString(DBCommon::GenerateDualTupleIdentifierId(storeId, appId)); + SetStringProp(DBProperties::DUAL_TUPLE_IDENTIFIER_DATA, dualIdentifier); +} +} \ No newline at end of file diff --git a/mock/distributeddb/storage/src/default_factory.cpp b/mock/distributeddb/storage/src/default_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0667c552143ca209e7e6b1a3ca107614004fb19c --- /dev/null +++ b/mock/distributeddb/storage/src/default_factory.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "default_factory.h" + +#include + +#include "db_errno.h" +#include "sqlite_local_kvdb.h" +#ifndef OMIT_MULTI_VER +#include "multi_ver_natural_store.h" +#include "multi_ver_natural_store_commit_storage.h" +#endif +#include "sqlite_single_ver_natural_store.h" +#ifndef OMIT_MULTI_VER +#include "sqlite_multi_ver_data_storage.h" +#endif + +namespace DistributedDB { +IKvDB *DefaultFactory::CreateKvDb(KvDBType kvDbType, int &errCode) +{ + switch (kvDbType) { + case LOCAL_KVDB: + return CreateLocalKvDB(errCode); + case SINGER_VER_KVDB: + return CreateSingleVerNaturalStore(errCode); +#ifndef OMIT_MULTI_VER + case MULTI_VER_KVDB: + return CreateMultiVerNaturalStore(errCode); +#endif + default: + errCode = -E_INVALID_ARGS; + return nullptr; + } +} + +IKvDB *DefaultFactory::CreateLocalKvDB(int &errCode) +{ + IKvDB *kvDb = new (std::nothrow) SQLiteLocalKvDB(); + errCode = ((kvDb == nullptr) ? -E_OUT_OF_MEMORY : E_OK); + return kvDb; +} + +#ifndef OMIT_MULTI_VER +// Create the multi-version natural store, it contains a commit version and commit storage kvdb. +IKvDB *DefaultFactory::CreateMultiVerNaturalStore(int &errCode) +{ + IKvDB *kvDb = new (std::nothrow) MultiVerNaturalStore(); + errCode = ((kvDb == nullptr) ? -E_OUT_OF_MEMORY : E_OK); + return kvDb; +} +#endif + +// Create the single version natural store. +IKvDB *DefaultFactory::CreateSingleVerNaturalStore(int &errCode) +{ + IKvDB *kvDb = new (std::nothrow) SQLiteSingleVerNaturalStore(); + errCode = ((kvDb == nullptr) ? -E_OUT_OF_MEMORY : E_OK); + return kvDb; +} + +// Create a key-value database for commit storage module. +IKvDB *DefaultFactory::CreateCommitStorageDB(int &errCode) +{ + return CreateLocalKvDB(errCode); +} + +#ifndef OMIT_MULTI_VER +IKvDBMultiVerDataStorage *DefaultFactory::CreateMultiVerStorage(int &errCode) +{ + IKvDBMultiVerDataStorage *multiStorage = new (std::nothrow) SQLiteMultiVerDataStorage(); + errCode = ((multiStorage == nullptr) ? -E_OUT_OF_MEMORY : E_OK); + return multiStorage; +} + +// Create the commit storage. The object can be deleted directly when it is not needed. +IKvDBCommitStorage *DefaultFactory::CreateMultiVerCommitStorage(int &errCode) +{ + IKvDBCommitStorage *commitStorage = new (std::nothrow) MultiVerNaturalStoreCommitStorage(); + errCode = ((commitStorage == nullptr) ? -E_OUT_OF_MEMORY : E_OK); + return commitStorage; +} +#endif +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/default_factory.h b/mock/distributeddb/storage/src/default_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..975a00679db774a778bfa0f504085b6ba216185c --- /dev/null +++ b/mock/distributeddb/storage/src/default_factory.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DEFAULT_FACTORY_H +#define DEFAULT_FACTORY_H + +#include "ikvdb.h" +#include "ikvdb_factory.h" + +namespace DistributedDB { +class DefaultFactory final : public IKvDBFactory { +public: + DefaultFactory() noexcept {} + ~DefaultFactory() override {} + + DISABLE_COPY_ASSIGN_MOVE(DefaultFactory); + IKvDB *CreateKvDb(KvDBType kvDbType, int &errCode) override; + + // Create a key-value database for commit storage module. + IKvDB *CreateCommitStorageDB(int &errCode) override; + +#ifndef OMIT_MULTI_VER + // Create the multi version storage for multi version natural store + IKvDBMultiVerDataStorage *CreateMultiVerStorage(int &errCode) override; + + // Create the commit storage. The object can be deleted directly when it is not needed. + IKvDBCommitStorage *CreateMultiVerCommitStorage(int &errCode) override; +#endif +private: + // Create the a local kv db + IKvDB *CreateLocalKvDB(int &errCode); + +#ifndef OMIT_MULTI_VER + // Create the a natural store, it contains a commit version and commit storage kvdb. + IKvDB *CreateMultiVerNaturalStore(int &errCode); +#endif + + IKvDB *CreateSingleVerNaturalStore(int &errCode); +}; +} // namespace DistributedDB +#endif // DEFAULT_FACTORY_H diff --git a/mock/distributeddb/storage/src/generic_kvdb.cpp b/mock/distributeddb/storage/src/generic_kvdb.cpp new file mode 100644 index 0000000000000000000000000000000000000000..556a3fdc051fab10f6d7dbbb44f56e1b732956e9 --- /dev/null +++ b/mock/distributeddb/storage/src/generic_kvdb.cpp @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "generic_kvdb.h" +#include "platform_specific.h" +#include "log_print.h" +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "package_file.h" +#include "runtime_context.h" +#include "kvdb_utils.h" +#include "kvdb_commit_notify_filterable_data.h" + +namespace DistributedDB { +DEFINE_OBJECT_TAG_FACILITIES(GenericKvDB); + +GenericKvDB::GenericKvDB() + : performance_(nullptr), + eventNotifyCounter_(0), + connectionCount_(0), + notificationChain_(nullptr), + operatePerm_(OperatePerm::NORMAL_PERM) +{} + +GenericKvDB::~GenericKvDB() +{ + if (connectionCount_ > 0) { + LOGF("KvDB destructed with connection count > 0."); + } + + if (notificationChain_ != nullptr) { + RefObject::KillAndDecObjRef(notificationChain_); + notificationChain_ = nullptr; + } +} + +const KvDBProperties &GenericKvDB::GetMyProperties() const +{ + return MyProp(); +} + +IKvDBConnection *GenericKvDB::GetDBConnection(int &errCode) +{ + std::lock_guard lock(connectMutex_); + if (operatePerm_ != OperatePerm::NORMAL_PERM) { + errCode = (operatePerm_ == OperatePerm::DISABLE_PERM) ? -E_STALE : -E_BUSY; + return nullptr; + } + + GenericKvDBConnection *connection = NewConnection(errCode); + if (connection != nullptr) { + IncObjRef(this); + IncreaseConnectionCounter(); + } + return connection; +} + +void GenericKvDB::OnClose(const std::function ¬ifier) +{ + AutoLock lockGuard(this); + if (notifier) { + closeNotifiers_.push_back(notifier); + } else { + LOGW("Register kvdb 'Close()' notifier failed, notifier is null."); + } +} + +std::string GenericKvDB::GetStoreId() const +{ + return MyProp().GetStringProp(KvDBProperties::STORE_ID, ""); +} + +void GenericKvDB::DelConnection(GenericKvDBConnection *connection) +{ + delete connection; + connection = nullptr; +} + +void GenericKvDB::ReleaseDBConnection(GenericKvDBConnection *connection) +{ + if (connectionCount_.load() == 1) { + SetConnectionFlag(false); + } + + connectMutex_.lock(); + if (connection != nullptr) { + connection->SetSafeDeleted(); + DelConnection(connection); + DecreaseConnectionCounter(); + connectMutex_.unlock(); + DecObjRef(this); + } else { + connectMutex_.unlock(); + } +} + +void GenericKvDB::CommitNotify(int notifyEvent, KvDBCommitNotifyFilterAbleData *data) +{ + if (notificationChain_ == nullptr) { + LOGE("Failed to do commit notify, notificationChain_ is nullptr."); + return; + } + ++eventNotifyCounter_; + if (data == nullptr) { + notificationChain_->NotifyEvent(static_cast(notifyEvent), nullptr); + } else { + data->SetMyDb(this, eventNotifyCounter_); + data->IncObjRef(data); + int errCode = RuntimeContext::GetInstance()->ScheduleQueuedTask(GetStoreId(), + std::bind(&GenericKvDB::CommitNotifyAsync, this, notifyEvent, data)); + if (errCode != E_OK) { + LOGE("Failed to do commit notify, schedule task err:%d.", errCode); + data->DecObjRef(data); + data = nullptr; + } + } +} + +void GenericKvDB::CorruptNotifyAsync() const +{ + { + std::lock_guard lock(corruptMutex_); + if (corruptHandler_) { + corruptHandler_(); + } + } + + DecObjRef(this); +} + +void GenericKvDB::CorruptNotify() const +{ + IncObjRef(this); + int errCode = RuntimeContext::GetInstance()->ScheduleQueuedTask(GetStoreId(), + std::bind(&GenericKvDB::CorruptNotifyAsync, this)); + if (errCode != E_OK) { + LOGE("Failed to do the corrupt notify, schedule task err:%d.", errCode); + DecObjRef(this); + } +} + +int GenericKvDB::TryToDisableConnection(OperatePerm perm) +{ + std::lock_guard lock(connectMutex_); + if (operatePerm_ != OperatePerm::NORMAL_PERM) { + return -E_BUSY; + } + // more than one connection, should prevent the rekey operation. + if (connectionCount_ > 1) { + return -E_BUSY; + } + + operatePerm_ = perm; + return E_OK; +} + +void GenericKvDB::ReEnableConnection(OperatePerm perm) +{ + std::lock_guard lock(connectMutex_); + if (perm == operatePerm_) { + operatePerm_ = OperatePerm::NORMAL_PERM; + } +} + +NotificationChain::Listener *GenericKvDB::RegisterEventListener(EventType type, + const NotificationChain::Listener::OnEvent &onEvent, + const NotificationChain::Listener::OnFinalize &onFinalize, int &errCode) +{ + NotificationChain::Listener *listener = nullptr; + if (notificationChain_ != nullptr) { + listener = notificationChain_->RegisterListener(type, onEvent, onFinalize, errCode); + } else { + errCode = -E_NOT_PERMIT; + } + return listener; +} + +uint64_t GenericKvDB::GetEventNotifyCounter() const +{ + return eventNotifyCounter_; +} + +// Called when a new connection created. +void GenericKvDB::IncreaseConnectionCounter() +{ + connectionCount_.fetch_add(1, std::memory_order_seq_cst); + if (connectionCount_.load() > 0) { + SetConnectionFlag(true); + } +} + +// Called when a connection released. +void GenericKvDB::DecreaseConnectionCounter() +{ + int count = connectionCount_.fetch_sub(1, std::memory_order_seq_cst); + if (count <= 0) { + LOGF("Decrease kvdb connection counter failed, count <= 0."); + return; + } + if (count != 1) { + return; + } + + operatePerm_ = OperatePerm::DISABLE_PERM; + LockObj(); + auto notifiers = std::move(closeNotifiers_); + UnlockObj(); + + for (auto ¬ifier : notifiers) { + if (notifier) { + notifier(); + } + } + + Close(); +} + +// Register a new notification event type. +int GenericKvDB::RegisterNotificationEventType(int eventType) +{ + if (notificationChain_ == nullptr) { + // Lazy init. + notificationChain_ = new (std::nothrow) NotificationChain; + if (notificationChain_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + } + // We DON'T release 'notificationChain_' here event if RegisterEventType() + // is failed, it belongs to the class object and is released in the destructor. + return notificationChain_->RegisterEventType(static_cast(eventType)); +} + +// Unregister a notification event type. +void GenericKvDB::UnRegisterNotificationEventType(int eventType) +{ + if (notificationChain_ != nullptr) { + notificationChain_->UnRegisterEventType(static_cast(eventType)); + } +} + +const KvDBProperties &GenericKvDB::MyProp() const +{ + return properties_; +} + +KvDBProperties &GenericKvDB::MyProp() +{ + return properties_; +} + +int GenericKvDB::GetWorkDir(const KvDBProperties &kvDBProp, std::string &workDir) +{ + std::string origDataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifierDir = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + if (origDataDir.empty()) { + return -E_INVALID_ARGS; + } + + workDir = origDataDir + "/" + identifierDir; + return E_OK; +} + +void GenericKvDB::SetCorruptHandler(const DatabaseCorruptHandler &handler) +{ + std::lock_guard lock(corruptMutex_); + corruptHandler_ = handler; +} + +void GenericKvDB::OpenPerformanceAnalysis() +{ + if (performance_ != nullptr) { + performance_->OpenPerformanceAnalysis(); + } +} + +void GenericKvDB::ClosePerformanceAnalysis() +{ + if (performance_ != nullptr) { + performance_->ClosePerformanceAnalysis(); + } +} + +void GenericKvDB::CommitNotifyAsync(int notifyEvent, KvDBCommitNotifyFilterAbleData *data) +{ + notificationChain_->NotifyEvent(static_cast(notifyEvent), data); + data->DecObjRef(data); + data = nullptr; +} + +int GenericKvDB::RegisterFunction(RegisterFuncType type) +{ + if (type >= REGISTER_FUNC_TYPE_MAX) { + return -E_NOT_SUPPORT; + } + std::lock_guard lock(regFuncCountMutex_); + if (registerFunctionCount_.empty()) { + registerFunctionCount_.resize(static_cast(REGISTER_FUNC_TYPE_MAX), 0); + if (registerFunctionCount_.size() != static_cast(REGISTER_FUNC_TYPE_MAX)) { + return -E_OUT_OF_MEMORY; + } + } + registerFunctionCount_[type]++; + return E_OK; +} + +int GenericKvDB::UnregisterFunction(RegisterFuncType type) +{ + if (type >= REGISTER_FUNC_TYPE_MAX) { + return -E_NOT_SUPPORT; + } + std::lock_guard lock(regFuncCountMutex_); + if (registerFunctionCount_.size() != static_cast(REGISTER_FUNC_TYPE_MAX) || + registerFunctionCount_[type] == 0) { + return -E_UNEXPECTED_DATA; + } + registerFunctionCount_[type]--; + return E_OK; +} + +uint32_t GenericKvDB::GetRegisterFunctionCount(RegisterFuncType type) const +{ + std::lock_guard lock(regFuncCountMutex_); + if (type >= REGISTER_FUNC_TYPE_MAX || + registerFunctionCount_.size() != static_cast(REGISTER_FUNC_TYPE_MAX)) { + return 0; + } + return registerFunctionCount_[type]; +} + +int GenericKvDB::TransObserverTypeToRegisterFunctionType(int observerType, RegisterFuncType &type) const +{ + (void)observerType; + (void)type; + return -E_NOT_SUPPORT; +} + +int GenericKvDB::TransConflictTypeToRegisterFunctionType(int conflictType, RegisterFuncType &type) const +{ + (void)conflictType; + (void)type; + return -E_NOT_SUPPORT; +} + +int GenericKvDB::CheckDataStatus(const Key &key, const Value &value, bool isDeleted) const +{ + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE || + value.size() > DBConstant::MAX_VALUE_SIZE) { + return -E_INVALID_ARGS; + } + return E_OK; +} + +bool GenericKvDB::CheckWritePermission() const +{ + return true; +} + +bool GenericKvDB::IsDataMigrating() const +{ + return false; +} + +void GenericKvDB::SetConnectionFlag(bool isExisted) const +{ + (void)isExisted; + return; +} + +int GenericKvDB::CheckIntegrity() const +{ + return E_OK; +} + +void GenericKvDB::GetStoreDirectory(const KvDBProperties &properties, int dbType, + std::string &storeDir, std::string &storeOnlyDir) const +{ + std::string identifierDir = properties.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + std::string dataDir = properties.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string subDir = KvDBProperties::GetStoreSubDirectory(dbType); + + std::string storeOnlyIdentifier = GetStoreIdOnlyIdentifier(properties); + storeOnlyDir = dataDir + "/" + storeOnlyIdentifier + "/" + subDir + "/"; + storeDir = dataDir + "/" + identifierDir + "/" + subDir + "/"; +} + +std::string GenericKvDB::GetStoreIdOnlyIdentifier(const KvDBProperties &properties) const +{ + std::string storeId = properties.GetStringProp(KvDBProperties::STORE_ID, ""); + std::string hashStoreId = DBCommon::TransferHashString(storeId); + std::string hashStoreDir = DBCommon::TransferStringToHex(hashStoreId); + return hashStoreDir; +} + +std::string GenericKvDB::GetStorePath() const +{ + return properties_.GetStringProp(KvDBProperties::DATA_DIR, ""); +} + +void GenericKvDB::Dump(int fd) +{ +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/generic_kvdb.h b/mock/distributeddb/storage/src/generic_kvdb.h new file mode 100644 index 0000000000000000000000000000000000000000..13218efc25e5774795d607c5a41ab0652a9d15e0 --- /dev/null +++ b/mock/distributeddb/storage/src/generic_kvdb.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GENERIC_KVDB_H +#define GENERIC_KVDB_H + +#include +#include +#include +#include + +#include "store_types.h" +#include "version.h" +#include "ikvdb.h" +#include "generic_kvdb_connection.h" +#include "performance_analysis.h" +#include "kvdb_conflict_entry.h" +#include "db_types.h" + +namespace DistributedDB { +class KvDBCommitNotifyFilterAbleData; + +struct ImportFileInfo { + std::string backupDir; // the directory of the current database backup + std::string unpackedDir; // the directory of the unpacked import file + std::string currentDir; // the directory of the current database + std::string curValidFile; // the file imply that the current directory is valid + std::string backValidFile; // the file imply that the backup directory is valid +}; + +enum RegisterFuncType { + OBSERVER_SINGLE_VERSION_NS_PUT_EVENT = 0, + OBSERVER_SINGLE_VERSION_NS_SYNC_EVENT, + OBSERVER_SINGLE_VERSION_NS_LOCAL_EVENT, + OBSERVER_SINGLE_VERSION_NS_CONFLICT_EVENT, + OBSERVER_MULTI_VERSION_NS_COMMIT_EVENT, + CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ONLY, + CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ORIG, + CONFLICT_SINGLE_VERSION_NS_NATIVE_ALL, + REGISTER_FUNC_TYPE_MAX +}; + +class GenericKvDB : public IKvDB { +public: + GenericKvDB(); + ~GenericKvDB() override; + + DISABLE_COPY_ASSIGN_MOVE(GenericKvDB); + + // Get properties of this database. + const KvDBProperties &GetMyProperties() const override; + + // Create a db connection. + IKvDBConnection *GetDBConnection(int &errCode) final; + + // Called when all connections of this database closed. + void OnClose(const std::function ¬ifier) final; + + // Publish event when a commit action happened. + virtual void CommitNotify(int notifyEvent, KvDBCommitNotifyFilterAbleData *data); + + // Invoked automatically when connection count is zero + virtual void Close() = 0; + + virtual int TryToDisableConnection(OperatePerm perm); + + virtual void ReEnableConnection(OperatePerm perm); + + virtual int Rekey(const CipherPassword &passwd) = 0; + + // Empty passwords represent non-encrypted files. + // Export existing database files to a specified database file in the specified directory. + virtual int Export(const std::string &filePath, const CipherPassword &passwd) = 0; + + // Import the existing database files to the specified database file in the specified directory. + virtual int Import(const std::string &filePath, const CipherPassword &passwd) = 0; + + // Release a db connection. + void ReleaseDBConnection(GenericKvDBConnection *connection); + + // Register an event listener. + NotificationChain::Listener *RegisterEventListener(EventType type, + const NotificationChain::Listener::OnEvent &onEvent, + const NotificationChain::Listener::OnFinalize &onFinalize, int &errCode); + + // Get event notify counter. + uint64_t GetEventNotifyCounter() const; + + void OpenPerformanceAnalysis() override; + + void ClosePerformanceAnalysis() override; + + void WakeUpSyncer() override {}; + + void EnableAutonomicUpgrade() override {}; + + void SetCorruptHandler(const DatabaseCorruptHandler &handler) override; + + int RegisterFunction(RegisterFuncType type); + + int UnregisterFunction(RegisterFuncType type); + + uint32_t GetRegisterFunctionCount(RegisterFuncType type) const; + + virtual int TransObserverTypeToRegisterFunctionType(int observerType, RegisterFuncType &type) const; + + virtual int TransConflictTypeToRegisterFunctionType(int conflictType, RegisterFuncType &type) const; + + virtual int CheckDataStatus(const Key &key, const Value &value, bool isDeleted) const; + + virtual bool CheckWritePermission() const; + + virtual bool IsDataMigrating() const; + + virtual void SetConnectionFlag(bool isExisted) const; + + int CheckIntegrity() const override; + + std::string GetStorePath() const override; + + void Dump(int fd) override; + +protected: + // Create a connection object, no DB ref increased. + virtual GenericKvDBConnection *NewConnection(int &errCode) = 0; + + // Delete a connection object. + virtual void DelConnection(GenericKvDBConnection *connection); + + // Called when a new connection created. + void IncreaseConnectionCounter(); + + // Called when a connection released. + void DecreaseConnectionCounter(); + + // Register a new notification event type. + int RegisterNotificationEventType(int eventType); + + // Unregister a notification event type. + void UnRegisterNotificationEventType(int eventType); + + // Access 'properties_' for derived class. + const KvDBProperties &MyProp() const; + KvDBProperties &MyProp(); + + static int GetWorkDir(const KvDBProperties &kvDBProp, std::string &workDir); + + void CorruptNotify() const; + + std::string GetStoreIdOnlyIdentifier(const KvDBProperties &properties) const; + + void GetStoreDirectory(const KvDBProperties &properties, int dbType, + std::string &storeDir, std::string &storeOnlyDir) const; + + PerformanceAnalysis *performance_; + DatabaseCorruptHandler corruptHandler_; + DeviceID devId_; + +private: + // Do commit notify in task pool. + void CommitNotifyAsync(int notifyEvent, KvDBCommitNotifyFilterAbleData *data); + + void CorruptNotifyAsync() const; + + // Get the ID of this kvdb. + std::string GetStoreId() const; + + DECLARE_OBJECT_TAG(GenericKvDB); + + // Databasse event notify counter. + std::atomic eventNotifyCounter_; + + // Fields for tracking the connection count and invoking callbacks. + std::atomic connectionCount_; + std::vector> closeNotifiers_; + NotificationChain *notificationChain_; + KvDBProperties properties_; + std::mutex connectMutex_; + mutable std::mutex corruptMutex_; + OperatePerm operatePerm_; + mutable std::mutex regFuncCountMutex_; + std::vector registerFunctionCount_; +}; +} // namespace DistributedDB + +#endif // GENERIC_KVDB_H diff --git a/mock/distributeddb/storage/src/generic_kvdb_connection.cpp b/mock/distributeddb/storage/src/generic_kvdb_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..464d9850aec86cacb01de5ac0d122bb3a17c8d9d --- /dev/null +++ b/mock/distributeddb/storage/src/generic_kvdb_connection.cpp @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "generic_kvdb_connection.h" + +#include + +#include "log_print.h" +#include "db_constant.h" +#include "db_errno.h" +#include "generic_kvdb.h" +#include "kvdb_observer_handle.h" +#include "kvdb_commit_notify_filterable_data.h" + +namespace DistributedDB { +GenericKvDBConnection::GenericKvDBConnection(GenericKvDB *kvDB) + : kvDB_(kvDB), + isExclusive_(false), + isSafeDeleted_(false) +{ +} + +GenericKvDBConnection::~GenericKvDBConnection() +{ + if (!isSafeDeleted_) { + LOGF("The connection is deleted directly by user."); + } + + for (auto &observer : observerList_) { + delete observer; + observer = nullptr; + } +} + +int GenericKvDBConnection::RegisterObserverForOneType(int type, const Key &key, const KvDBObserverAction &action, + NotificationChain::Listener *&listener) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_CONNECTION; + } + RegisterFuncType funcType = REGISTER_FUNC_TYPE_MAX; + int errCode = kvDB_->TransObserverTypeToRegisterFunctionType(type, funcType); + if (errCode != E_OK) { + return errCode; + } + errCode = kvDB_->RegisterFunction(funcType); + if (errCode != E_OK) { + return errCode; + } + listener = RegisterSpecialListener(type, key, action, false, errCode); + if (listener == nullptr) { + (void)(kvDB_->UnregisterFunction(funcType)); + return errCode; + } + return E_OK; +} + +KvDBObserverHandle *GenericKvDBConnection::RegisterObserver(unsigned mode, + const Key &key, const KvDBObserverAction &action, int &errCode) +{ + if (!action || key.size() > DBConstant::MAX_KEY_SIZE) { + errCode = -E_INVALID_ARGS; + return nullptr; + } + std::list eventTypes; + errCode = GetEventType(mode, eventTypes); + if (errCode != E_OK) { + return nullptr; + } + + std::lock_guard lockGuard(observerListLock_); + if (observerList_.size() >= MAX_OBSERVER_COUNT) { + errCode = -E_MAX_LIMITS; + LOGE("The number of observers has been larger than 'MAX_OBSERVER_COUNT'!"); + return nullptr; + } + if (isExclusive_.load()) { + errCode = -E_BUSY; + return nullptr; + } + auto observerHandle = new (std::nothrow) KvDBObserverHandle(mode); + if (observerHandle == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + + std::list listenerList; + for (const auto &type : eventTypes) { + NotificationChain::Listener *listenerObj = nullptr; + // Register function count in db is also protected by observer list lock. + errCode = RegisterObserverForOneType(type, key, action, listenerObj); + if (errCode != E_OK) { + for (auto &listener : listenerList) { + listener->Drop(); + } + LOGE("Register observer failed, register listener failed, err:'%d'.", errCode); + delete observerHandle; + observerHandle = nullptr; + return nullptr; + } + listenerList.push_back(listenerObj); + } + + for (auto &listener : listenerList) { + observerHandle->InsertListener(listener); + } + observerList_.push_back(observerHandle); + errCode = E_OK; + return observerHandle; +} + +int GenericKvDBConnection::UnRegisterObserver(const KvDBObserverHandle *observerHandle) +{ + if (observerHandle == nullptr) { + return -E_INVALID_ARGS; + } + + if (kvDB_ == nullptr) { + return -E_INVALID_CONNECTION; + } + + std::list eventTypes; + int errCode = GetEventType(observerHandle->GetObserverMode(), eventTypes); + if (errCode != E_OK) { + return errCode; + } + + { + std::lock_guard lockGuard(observerListLock_); + auto observerIter = std::find(observerList_.begin(), observerList_.end(), observerHandle); + if (observerIter == observerList_.end()) { + LOGE("Unregister observer failed, no such entry."); + return -E_NO_SUCH_ENTRY; + } + observerList_.erase(observerIter); + // Register function count in db is also protected by observer list lock. + RegisterFuncType funcType = REGISTER_FUNC_TYPE_MAX; + for (auto type : eventTypes) { + errCode = kvDB_->TransObserverTypeToRegisterFunctionType(type, funcType); + if (errCode != E_OK) { + LOGE("Get register function type failed, err:'%d'.", errCode); + continue; + } + errCode = kvDB_->UnregisterFunction(funcType); + if (errCode != E_OK) { + LOGE("Unregister function failed, err:'%d'.", errCode); + continue; + } + } + } + + delete observerHandle; + observerHandle = nullptr; + return E_OK; +} + +int GenericKvDBConnection::SetConflictNotifier(int conflictType, const KvDBConflictAction &action) +{ + (void)conflictType; + (void)action; + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::Close() +{ + if (kvDB_ == nullptr) { + return -E_INVALID_CONNECTION; + } + + if (isExclusive_.load()) { + return -E_BUSY; + } + if (kvDB_->IsDataMigrating()) { + return -E_BUSY; + } + + int errCode = PreClose(); + if (errCode != E_OK) { + LOGE("Close connection failed, err:'%d'.", errCode); + return errCode; + } + kvDB_->ReleaseDBConnection(this); + return E_OK; +} + +std::string GenericKvDBConnection::GetIdentifier() const +{ + if (kvDB_ == nullptr) { + return ""; + } + return kvDB_->GetMyProperties().GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); +} + +int GenericKvDBConnection::Pragma(int cmd, void *parameter) +{ + (void)cmd; + (void)parameter; + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::PreClose() +{ + return E_OK; +} + +void GenericKvDBConnection::SetSafeDeleted() +{ + isSafeDeleted_ = true; +} + +int GenericKvDBConnection::GetEntries(const IOption &option, const Key &keyPrefix, std::vector &entries) const +{ + (void)option; + (void)keyPrefix; + (void)entries; + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::GetEntries(const IOption &option, const Query &query, std::vector &entries) const +{ + (void)option; + (void)query; + (void)entries; + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::GetResultSet(const IOption &option, const Key &keyPrefix, IKvDBResultSet *&resultSet) const +{ + (void)option; + (void)keyPrefix; + (void)resultSet; + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::GetResultSet(const IOption &option, const Query &query, IKvDBResultSet *&resultSet) const +{ + (void)option; + (void)query; + (void)resultSet; + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::GetCount(const IOption &option, const Query &query, int &count) const +{ + (void)option; + (void)query; + (void)count; + return -E_NOT_SUPPORT; +} + +void GenericKvDBConnection::ReleaseResultSet(IKvDBResultSet *&resultSet) +{ + (void)resultSet; + return; +} + +int GenericKvDBConnection::RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) +{ + (void)notifier; + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::GetSecurityOption(int &securityLabel, int &securityFlag) const +{ + if (kvDB_ == nullptr) { + return -E_INVALID_CONNECTION; + } + securityLabel = kvDB_->GetMyProperties().GetIntProp(KvDBProperties::SECURITY_LABEL, 0); + securityFlag = kvDB_->GetMyProperties().GetIntProp(KvDBProperties::SECURITY_FLAG, 0); + return E_OK; +} + +NotificationChain::Listener *GenericKvDBConnection::RegisterSpecialListener(int type, + const Key &key, const KvDBObserverAction &action, bool conflict, int &errCode) +{ + if (!action) { + errCode = -E_INVALID_ARGS; + return nullptr; + } + + if (kvDB_ == nullptr) { + errCode = -E_INVALID_CONNECTION; + return nullptr; + } + + uint64_t notifyBarrier = kvDB_->GetEventNotifyCounter(); + return kvDB_->RegisterEventListener(static_cast(type), + [key, action, conflict, notifyBarrier](void *ptr) { + if (ptr == nullptr) { + return; + } + KvDBCommitNotifyFilterAbleData *data = static_cast(ptr); + if (data->GetNotifyID() <= notifyBarrier) { + return; + } + data->SetFilterKey(key); + if (conflict) { + if (!data->IsConflictedDataEmpty()) { + action(*data); + } + } else { + if (!data->IsChangedDataEmpty()) { + action(*data); + } + } + }, nullptr, errCode); +} + +int GenericKvDBConnection::PreCheckExclusiveStatus() +{ + std::lock_guard lockGuard(observerListLock_); + if (observerList_.empty()) { + isExclusive_.store(true); + return E_OK; + } + return -E_BUSY; +} + +void GenericKvDBConnection::ResetExclusiveStatus() +{ + isExclusive_.store(false); +} + +int GenericKvDBConnection::GetEventType(unsigned mode, std::list &eventTypes) const +{ + if (kvDB_ == nullptr) { + return -E_INVALID_CONNECTION; + } + + return TranslateObserverModeToEventTypes(mode, eventTypes); +} + +int GenericKvDBConnection::CheckIntegrity() const +{ + return E_OK; +} +} \ No newline at end of file diff --git a/mock/distributeddb/storage/src/generic_kvdb_connection.h b/mock/distributeddb/storage/src/generic_kvdb_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..e5e2378e1683cdf0f7a924d60f6d5a41043d2d51 --- /dev/null +++ b/mock/distributeddb/storage/src/generic_kvdb_connection.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GENERIC_KV_DB_CONNECTION_H +#define GENERIC_KV_DB_CONNECTION_H + +#include +#include +#include + +#include "ikvdb_connection.h" +#include "notification_chain.h" + +namespace DistributedDB { +class GenericKvDB; + +class GenericKvDBConnection : public IKvDBConnection { +public: + explicit GenericKvDBConnection(GenericKvDB *kvDB); + ~GenericKvDBConnection() override; + + DISABLE_COPY_ASSIGN_MOVE(GenericKvDBConnection); + + // Register observer. + KvDBObserverHandle *RegisterObserver(unsigned mode, const Key &key, + const KvDBObserverAction &action, int &errCode) override; + + // Unregister observer. + int UnRegisterObserver(const KvDBObserverHandle *observerHandle) override; + + // Register a conflict notifier. + int SetConflictNotifier(int conflictType, const KvDBConflictAction &action) override; + + // Close and release the connection. + int Close() final; + + std::string GetIdentifier() const override; + + // Pragma interface. + int Pragma(int cmd, void *parameter) override; + + // Parse event types(from observer mode). + virtual int TranslateObserverModeToEventTypes(unsigned mode, std::list &eventTypes) const = 0; + + // Set it to 'safe' state to delete the connection + void SetSafeDeleted(); + + int GetEntries(const IOption &option, const Key &keyPrefix, std::vector &entries) const override; + + int GetEntries(const IOption &option, const Query &query, std::vector &entries) const override; + + int GetResultSet(const IOption &option, const Key &keyPrefix, IKvDBResultSet *&resultSet) const override; + + int GetResultSet(const IOption &option, const Query &query, IKvDBResultSet *&resultSet) const override; + + int GetCount(const IOption &option, const Query &query, int &count) const override; + + void ReleaseResultSet(IKvDBResultSet *&resultSet) override; + + int RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) override; + + int GetSecurityOption(int &securityLabel, int &securityFlag) const override; + + int CheckIntegrity() const override; +protected: + // Get the stashed 'KvDB_ pointer' without ref. + template + DerivedDBType *GetDB() const + { + return static_cast(kvDB_); + } + + // Register an event listener with observer action data. + NotificationChain::Listener *RegisterSpecialListener(int type, const Key &key, + const KvDBObserverAction &action, bool conflict, int &errCode); + + virtual int PreCheckExclusiveStatus(); + + void ResetExclusiveStatus(); + + // Called in Close(), overriding of Close() is forbidden. + virtual int PreClose(); + + GenericKvDB *kvDB_; + std::atomic isExclusive_; + +private: + int GetEventType(unsigned mode, std::list &eventTypes) const; + + int RegisterObserverForOneType(int type, const Key &key, const KvDBObserverAction &action, + NotificationChain::Listener *&listener); + + // Soft limit of a connection observer count. + static constexpr int MAX_OBSERVER_COUNT = 8; + + bool isSafeDeleted_; + std::mutex observerListLock_; + std::list observerList_; +}; +} + +#endif // GENERIC_KV_DB_CONNECTION_H diff --git a/mock/distributeddb/storage/src/generic_single_ver_kv_entry.cpp b/mock/distributeddb/storage/src/generic_single_ver_kv_entry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c0963616f1253f0fd10c126f44d8152518e389d6 --- /dev/null +++ b/mock/distributeddb/storage/src/generic_single_ver_kv_entry.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "generic_single_ver_kv_entry.h" + +#include +#include "data_compression.h" +#include "db_errno.h" +#include "parcel.h" +#include "version.h" + +namespace DistributedDB { +GenericSingleVerKvEntry::GenericSingleVerKvEntry() +{ +} + +GenericSingleVerKvEntry::~GenericSingleVerKvEntry() +{ +} + +std::string GenericSingleVerKvEntry::GetOrigDevice() const +{ + return dataItem_.origDev; +} + +void GenericSingleVerKvEntry::SetOrigDevice(const std::string &dev) +{ + dataItem_.origDev = dev; +} + +Timestamp GenericSingleVerKvEntry::GetTimestamp() const +{ + return dataItem_.timestamp; +} + +void GenericSingleVerKvEntry::SetTimestamp(Timestamp time) +{ + dataItem_.timestamp = time; +} + +Timestamp GenericSingleVerKvEntry::GetWriteTimestamp() const +{ + return dataItem_.writeTimestamp; +} + +void GenericSingleVerKvEntry::SetWriteTimestamp(Timestamp time) +{ + dataItem_.writeTimestamp = time; +} + +void GenericSingleVerKvEntry::SetEntryData(DataItem &&dataItem) +{ + dataItem_ = dataItem; +} + +void GenericSingleVerKvEntry::GetKey(Key &key) const +{ + key = dataItem_.key; +} + +void GenericSingleVerKvEntry::GetHashKey(Key &key) const +{ + key = dataItem_.hashKey; +} + +const Key &GenericSingleVerKvEntry::GetKey() const +{ + return dataItem_.key; +} + +void GenericSingleVerKvEntry::GetValue(Value &value) const +{ + value = dataItem_.value; +} + +const Value &GenericSingleVerKvEntry::GetValue() const +{ + return dataItem_.value; +} + +uint64_t GenericSingleVerKvEntry::GetFlag() const +{ + return dataItem_.flag; +} + +void GenericSingleVerKvEntry::SetKey(const Key &key) +{ + dataItem_.key = key; +} + +void GenericSingleVerKvEntry::SetValue(const Value &value) +{ + dataItem_.value = value; +} + +void GenericSingleVerKvEntry::SetHashKey(const Key &hashKey) +{ + dataItem_.hashKey = hashKey; +} + +// this func should do compatible +int GenericSingleVerKvEntry::SerializeData(Parcel &parcel, uint32_t targetVersion) +{ + uint64_t len = 0; + int errCode = parcel.WriteUInt32(targetVersion); + if (errCode != E_OK) { + return errCode; + } + errCode = AdaptToVersion(OperType::SERIALIZE, targetVersion, parcel, len); + if (errCode != E_OK) { + return errCode; + } + return errCode; +} + +int GenericSingleVerKvEntry::SerializeDatas(const std::vector &kvEntries, Parcel &parcel, + uint32_t targetVersion) +{ + uint32_t size = kvEntries.size(); + int errCode = parcel.WriteUInt32(size); + if (errCode != E_OK) { + LOGE("[SerializeDatas] write entries size failed, errCode=%d.", errCode); + return errCode; + } + parcel.EightByteAlign(); + for (const auto &kvEntry : kvEntries) { + if (kvEntry == nullptr) { + continue; + } + errCode = kvEntry->SerializeData(parcel, targetVersion); + if (errCode != E_OK) { + LOGE("[SerializeDatas] write kvEntry failed, errCode=%d.", errCode); + return errCode; + } + } + return errCode; +} + +// this func should do compatible +uint32_t GenericSingleVerKvEntry::CalculateLen(uint32_t targetVersion) +{ + uint64_t len = 0; + int errCode = AdaptToVersion(OperType::CAL_LEN, targetVersion, len); + if ((len > INT32_MAX) || (errCode != E_OK)) { + return 0; + } + return len; +} + +uint32_t GenericSingleVerKvEntry::CalculateLens(const std::vector &kvEntries, + uint32_t targetVersion) +{ + uint64_t len = 0; + len += Parcel::GetUInt32Len(); + len = BYTE_8_ALIGN(len); + for (const auto &kvEntry : kvEntries) { + if (kvEntry == nullptr) { + continue; + } + len += kvEntry->CalculateLen(targetVersion); + if (len > INT32_MAX) { + return 0; + } + } + return len; +} + +// this func should do compatible +int GenericSingleVerKvEntry::DeSerializeData(Parcel &parcel) +{ + uint32_t version = VERSION_INVALID; + uint64_t len = parcel.ReadUInt32(version); + if (parcel.IsError()) { + return 0; + } + int errCode = AdaptToVersion(OperType::DESERIALIZE, version, parcel, len); + if (errCode != E_OK) { + len = 0; + } + return len; +} + +int GenericSingleVerKvEntry::DeSerializeDatas(std::vector &kvEntries, Parcel &parcel) +{ + uint64_t len = 0; + uint32_t size = 0; + len += parcel.ReadUInt32(size); + parcel.EightByteAlign(); + len = BYTE_8_ALIGN(len); + for (uint32_t i = 0; i < size; i++) { + auto kvEntry = new (std::nothrow) GenericSingleVerKvEntry(); + if (kvEntry == nullptr) { + LOGE("Create kvEntry failed."); + len = 0; + goto END; + } + len += kvEntry->DeSerializeData(parcel); + kvEntries.push_back(kvEntry); + if (len > INT32_MAX) { + len = 0; + goto END; + } + } +END: + if (len == 0) { + for (auto &kvEntry : kvEntries) { + delete kvEntry; + kvEntry = nullptr; + } + } + return len; +} + +int GenericSingleVerKvEntry::AdaptToVersion(OperType operType, uint32_t targetVersion, Parcel &parcel, + uint64_t &datalen) +{ + if (targetVersion < SOFTWARE_VERSION_EARLIEST || targetVersion > SOFTWARE_VERSION_CURRENT) { + return -E_VERSION_NOT_SUPPORT; + } + int errCode = E_OK; + switch (operType) { + case OperType::SERIALIZE: + errCode = SerializeDataByVersion(targetVersion, parcel); + break; + case OperType::DESERIALIZE: + errCode = DeSerializeByVersion(targetVersion, parcel, datalen); + break; + default: + LOGE("Unknown upgrade serialize oper!"); + return -E_UPGRADE_FAILED; + } + return errCode; +} + +int GenericSingleVerKvEntry::AdaptToVersion(OperType operType, uint32_t targetVersion, uint64_t &datalen) +{ + if (targetVersion < SOFTWARE_VERSION_EARLIEST || targetVersion > SOFTWARE_VERSION_CURRENT) { + return -E_VERSION_NOT_SUPPORT; + } + + if (operType == OperType::CAL_LEN) { + return CalLenByVersion(targetVersion, datalen); + } else { + LOGE("Unknown upgrade serialize oper!"); + return -E_UPGRADE_FAILED; + } +} + +int GenericSingleVerKvEntry::SerializeDataByFirstVersion(Parcel &parcel) const +{ + int errCode = parcel.WriteVectorChar(dataItem_.key); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteVectorChar(dataItem_.value); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt64(dataItem_.timestamp); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt64(dataItem_.flag); + if (errCode != E_OK) { + return errCode; + } + + return parcel.WriteString(dataItem_.origDev); +} + +int GenericSingleVerKvEntry::SerializeDataByLaterVersion(Parcel &parcel, uint32_t targetVersion) const +{ + Timestamp writeTimestamp = dataItem_.writeTimestamp; + if (writeTimestamp == 0) { + writeTimestamp = dataItem_.timestamp; + } + int errCode = parcel.WriteUInt64(writeTimestamp); + if (errCode != E_OK) { + return errCode; + } + if (targetVersion >= SOFTWARE_VERSION_RELEASE_6_0) { + errCode = parcel.WriteVector(dataItem_.hashKey); + } + return errCode; +} + +int GenericSingleVerKvEntry::SerializeDataByVersion(uint32_t targetVersion, Parcel &parcel) const +{ + int errCode = SerializeDataByFirstVersion(parcel); + if (targetVersion == SOFTWARE_VERSION_EARLIEST || errCode != E_OK) { + return errCode; + } + return SerializeDataByLaterVersion(parcel, targetVersion); +} + +void GenericSingleVerKvEntry::CalLenByFirstVersion(uint64_t &len) const +{ + len += Parcel::GetUInt32Len(); + len += Parcel::GetVectorCharLen(dataItem_.key); + len += Parcel::GetVectorCharLen(dataItem_.value); + len += Parcel::GetUInt64Len(); + len += Parcel::GetUInt64Len(); + len += Parcel::GetStringLen(dataItem_.origDev); +} + +void GenericSingleVerKvEntry::CalLenByLaterVersion(uint64_t &len, uint32_t targetVersion) const +{ + len += Parcel::GetUInt64Len(); + if (targetVersion >= SOFTWARE_VERSION_RELEASE_6_0) { + len += Parcel::GetVectorLen(dataItem_.hashKey); + } +} + +int GenericSingleVerKvEntry::CalLenByVersion(uint32_t targetVersion, uint64_t &len) const +{ + CalLenByFirstVersion(len); + if (targetVersion == SOFTWARE_VERSION_EARLIEST) { + return E_OK; + } + CalLenByLaterVersion(len, targetVersion); + return E_OK; +} + +void GenericSingleVerKvEntry::DeSerializeByFirstVersion(uint64_t &len, Parcel &parcel) +{ + len += parcel.ReadVectorChar(dataItem_.key); + len += parcel.ReadVectorChar(dataItem_.value); + len += parcel.ReadUInt64(dataItem_.timestamp); + len += parcel.ReadUInt64(dataItem_.flag); + len += parcel.ReadString(dataItem_.origDev); + dataItem_.writeTimestamp = dataItem_.timestamp; +} + +void GenericSingleVerKvEntry::DeSerializeByLaterVersion(uint64_t &len, Parcel &parcel, uint32_t targetVersion) +{ + len += parcel.ReadUInt64(dataItem_.writeTimestamp); + if (targetVersion >= SOFTWARE_VERSION_RELEASE_6_0) { + len += parcel.ReadVector(dataItem_.hashKey); + } +} + +int GenericSingleVerKvEntry::DeSerializeByVersion(uint32_t targetVersion, Parcel &parcel, uint64_t &len) +{ + DeSerializeByFirstVersion(len, parcel); + if (targetVersion == SOFTWARE_VERSION_EARLIEST) { + return E_OK; + } + DeSerializeByLaterVersion(len, parcel, targetVersion); + return E_OK; +} + +uint32_t GenericSingleVerKvEntry::CalculateCompressedLens(const std::vector &compressedData) +{ + // No compressed data in sync. + if (compressedData.empty()) { + return 0; + } + + // Calculate compressed data length. + uint64_t len = 0; + len += Parcel::GetUInt32Len(); // srcLen. + len += Parcel::GetUInt32Len(); // compression algorithm type. + len += Parcel::GetVectorLen(compressedData); // compressed data. + return (len > INT32_MAX) ? 0 : len; +} + +int GenericSingleVerKvEntry::Compress(const std::vector &kvEntries, std::vector &destData, + const CompressInfo &compressInfo) +{ + // Calculate length. + auto srcLen = CalculateLens(kvEntries, compressInfo.targetVersion); + if (srcLen == 0) { + LOGE("Over limit size, cannot compress."); + return -E_INVALID_ARGS; + } + + // Serialize data. + std::vector srcData(srcLen, 0); + Parcel parcel(srcData.data(), srcData.size()); + int errCode = SerializeDatas(kvEntries, parcel, compressInfo.targetVersion); + if (errCode != E_OK) { + return errCode; + } + + // Compress data. + auto inst = DataCompression::GetInstance(compressInfo.compressAlgo); + if (inst == nullptr) { + return -E_INVALID_COMPRESS_ALGO; + } + return inst->Compress(srcData, destData); +} + +int GenericSingleVerKvEntry::Uncompress(const std::vector &srcData, std::vector &kvEntries, + uint32_t destLen, CompressAlgorithm algo) +{ + // Uncompress data. + std::vector destData(destLen, 0); + auto inst = DataCompression::GetInstance(algo); + if (inst == nullptr) { + return -E_INVALID_COMPRESS_ALGO; + } + int errCode = inst->Uncompress(srcData, destData, destLen); + if (errCode != E_OK) { + return errCode; + } + + // Deserialize data. + Parcel parcel(destData.data(), destData.size()); + if (DeSerializeDatas(kvEntries, parcel) == 0) { + return -E_PARSE_FAIL; + } + return E_OK; +} + +int GenericSingleVerKvEntry::SerializeCompressedDatas(const std::vector &kvEntries, + const std::vector &compressedEntries, Parcel &parcel, uint32_t targetVersion, CompressAlgorithm algo) +{ + uint32_t srcLen = CalculateLens(kvEntries, targetVersion); + (void)parcel.WriteUInt32(static_cast(algo)); + (void)parcel.WriteUInt32(srcLen); + (void)parcel.WriteVector(compressedEntries); + return parcel.IsError() ? -E_PARSE_FAIL : E_OK; +} + +int GenericSingleVerKvEntry::DeSerializeCompressedDatas(std::vector &kvEntries, Parcel &parcel) +{ + // Get compression algo type. + uint32_t algoType = 0; + (void)parcel.ReadUInt32(algoType); + CompressAlgorithm compressAlgo = CompressAlgorithm::NONE; + int errCode = DataCompression::TransferCompressionAlgo(algoType, compressAlgo); + if (errCode != E_OK) { + return errCode; + } + + // Get buffer length. + uint32_t destLen = 0; + (void)parcel.ReadUInt32(destLen); + + // Get compressed data. + std::vector srcData; + (void)parcel.ReadVector(srcData); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + + // Uncompress data. + return GenericSingleVerKvEntry::Uncompress(srcData, kvEntries, destLen, compressAlgo); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/generic_single_ver_kv_entry.h b/mock/distributeddb/storage/src/generic_single_ver_kv_entry.h new file mode 100644 index 0000000000000000000000000000000000000000..8fa82b0061a4b3282e3cac020951c3228d1af588 --- /dev/null +++ b/mock/distributeddb/storage/src/generic_single_ver_kv_entry.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GENERIC_SINGLE_VER_KV_ENTRY_H +#define GENERIC_SINGLE_VER_KV_ENTRY_H + +#include +#include +#include + +#include "db_types.h" +#include "single_ver_kv_entry.h" + +namespace DistributedDB { +struct CompressInfo { + CompressAlgorithm compressAlgo; + uint32_t targetVersion; +}; + +class GenericSingleVerKvEntry : public SingleVerKvEntry { +public: + GenericSingleVerKvEntry(); + ~GenericSingleVerKvEntry() override; + DISABLE_COPY_ASSIGN_MOVE(GenericSingleVerKvEntry); + + std::string GetOrigDevice() const override; + + void SetOrigDevice(const std::string &dev) override; + + Timestamp GetTimestamp() const override; + + void SetTimestamp(Timestamp time) override; + + Timestamp GetWriteTimestamp() const override; + + void SetWriteTimestamp(Timestamp time) override; + + void GetKey(Key &key) const; + const Key &GetKey() const override; + + void GetHashKey(Key &key) const; + + void GetValue(Value &value) const; + const Value &GetValue() const override; + + uint64_t GetFlag() const override; + + void SetEntryData(DataItem &&dataItem); + + int SerializeData(Parcel &parcel, uint32_t targetVersion) override; + + int DeSerializeData(Parcel &parcel) override; + + uint32_t CalculateLen(uint32_t targetVersion) override; + + static int SerializeDatas(const std::vector &kvEntries, Parcel &parcel, uint32_t targetVersion); + + static int DeSerializeDatas(std::vector &kvEntries, Parcel &parcel); + + void SetKey(const Key &key) override; + void SetValue(const Value &value) override; + void SetHashKey(const Key &hashKey) override; + + static uint32_t CalculateLens(const std::vector &kvEntries, uint32_t targetVersion); + static uint32_t CalculateCompressedLens(const std::vector &compressedData); + static int Compress(const std::vector &kvEntries, std::vector &destData, + const CompressInfo &compressInfo); + static int Uncompress(const std::vector &srcData, std::vector &kvEntries, + uint32_t destLen, CompressAlgorithm algo); + static int SerializeCompressedDatas(const std::vector &kvEntries, + const std::vector &compressedEntries, Parcel &parcel, uint32_t targetVersion, CompressAlgorithm algo); + static int DeSerializeCompressedDatas(std::vector &kvEntries, Parcel &parcel); + +private: + enum class OperType { + SERIALIZE, + DESERIALIZE, + CAL_LEN, + }; + int AdaptToVersion(OperType operType, uint32_t targetVersion, Parcel &parcel, uint64_t &datalen); + int AdaptToVersion(OperType operType, uint32_t targetVersion, uint64_t &datalen); + + int SerializeDataByVersion(uint32_t targetVersion, Parcel &parcel) const; + int SerializeDataByFirstVersion(Parcel &parcel) const; + int SerializeDataByLaterVersion(Parcel &parcel, uint32_t targetVersion) const; + + int CalLenByVersion(uint32_t targetVersion, uint64_t &len) const; + void CalLenByFirstVersion(uint64_t &len) const; + void CalLenByLaterVersion(uint64_t &len, uint32_t targetVersion) const; + + int DeSerializeByVersion(uint32_t targetVersion, Parcel &parcel, uint64_t &len); + void DeSerializeByFirstVersion(uint64_t &len, Parcel &parcel); + void DeSerializeByLaterVersion(uint64_t &len, Parcel &parcel, uint32_t targetVersion); + + DataItem dataItem_; +}; +} // namespace DistributedDB + +#endif // GENERIC_SINGLE_VER_KV_ENTRY_H diff --git a/mock/distributeddb/storage/src/iconnection.cpp b/mock/distributeddb/storage/src/iconnection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..620a9a31a62f05bc6d6defb702633309f75fd410 --- /dev/null +++ b/mock/distributeddb/storage/src/iconnection.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "iconnection.h" + +#include "runtime_context.h" + +namespace DistributedDB { +IConnection::IConnection() + : connectionId_(0) +{ +} + +uint64_t IConnection::GetConnectionId() +{ + if (connectionId_ != 0) { + return connectionId_; + } + std::lock_guard autoLock(connectionIdLock_); + // check again here, may be generated after get lock + if (connectionId_ == 0) { + connectionId_ = static_cast(RuntimeContext::GetInstance()->GenerateSessionId()); + } + return connectionId_; +} +} \ No newline at end of file diff --git a/mock/distributeddb/storage/src/ikvdb_commit.h b/mock/distributeddb/storage/src/ikvdb_commit.h new file mode 100644 index 0000000000000000000000000000000000000000..75dfe2a46b75922def9c7b210c4f4a53ef52a507 --- /dev/null +++ b/mock/distributeddb/storage/src/ikvdb_commit.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IKVDB_COMMIT_H +#define IKVDB_COMMIT_H + +#include "db_types.h" +#include "multi_ver_def.h" + +namespace DistributedDB { +class IKvDBCommit { +public: + virtual ~IKvDBCommit() {}; + virtual Version GetCommitVersion() const = 0; + virtual void SetCommitVersion(const Version &versionInfo) = 0; + virtual CommitID GetCommitId() const = 0; + virtual void SetCommitId(const CommitID &id) = 0; + virtual CommitID GetLeftParentId() const = 0; + virtual void SetLeftParentId(const CommitID &id) = 0; + virtual CommitID GetRightParentId() const = 0; + virtual void SetRightParentId(const CommitID &id) = 0; + virtual Timestamp GetTimestamp() const = 0; + virtual void SetTimestamp(Timestamp timestamp) = 0; + virtual bool GetLocalFlag() const = 0; + virtual void SetLocalFlag(bool localFlag) = 0; + virtual DeviceID GetDeviceInfo() const = 0; + virtual void SetDeviceInfo(const DeviceID &deviceInfo) = 0; +}; +} + +#endif diff --git a/mock/distributeddb/storage/src/ikvdb_commit_storage.h b/mock/distributeddb/storage/src/ikvdb_commit_storage.h new file mode 100644 index 0000000000000000000000000000000000000000..c6e7aeef883ae0370af0048d564f580388c6f29c --- /dev/null +++ b/mock/distributeddb/storage/src/ikvdb_commit_storage.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KV_DB_COMMIT_STORAGE_H +#define I_KV_DB_COMMIT_STORAGE_H + +#include +#include +#include +#include + +#include "db_types.h" +#include "ikvdb_commit.h" +#include "multi_ver_def.h" + +namespace DistributedDB { +class IKvDBCommitStorage { +public: + struct Property final { + std::string path; + std::string identifierName; + bool isNeedCreate = true; + CipherType cipherType = CipherType::AES_256_GCM; + CipherPassword passwd; + }; + + virtual ~IKvDBCommitStorage() {}; + virtual int CheckVersion(const Property &property, bool &isDbExist) const = 0; + virtual int Open(const Property &property) = 0; + virtual void Close() = 0; + virtual int Remove(const Property &property) = 0; + virtual IKvDBCommit *AllocCommit(int &errCode) const = 0; + virtual IKvDBCommit *GetCommit(const CommitID &commitId, int &errCode) const = 0; + virtual int AddCommit(const IKvDBCommit &commitEntry, bool isHeader) = 0; + virtual int RemoveCommit(const CommitID &commitId) = 0; + virtual void ReleaseCommit(const IKvDBCommit *commit) const = 0; + virtual int SetHeader(const CommitID &commitId) = 0; + virtual CommitID GetHeader(int &errCode) const = 0; + virtual bool CommitExist(const CommitID &commitId, int &errCode) const = 0; + virtual int GetLatestCommits(std::map &latestCommits) const = 0; + virtual int GetCommitTree(const std::map &latestCommits, + std::list &commits) const = 0; + virtual Version GetMaxCommitVersion(int &errCode) const = 0; + virtual int BackupCurrentDatabase(const Property &property, const std::string &dir) = 0; + virtual int ImportDatabase(const Property &property, const std::string &dir, const CipherPassword &passwd) = 0; + virtual int StartVacuum() = 0; + virtual int CancelVacuum() = 0; + virtual int FinishlVacuum() = 0; + virtual int GetAllCommitsInTree(std::list &commits) const = 0; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_COMMIT_STORAGE_H diff --git a/mock/distributeddb/storage/src/ikvdb_factory.cpp b/mock/distributeddb/storage/src/ikvdb_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cfa42739d8c15f8e19fba9eb941eb4676e110872 --- /dev/null +++ b/mock/distributeddb/storage/src/ikvdb_factory.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ikvdb_factory.h" + +namespace DistributedDB { +IKvDBFactory *IKvDBFactory::factory_ = nullptr; +std::mutex IKvDBFactory::instanceLock_; + +// Get current factory object. +IKvDBFactory *IKvDBFactory::GetCurrent() +{ + std::lock_guard lockGuard(IKvDBFactory::instanceLock_); + return IKvDBFactory::factory_; +} + +// Set the factory object to 'current' +void IKvDBFactory::Register(IKvDBFactory *factory) +{ + std::lock_guard lockGuard(IKvDBFactory::instanceLock_); + IKvDBFactory::factory_ = factory; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/storage/src/ikvdb_raw_cursor.h b/mock/distributeddb/storage/src/ikvdb_raw_cursor.h new file mode 100644 index 0000000000000000000000000000000000000000..b306bf3e0a18954d7c8aa26d0d9371d1e14ad883 --- /dev/null +++ b/mock/distributeddb/storage/src/ikvdb_raw_cursor.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KV_DB_RAW_CURSOR_H +#define I_KV_DB_RAW_CURSOR_H + +#include "macro_utils.h" +#include "db_types.h" + +namespace DistributedDB { +class IKvDBRawCursor { +public: + IKvDBRawCursor() = default; + virtual ~IKvDBRawCursor() {} + + DISABLE_COPY_ASSIGN_MOVE(IKvDBRawCursor); + + // Open the raw cursor. + virtual int Open() = 0; + + // Close the raw cursor before invoking operator delete. + virtual void Close() = 0; + + // Reload all data. + virtual int Reload() = 0; + + // Get total entries count. + virtual int GetCount() const = 0; + + // Get next entry, return errCode if it fails. + virtual int GetNext(Entry &entry, bool isCopy) const = 0; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_RAW_CURSOR_H diff --git a/mock/distributeddb/storage/src/irelational_store.h b/mock/distributeddb/storage/src/irelational_store.h new file mode 100644 index 0000000000000000000000000000000000000000..5e3c1e6ad70937db48e67ed26ec217575d6ce9ea --- /dev/null +++ b/mock/distributeddb/storage/src/irelational_store.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef I_RELATIONAL_STORE_H +#define I_RELATIONAL_STORE_H +#ifdef RELATIONAL_STORE + +#include +#include + +#include "ref_object.h" +#include "relationaldb_properties.h" +#include "relational_store_connection.h" + +namespace DistributedDB { +class IRelationalStore : public virtual RefObject { +public: + IRelationalStore() = default; + ~IRelationalStore() override = default; + DISABLE_COPY_ASSIGN_MOVE(IRelationalStore); + + // Open the database. + virtual int Open(const RelationalDBProperties &properties) = 0; + + virtual void WakeUpSyncer() = 0; + + // Create a db connection. + virtual RelationalStoreConnection *GetDBConnection(int &errCode) = 0; + + virtual std::string GetStorePath() const = 0; + + virtual RelationalDBProperties GetProperties() const = 0; + + virtual void Dump(int fd) = 0; +}; +} // namespace DistributedDB + +#endif +#endif // I_RELATIONAL_STORE_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/kvdb_commit_notify_filterable_data.cpp b/mock/distributeddb/storage/src/kvdb_commit_notify_filterable_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3007d8566a99f88e5ad0c53b3429cb5c357631da --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_commit_notify_filterable_data.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kvdb_commit_notify_filterable_data.h" +#include "db_errno.h" + +namespace DistributedDB { +KvDBCommitNotifyFilterAbleData::KvDBCommitNotifyFilterAbleData() + : genericKvDB_(nullptr), + notifyID_(0) +{} + +KvDBCommitNotifyFilterAbleData::~KvDBCommitNotifyFilterAbleData() +{ + if (genericKvDB_ != nullptr) { + genericKvDB_->DecObjRef(genericKvDB_); + genericKvDB_ = nullptr; + } +} + +const std::list KvDBCommitNotifyFilterAbleData::GetInsertedEntries(int &errCode) const +{ + std::list entries; + errCode = E_OK; + return entries; +} + +const std::list KvDBCommitNotifyFilterAbleData::GetUpdatedEntries(int &errCode) const +{ + std::list entries; + errCode = E_OK; + return entries; +} + +const std::list KvDBCommitNotifyFilterAbleData::GetDeletedEntries(int &errCode) const +{ + std::list entries; + errCode = E_OK; + return entries; +} + +const std::list KvDBCommitNotifyFilterAbleData::GetCommitConflicts(int &errCode) const +{ + std::list entries; + errCode = E_OK; + return entries; +} + +bool KvDBCommitNotifyFilterAbleData::IsCleared() const +{ + return false; +} + +bool KvDBCommitNotifyFilterAbleData::IsChangedDataEmpty() const +{ + return true; +} + +bool KvDBCommitNotifyFilterAbleData::IsConflictedDataEmpty() const +{ + return true; +} + +void KvDBCommitNotifyFilterAbleData::SetFilterKey(const Key &key) +{ + (void)key; + return; +} + +void KvDBCommitNotifyFilterAbleData::SetMyDb(GenericKvDB *db, uint64_t notifyID) +{ + if (genericKvDB_ == db) { + notifyID_ = notifyID; + return; + } + if (genericKvDB_ != nullptr) { + genericKvDB_->DecObjRef(genericKvDB_); + } + genericKvDB_ = db; + if (genericKvDB_ != nullptr) { + genericKvDB_->IncObjRef(genericKvDB_); + } + notifyID_ = notifyID; +} + +uint64_t KvDBCommitNotifyFilterAbleData::GetNotifyID() const +{ + return notifyID_; +} + +DEFINE_OBJECT_TAG_FACILITIES(KvDBCommitNotifyFilterAbleData) +} diff --git a/mock/distributeddb/storage/src/kvdb_commit_notify_filterable_data.h b/mock/distributeddb/storage/src/kvdb_commit_notify_filterable_data.h new file mode 100644 index 0000000000000000000000000000000000000000..4db1c8a8d47953ec1c8fa13f6c943aa5f8585b6f --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_commit_notify_filterable_data.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KVDB_COMMIT_NOTIFY_FILTERABLE_DATA_H +#define KVDB_COMMIT_NOTIFY_FILTERABLE_DATA_H + +#include "generic_kvdb.h" +#include "kvdb_conflict_entry.h" +#include "kvdb_commit_notify_data.h" + +namespace DistributedDB { +class KvDBCommitNotifyFilterAbleData : public KvDBCommitNotifyData { +public: + KvDBCommitNotifyFilterAbleData(); + ~KvDBCommitNotifyFilterAbleData() override; + DISABLE_COPY_ASSIGN_MOVE(KvDBCommitNotifyFilterAbleData); + + // get the new inserted entries. + const std::list GetInsertedEntries(int &errCode) const override; + + // get the new updated entries. + const std::list GetUpdatedEntries(int &errCode) const override; + + // get the new deleted entries. + const std::list GetDeletedEntries(int &errCode) const override; + + // get all conflict entries when commit. + const std::list GetCommitConflicts(int &errCode) const override; + + // database is cleared by user in the commit. + bool IsCleared() const override; + + // test if the data is empty or not after filtered. + bool IsChangedDataEmpty() const override; + + // test if the conflict data is empty or not. + bool IsConflictedDataEmpty() const override; + + // set the filter key. + virtual void SetFilterKey(const Key &key); + + // set and ref the db that we belong to. + void SetMyDb(GenericKvDB *db, uint64_t notifyID); + + // get ID of this notify. + uint64_t GetNotifyID() const; + +private: + DECLARE_OBJECT_TAG(KvDBCommitNotifyFilterAbleData); + + GenericKvDB *genericKvDB_; + uint64_t notifyID_; +}; +} // namespace DistributedDB + +#endif // KVDB_COMMIT_NOTIFY_FILTERABLE_DATA_H diff --git a/mock/distributeddb/storage/src/kvdb_manager.cpp b/mock/distributeddb/storage/src/kvdb_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..761f56ffee15a41fb2a4be1f19c5fca6d79bc7a4 --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_manager.cpp @@ -0,0 +1,972 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kvdb_manager.h" +#include "log_print.h" +#include "db_common.h" +#include "runtime_context.h" +#include "schema_object.h" +#include "default_factory.h" +#include "generic_kvdb.h" +#include "db_constant.h" +#include "res_finalizer.h" + +namespace DistributedDB { +const std::string KvDBManager::PROCESS_LABEL_CONNECTOR = "-"; +std::atomic KvDBManager::instance_{nullptr}; +std::mutex KvDBManager::kvDBLock_; +std::mutex KvDBManager::instanceLock_; +std::map KvDBManager::locks_; + +namespace { + DefaultFactory g_defaultFactory; + + int CreateDataBaseInstance(const KvDBProperties &property, IKvDB *&kvDB) + { + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + if (factory == nullptr) { + return -E_OUT_OF_MEMORY; + } + int errCode = E_OK; + int databaseType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + if (databaseType == KvDBProperties::LOCAL_TYPE) { + kvDB = factory->CreateKvDb(LOCAL_KVDB, errCode); + if (kvDB != nullptr) { + kvDB->EnableAutonomicUpgrade(); + } + } else if (databaseType == KvDBProperties::SINGLE_VER_TYPE) { + kvDB = factory->CreateKvDb(SINGER_VER_KVDB, errCode); + } else { + kvDB = factory->CreateKvDb(MULTI_VER_KVDB, errCode); + } + return errCode; + } + + int CreateRemoveStateFlagFile(const KvDBProperties &properties) + { + std::string dataDir = properties.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + std::string identifierName = DBCommon::TransferStringToHex(identifier); + std::string storeDir = dataDir + "/" + identifierName + DBConstant::DELETE_KVSTORE_REMOVING; + if (OS::CheckPathExistence(storeDir)) { + return E_OK; + } + // create the pre flag file. + int errCode = OS::CreateFileByFileName(storeDir); + if (errCode != E_OK) { + LOGE("Create remove state flag file failed:%d.", errCode); + } + return errCode; + } +} + +int KvDBManager::CheckRemoveStateAndRetry(const KvDBProperties &property) +{ + std::string dataDir = property.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifier = property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + std::string identifierName = DBCommon::TransferStringToHex(identifier); + std::string storeDir = dataDir + "/" + identifierName + DBConstant::DELETE_KVSTORE_REMOVING; + + if (OS::CheckPathExistence(storeDir)) { + KvDBManager::ExecuteRemoveDatabase(property); + } + // Re-detection deleted had been finish + if (OS::CheckPathExistence(storeDir)) { + LOGD("Deletekvstore unfinished, can not create new same identifier kvstore!"); + return -E_REMOVE_FILE; + } + return E_OK; +} + +int KvDBManager::ExecuteRemoveDatabase(const KvDBProperties &properties) +{ + int errCode = CheckDatabaseFileStatus(properties); + if (errCode != E_OK) { + return errCode; + } + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + if (factory == nullptr) { + return -E_INVALID_DB; + } + + errCode = CreateRemoveStateFlagFile(properties); + if (errCode != E_OK) { + LOGE("create ctrl file failed:%d.", errCode); + return errCode; + } + + errCode = -E_NOT_FOUND; + for (KvDBType kvDbType = LOCAL_KVDB; kvDbType < UNSUPPORT_KVDB_TYPE; kvDbType = (KvDBType)(kvDbType + 1)) { + int innerErrCode = E_OK; + IKvDB *kvdb = factory->CreateKvDb(kvDbType, innerErrCode); + if (innerErrCode != E_OK) { + return innerErrCode; + } + innerErrCode = kvdb->RemoveKvDB(properties); + RefObject::DecObjRef(kvdb); + if (innerErrCode != -E_NOT_FOUND) { + if (innerErrCode != E_OK) { + return innerErrCode; + } + errCode = E_OK; + } + } + + if (errCode == -E_NOT_FOUND) { + LOGE("DataBase file Not exist! return NOT_FOUND."); + } + + RemoveDBDirectory(properties); + return errCode; +} + +void KvDBManager::RemoveDBDirectory(const KvDBProperties &properties) +{ + std::string dataDir = properties.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + std::string identifierName = DBCommon::TransferStringToHex(identifier); + std::string storeDir = dataDir + "/" + identifierName; + std::string removingFlag = dataDir + "/" + identifierName + DBConstant::DELETE_KVSTORE_REMOVING; + (void)OS::RemoveDBDirectory(storeDir); + + std::string storeId = properties.GetStringProp(KvDBProperties::STORE_ID, ""); + identifier = DBCommon::TransferHashString(storeId); + identifierName = DBCommon::TransferStringToHex(identifier); + storeDir = dataDir + "/" + identifierName; + (void)OS::RemoveDBDirectory(storeDir); + + (void)OS::RemoveFile(removingFlag); +} + +// Used to open a kvdb with the given property +IKvDB *KvDBManager::OpenDatabase(const KvDBProperties &property, int &errCode) +{ + KvDBManager *manager = GetInstance(); + if (manager == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + return manager->GetDataBase(property, errCode, true); +} + +void KvDBManager::EnterDBOpenCloseProcess(const std::string &identifier) +{ + std::unique_lock lock(kvDBOpenMutex_); + kvDBOpenCondition_.wait(lock, [this, &identifier]() { + return this->kvDBOpenSet_.count(identifier) == 0; + }); + (void)kvDBOpenSet_.insert(identifier); +} + +void KvDBManager::ExitDBOpenCloseProcess(const std::string &identifier) +{ + std::unique_lock lock(kvDBOpenMutex_); + (void)kvDBOpenSet_.erase(identifier); + kvDBOpenCondition_.notify_all(); +} + +// one time 100ms +// In order to prevent long-term blocking of the process, a retry method is used +// The dimensions of the lock by appid-userid-storeid +int KvDBManager::TryLockDB(const KvDBProperties &kvDBProp, int retryTimes) +{ + std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, ""); + bool isMemoryDb = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + std::string id = KvDBManager::GenerateKvDBIdentifier(kvDBProp); + if (dataDir.back() != '/') { + dataDir += "/"; + } + + if (isMemoryDb) { + LOGI("MemoryDb not need lock!"); + return E_OK; + } + + if (locks_.count(id) != 0) { + LOGI("db has been locked!"); + return E_OK; + } + + std::string hexHashId = DBCommon::TransferStringToHex((id)); + OS::FileHandle handle; + int errCode = OS::OpenFile(dataDir + hexHashId + DBConstant::DB_LOCK_POSTFIX, handle); + if (errCode != E_OK) { + LOGE("Open lock file fail errCode = [%d], errno:%d", errCode, errno); + return errCode; + } + + while (retryTimes-- > 0) { + errCode = OS::FileLock(handle, false); // not block process + if (errCode == E_OK) { + LOGI("[%s]locked!", STR_MASK(DBCommon::TransferStringToHex(KvDBManager::GenerateKvDBIdentifier(kvDBProp)))); + locks_[id] = handle; + return errCode; + } else if (errCode == -E_BUSY) { + LOGD("DB already held by process lock!"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait for 100ms + continue; + } else { + LOGE("Try lock db failed, errCode = [%d] errno:%d", errCode, errno); + OS::CloseFile(handle); + return errCode; + } + } + OS::CloseFile(handle); + return -E_BUSY; +} + +int KvDBManager::UnlockDB(const KvDBProperties &kvDBProp) +{ + bool isMemoryDb = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + if (isMemoryDb) { + return E_OK; + } + std::string identifierDir = KvDBManager::GenerateKvDBIdentifier(kvDBProp); + if (locks_.count(identifierDir) == 0) { + return E_OK; + } + int errCode = OS::FileUnlock(locks_[identifierDir]); + LOGI("DB unlocked! errCode = [%d]", errCode); + if (errCode != E_OK) { + return errCode; + } + locks_.erase(identifierDir); + return E_OK; +} + +// Used to open a kvdb with the given property +IKvDBConnection *KvDBManager::GetDatabaseConnection(const KvDBProperties &properties, int &errCode, + bool isNeedIfOpened) +{ + auto manager = GetInstance(); + if (manager == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + IKvDBConnection *connection = nullptr; + std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + LOGD("Begin to get [%s] database connection.", STR_MASK(DBCommon::TransferStringToHex(identifier))); + manager->EnterDBOpenCloseProcess(identifier); + + IKvDB *kvDB = manager->GetDataBase(properties, errCode, isNeedIfOpened); + if (kvDB == nullptr) { + if (isNeedIfOpened) { + LOGE("Failed to open the db:%d", errCode); + } + } else { + bool isMemoryDb = properties.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + std::string canonicalDir = properties.GetStringProp(KvDBProperties::DATA_DIR, ""); + if (!isMemoryDb && (canonicalDir.empty() || canonicalDir != kvDB->GetStorePath())) { + LOGE("Failed to check store path, the input path does not match with cached store."); + errCode = -E_INVALID_ARGS; + } else { + connection = kvDB->GetDBConnection(errCode); + if (connection == nullptr) { // not kill kvdb, Other operations like import may be used concurrently + LOGE("Failed to get the db connect for delegate:%d", errCode); + } + } + RefObject::DecObjRef(kvDB); // restore the reference increased by the cache. + kvDB = nullptr; + } + + manager->ExitDBOpenCloseProcess(identifier); + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + std::string appId = properties.GetStringProp(KvDBProperties::APP_ID, ""); + std::string userId = properties.GetStringProp(KvDBProperties::USER_ID, ""); + std::string storeId = properties.GetStringProp(KvDBProperties::STORE_ID, ""); + manager->DataBaseCorruptNotify(appId, userId, storeId); + LOGE("Database [%s] is corrupted:%d", STR_MASK(DBCommon::TransferStringToHex(identifier)), errCode); + } + + return connection; +} + +int KvDBManager::ReleaseDatabaseConnection(IKvDBConnection *connection) +{ + if (connection == nullptr) { + return -E_INVALID_DB; + } + + std::string identifier = connection->GetIdentifier(); + auto manager = GetInstance(); + if (manager == nullptr) { + return -E_OUT_OF_MEMORY; + } + manager->EnterDBOpenCloseProcess(identifier); + int errCode = connection->Close(); + manager->ExitDBOpenCloseProcess(identifier); + + if (errCode != E_OK) { + LOGE("[KvDBManager] Release db connection:%d", errCode); + } + LOGI("[Connection] db[%s] conn Close", STR_MASK(DBCommon::TransferStringToHex(identifier))); + return errCode; +} + +IKvDB *KvDBManager::CreateDataBase(const KvDBProperties &property, int &errCode) +{ + IKvDB *kvDB = OpenNewDatabase(property, errCode); + if (kvDB == nullptr) { + LOGE("Failed to open the new database."); + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB && + property.GetBoolProp(KvDBProperties::RM_CORRUPTED_DB, false)) { + LOGI("Remove the corrupted database while open"); + ExecuteRemoveDatabase(property); + kvDB = OpenNewDatabase(property, errCode); + } + return kvDB; + } + + if (property.GetBoolProp(KvDBProperties::CHECK_INTEGRITY, false)) { + int integrityStatus = kvDB->CheckIntegrity(); + if (integrityStatus == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + RemoveKvDBFromCache(kvDB); + RefObject::KillAndDecObjRef(kvDB); + kvDB = nullptr; + errCode = -E_INVALID_PASSWD_OR_CORRUPTED_DB; + if (property.GetBoolProp(KvDBProperties::RM_CORRUPTED_DB, false)) { + LOGI("Remove the corrupted database for the integrity check"); + ExecuteRemoveDatabase(property); + kvDB = OpenNewDatabase(property, errCode); + } + } + } + return kvDB; +} + +IKvDB *KvDBManager::GetDataBase(const KvDBProperties &property, int &errCode, bool isNeedIfOpened) +{ + bool isMemoryDb = property.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + bool isCreateNecessary = property.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + IKvDB *kvDB = FindAndGetKvDBFromCache(property, errCode); + if (kvDB != nullptr) { + if (!isNeedIfOpened) { + LOGI("[KvDBManager] Database has already been opened."); + RefObject::DecObjRef(kvDB); + errCode = -E_ALREADY_OPENED; + kvDB = nullptr; + } + return kvDB; + } + if (isMemoryDb && !isCreateNecessary) { + LOGI("IsCreateNecessary is false, Not need create database"); + return nullptr; + } + if (errCode != -E_NOT_FOUND) { + return nullptr; + } + + // Taking into account the compatibility of version delivery, + // temporarily use isNeedIntegrityCheck this field to avoid multi-process concurrency + bool isNeedIntegrityCheck = property.GetBoolProp(KvDBProperties::CHECK_INTEGRITY, false); + if (isNeedIntegrityCheck) { + LOGI("db need lock, need check integrity is [%d]", isNeedIntegrityCheck); + errCode = KvDBManager::TryLockDB(property, 10); // default 10 times retry + if (errCode != E_OK) { + return nullptr; + } + } + + ResFinalizer unlock([&errCode, &property, &kvDB]() { + int err = KvDBManager::UnlockDB(property); + if (err != E_OK) { + LOGE("GetDataBase unlock failed! err [%d] errCode [%d]", err, errCode); + errCode = err; + RefObject::KillAndDecObjRef(kvDB); + kvDB = nullptr; + } + }); + + kvDB = CreateDataBase(property, errCode); + if (errCode != E_OK) { + LOGE("Create data base failed, errCode = [%d]", errCode); + } + return kvDB; +} + +bool KvDBManager::IsOpenMemoryDb(const KvDBProperties &properties, const std::map &cache) const +{ + std::string identifier = GenerateKvDBIdentifier(properties); + auto iter = cache.find(identifier); + if (iter != cache.end()) { + IKvDB *kvDB = iter->second; + if (kvDB != nullptr && kvDB->GetMyProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false)) { + return true; + } + } + return false; +} + +// used to get kvdb size with the given property. +int KvDBManager::CalculateKvStoreSize(const KvDBProperties &properties, uint64_t &size) +{ + KvDBManager *manager = GetInstance(); + if (manager == nullptr) { + LOGE("Failed to get KvDBManager instance!"); + return -E_OUT_OF_MEMORY; + } + + std::lock_guard lockGuard(kvDBLock_); + if (manager->IsOpenMemoryDb(properties, manager->singleVerNaturalStores_)) { + size = 0; + return E_OK; + } + + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + if (factory == nullptr) { + return -E_INVALID_DB; + } + + uint64_t totalSize = 0; + for (KvDBType kvDbType = LOCAL_KVDB; kvDbType < UNSUPPORT_KVDB_TYPE; kvDbType = (KvDBType)(kvDbType + 1)) { + int innerErrCode = E_OK; + IKvDB *kvDB = factory->CreateKvDb(kvDbType, innerErrCode); + if (innerErrCode != E_OK) { + return innerErrCode; + } + uint64_t dbSize = 0; + innerErrCode = kvDB->GetKvDBSize(properties, dbSize); + RefObject::DecObjRef(kvDB); + if (innerErrCode != E_OK && innerErrCode != -E_NOT_FOUND) { + return innerErrCode; + } + LOGD("DB type [%u], size[%" PRIu64 "]", static_cast(kvDbType), dbSize); + totalSize = totalSize + dbSize; + } + // This represent Db file size(Unit is byte), It is small than max size(max uint64_t represent 2^64B) + if (totalSize != 0ull) { + size = totalSize; + return E_OK; + } + return -E_NOT_FOUND; +} + +IKvDB *KvDBManager::GetKvDBFromCacheByIdentify(const std::string &identifier, + const std::map &cache) const +{ + auto iter = cache.find(identifier); + if (iter != cache.end()) { + IKvDB *kvDB = iter->second; + if (kvDB == nullptr) { + LOGE("Kvstore cache is nullptr, there may be a logic error"); + return nullptr; + } + return kvDB; + } + return nullptr; +} + +int KvDBManager::CheckDatabaseFileStatus(const KvDBProperties &properties) +{ + KvDBManager *manager = GetInstance(); + if (manager == nullptr) { + LOGE("Failed to get KvDBManager instance!"); + return -E_OUT_OF_MEMORY; + } + + std::string identifier = GenerateKvDBIdentifier(properties); + std::lock_guard lockGuard(kvDBLock_); + IKvDB *kvDB = manager->GetKvDBFromCacheByIdentify(identifier, manager->localKvDBs_); + if (kvDB != nullptr) { + LOGE("The local KvDB is busy!"); + return -E_BUSY; + } + + kvDB = manager->GetKvDBFromCacheByIdentify(identifier, manager->multiVerNaturalStores_); + if (kvDB != nullptr) { + LOGE("The multi ver natural store is busy!"); + return -E_BUSY; + } + + kvDB = manager->GetKvDBFromCacheByIdentify(identifier, manager->singleVerNaturalStores_); + if (kvDB != nullptr) { + LOGE("The single version natural store is busy!"); + return -E_BUSY; + } + return E_OK; +} + +IKvDB *KvDBManager::OpenNewDatabase(const KvDBProperties &property, int &errCode) +{ + errCode = CheckRemoveStateAndRetry(property); + if (errCode != E_OK) { + LOGE("Failed to open IKvDB! Because delete kvstore unfinished err:%d", errCode); + return nullptr; + } + + IKvDB *kvDB = nullptr; + errCode = CreateDataBaseInstance(property, kvDB); + if (errCode != E_OK) { + LOGE("Failed to get IKvDB! err:%d", errCode); + return nullptr; + } + + errCode = kvDB->Open(property); + if (errCode != E_OK) { + LOGE("Failed to open IKvDB! err:%d", errCode); + RefObject::KillAndDecObjRef(kvDB); + kvDB = nullptr; + return nullptr; + } + auto identifier = DBCommon::TransferStringToHex(property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "")); + auto dbDir = property.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + LOGI("Database identifier:%.6s, dir:%.6s", identifier.c_str(), dbDir.c_str()); + // Register the callback function when the database is closed, triggered when kvdb free + kvDB->OnClose([kvDB, this]() { + LOGI("Remove from the cache"); + this->RemoveKvDBFromCache(kvDB); + }); + + IKvDB *kvDBTmp = SaveKvDBToCache(kvDB); + if (kvDBTmp != kvDB) { + RefObject::KillAndDecObjRef(kvDB); + kvDB = nullptr; + return kvDBTmp; + } + return kvDB; +} + +// used to delete a kvdb with the given property. +// return BUSY if in use +int KvDBManager::RemoveDatabase(const KvDBProperties &properties) +{ + KvDBManager *manager = GetInstance(); + if (manager == nullptr) { + LOGE("Failed to get kvdb manager while removing the db!"); + return -E_OUT_OF_MEMORY; + } + std::string identifier = GenerateKvDBIdentifier(properties); + manager->EnterDBOpenCloseProcess(identifier); + + LOGI("KvDBManager::RemoveDatabase begin try lock the database!"); + std::string lockFile = properties.GetStringProp(KvDBProperties::DATA_DIR, "") + "/" + + DBCommon::TransferStringToHex(identifier) + DBConstant::DB_LOCK_POSTFIX; + int errCode = E_OK; + if (OS::CheckPathExistence(lockFile)) { + errCode = KvDBManager::TryLockDB(properties, 10); // default 10 times retry + if (errCode != E_OK) { + manager->ExitDBOpenCloseProcess(identifier); + return errCode; + } + } + + errCode = ExecuteRemoveDatabase(properties); + if (errCode != E_OK) { + LOGE("[KvDBManager] Remove database failed:%d", errCode); + } + int err = KvDBManager::UnlockDB(properties); // unlock and delete lock file before delete dir + if (err != E_OK) { + LOGE("[KvDBManager][RemoveDatabase] UnlockDB failed:%d, errno:%d", err, errno); + errCode = err; + } + + manager->ExitDBOpenCloseProcess(identifier); + return errCode; +} + +std::string KvDBManager::GenerateKvDBIdentifier(const KvDBProperties &property) +{ + return property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); +} + +KvDBManager *KvDBManager::GetInstance() +{ + // For Double-Checked Locking, we need check instance_ twice + if (instance_ == nullptr) { + std::lock_guard lockGuard(instanceLock_); + if (instance_ == nullptr) { + instance_ = new (std::nothrow) KvDBManager(); + if (instance_ == nullptr) { + LOGE("failed to new KvDBManager!"); + return nullptr; + } + } + } + if (IKvDBFactory::GetCurrent() == nullptr) { + IKvDBFactory::Register(&g_defaultFactory); + } + return instance_; +} + +// Save to IKvDB to the global map +IKvDB *KvDBManager::SaveKvDBToCache(IKvDB *kvDB) +{ + if (kvDB == nullptr) { + return nullptr; + } + + { + KvDBProperties properties = kvDB->GetMyProperties(); + std::string identifier = GenerateKvDBIdentifier(properties); + int databaseType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + std::lock_guard lockGuard(kvDBLock_); + int errCode = E_OK; + if (databaseType == KvDBProperties::LOCAL_TYPE) { + IKvDB *kvDBTmp = FindKvDBFromCache(properties, localKvDBs_, true, errCode); + if (kvDBTmp != nullptr) { + kvDBTmp->IncObjRef(kvDBTmp); + return kvDBTmp; + } + localKvDBs_.insert(std::pair(identifier, kvDB)); + } else if (databaseType == KvDBProperties::MULTI_VER_TYPE) { + IKvDB *kvDBTmp = FindKvDBFromCache(properties, multiVerNaturalStores_, true, errCode); + if (kvDBTmp != nullptr) { + kvDBTmp->IncObjRef(kvDBTmp); + return kvDBTmp; + } + kvDB->WakeUpSyncer(); + multiVerNaturalStores_.insert(std::pair(identifier, kvDB)); + } else { + IKvDB *kvDBTmp = FindKvDBFromCache(properties, singleVerNaturalStores_, true, errCode); + if (kvDBTmp != nullptr) { + kvDBTmp->IncObjRef(kvDBTmp); + return kvDBTmp; + } + kvDB->WakeUpSyncer(); + singleVerNaturalStores_.insert(std::pair(identifier, kvDB)); + } + } + kvDB->SetCorruptHandler([kvDB, this]() { + std::string appId = kvDB->GetMyProperties().GetStringProp(KvDBProperties::APP_ID, ""); + std::string userId = kvDB->GetMyProperties().GetStringProp(KvDBProperties::USER_ID, ""); + std::string storeId = kvDB->GetMyProperties().GetStringProp(KvDBProperties::STORE_ID, ""); + this->DataBaseCorruptNotifyAsync(appId, userId, storeId); + }); + return kvDB; +} + +// Save to IKvDB to the global map +void KvDBManager::RemoveKvDBFromCache(const IKvDB *kvDB) +{ + KvDBProperties properties = kvDB->GetMyProperties(); + std::string identifier = GenerateKvDBIdentifier(properties); + int databaseType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + std::lock_guard lockGuard(kvDBLock_); + if (databaseType == KvDBProperties::LOCAL_TYPE) { + localKvDBs_.erase(identifier); + } else if (databaseType == KvDBProperties::MULTI_VER_TYPE) { + multiVerNaturalStores_.erase(identifier); + } else { + singleVerNaturalStores_.erase(identifier); + } +} + +// Get IKvDB from the global map +IKvDB *KvDBManager::FindAndGetKvDBFromCache(const KvDBProperties &properties, int &errCode) const +{ + std::lock_guard lockGuard(kvDBLock_); + + IKvDB *kvDB = FindKvDBFromCache(properties, localKvDBs_, true, errCode); + if (kvDB != nullptr) { + kvDB->IncObjRef(kvDB); + return kvDB; + } + if (errCode != -E_NOT_FOUND) { + return nullptr; + } + + kvDB = FindKvDBFromCache(properties, multiVerNaturalStores_, true, errCode); + if (kvDB != nullptr) { + kvDB->IncObjRef(kvDB); + return kvDB; + } + if (errCode != -E_NOT_FOUND) { + return nullptr; + } + + kvDB = FindKvDBFromCache(properties, singleVerNaturalStores_, true, errCode); + if (kvDB != nullptr) { + kvDB->IncObjRef(kvDB); + return kvDB; + } + return nullptr; +} + +IKvDB *KvDBManager::FindKvDBFromCache(const KvDBProperties &properties, const std::map &cache, + bool isNeedCheckPasswd, int &errCode) const +{ + errCode = E_OK; + std::string identifier = GenerateKvDBIdentifier(properties); + auto iter = cache.find(identifier); + if (iter != cache.end()) { + IKvDB *kvDB = iter->second; + if (kvDB == nullptr) { + LOGE("KVSTORE cache is nullptr, there may be a logic error"); + errCode = -E_INTERNAL_ERROR; + return nullptr; + } + int newType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + int oldType = kvDB->GetMyProperties().GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + if (oldType == newType) { + errCode = CheckKvDBProperties(kvDB, properties, isNeedCheckPasswd); + if (errCode != E_OK) { + return nullptr; + } + return kvDB; + } else { + errCode = -E_INVALID_ARGS; + LOGE("Database [%s] type not matched, type [%d] vs [%d]", + STR_MASK(DBCommon::TransferStringToHex(identifier)), newType, oldType); + return nullptr; + } + } + + errCode = -E_NOT_FOUND; + return nullptr; +} + +int KvDBManager::SetProcessLabel(const std::string &appId, const std::string &userId) +{ + std::string label = appId + PROCESS_LABEL_CONNECTOR + userId; + RuntimeContext::GetInstance()->SetProcessLabel(label); + return E_OK; +} + +void KvDBManager::RestoreSyncableKvStore() +{ + KvDBManager *manager = GetInstance(); + if (manager == nullptr) { + return; + } + + manager->RestoreSyncerOfAllKvStore(); +} + +void KvDBManager::SetDatabaseCorruptionHandler(const KvStoreCorruptionHandler &handler) +{ + KvDBManager *manager = GetInstance(); + if (manager == nullptr) { + return; + } + + manager->SetAllDatabaseCorruptionHander(handler); +} + +void KvDBManager::SetAllDatabaseCorruptionHander(const KvStoreCorruptionHandler &handler) +{ + { + std::lock_guard lock(corruptMutex_); + corruptHandler_ = handler; + } + std::lock_guard lockGuard(kvDBLock_); + SetCorruptHandlerForDatabases(singleVerNaturalStores_); + SetCorruptHandlerForDatabases(localKvDBs_); + SetCorruptHandlerForDatabases(multiVerNaturalStores_); +} + +void KvDBManager::DataBaseCorruptNotify(const std::string &appId, const std::string &userId, const std::string &storeId) +{ + KvStoreCorruptionHandler corruptHandler = nullptr; + { + std::lock_guard lock(corruptMutex_); + corruptHandler = corruptHandler_; + } + + if (corruptHandler != nullptr) { + corruptHandler(appId, userId, storeId); + } +} + +void KvDBManager::DataBaseCorruptNotifyAsync(const std::string &appId, const std::string &userId, + const std::string &storeId) +{ + int errCode = RuntimeContext::GetInstance()->ScheduleTask( + std::bind(&KvDBManager::DataBaseCorruptNotify, this, appId, userId, storeId)); + if (errCode != E_OK) { + LOGE("[KvDBManager][CorruptNotify] ScheduleTask failed, errCode = %d.", errCode); + return; + } +} + +void KvDBManager::SetCorruptHandlerForDatabases(const std::map &dbMaps) +{ + for (const auto &item : dbMaps) { + if (item.second == nullptr) { + continue; + } + + item.second->SetCorruptHandler([item, this]() { + std::string appId = item.second->GetMyProperties().GetStringProp(KvDBProperties::APP_ID, ""); + std::string userId = item.second->GetMyProperties().GetStringProp(KvDBProperties::USER_ID, ""); + std::string storeId = item.second->GetMyProperties().GetStringProp(KvDBProperties::STORE_ID, ""); + this->DataBaseCorruptNotifyAsync(appId, userId, storeId); + }); + } +} + +void KvDBManager::RestoreSyncerOfAllKvStore() +{ + std::lock_guard lockGuard(kvDBLock_); + for (auto &item : singleVerNaturalStores_) { + if (item.second != nullptr) { + item.second->WakeUpSyncer(); + } + } + + for (auto &item : multiVerNaturalStores_) { + if (item.second != nullptr) { + item.second->WakeUpSyncer(); + } + } +} + +bool KvDBManager::CompareSchemaObject(const SchemaObject &newSchema, const SchemaObject &oldSchema) +{ + if (!newSchema.IsSchemaValid() && !oldSchema.IsSchemaValid()) { + return true; + } + if (!newSchema.IsSchemaValid() || !oldSchema.IsSchemaValid()) { + return false; + } + int errCode = oldSchema.CompareAgainstSchemaObject(newSchema); + if (errCode == -E_SCHEMA_EQUAL_EXACTLY) { + return true; + } + return false; +} + +int KvDBManager::CheckSchema(const IKvDB *kvDB, const KvDBProperties &properties) +{ + if (kvDB == nullptr) { + LOGE("input kvdb is nullptr"); + return -E_INVALID_ARGS; + } + SchemaObject inputSchema = properties.GetSchema(); + SchemaObject cacheSchema = kvDB->GetMyProperties().GetSchema(); + bool isFirstOpenReadOnly = + kvDB->GetMyProperties().GetBoolProp(KvDBProperties::FIRST_OPEN_IS_READ_ONLY, false); + if (isFirstOpenReadOnly) { + if (inputSchema.IsSchemaValid()) { + LOGE("schema not matched"); + return -E_SCHEMA_MISMATCH; + } else { + return E_OK; + } + } + if (!CompareSchemaObject(inputSchema, cacheSchema)) { + LOGE("schema not matched"); + return -E_SCHEMA_MISMATCH; + } + return E_OK; +} + +namespace { + bool IsSameCipher(CipherType srcType, CipherType inputType) + { + // At present, the default type is AES-256-GCM. + // So when src is default and input is AES-256-GCM, + // or when src is AES-256-GCM and input is default, + // we think they are the same type. + if (((srcType == CipherType::DEFAULT || srcType == CipherType::AES_256_GCM) && + (inputType == CipherType::DEFAULT || inputType == CipherType::AES_256_GCM)) || + srcType == inputType) { + return true; + } + return false; + } + + bool CheckSecOptions(const KvDBProperties &input, const KvDBProperties &existed) + { + // If any has NO_SET, skip the check and using the existed option. + if (input.GetIntProp(KvDBProperties::SECURITY_LABEL, 0) != 0 && + existed.GetIntProp(KvDBProperties::SECURITY_LABEL, 0) != 0) { + if (existed.GetIntProp(KvDBProperties::SECURITY_LABEL, 0) != + input.GetIntProp(KvDBProperties::SECURITY_LABEL, 0)) { + LOGE("Security label mismatch: existed[%d] vs input[%d]", + existed.GetIntProp(KvDBProperties::SECURITY_LABEL, 0), + input.GetIntProp(KvDBProperties::SECURITY_LABEL, 0)); + return false; + } + if (existed.GetIntProp(KvDBProperties::SECURITY_FLAG, 0) != + input.GetIntProp(KvDBProperties::SECURITY_FLAG, 0)) { + LOGE("Security flag mismatch: existed[%d] vs input[%d]", + existed.GetIntProp(KvDBProperties::SECURITY_FLAG, 0), + input.GetIntProp(KvDBProperties::SECURITY_FLAG, 0)); + return false; + } + } + return true; + } +} + +int KvDBManager::CheckKvDBProperties(const IKvDB *kvDB, const KvDBProperties &properties, + bool isNeedCheckPasswd) const +{ + // if get from cache is not memoryDb, do not support open or create memory DB + bool isMemoryDb = properties.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + if (isMemoryDb != kvDB->GetMyProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false)) { + LOGE("Already open same id physical DB, so do not support open or create memory DB"); + return -E_INVALID_ARGS; + } + + if (kvDB->GetMyProperties().GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false) != + properties.GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false)) { + LOGE("Different ways to create dir."); + return -E_INVALID_ARGS; + } + + if (kvDB->GetMyProperties().GetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, 0) != + properties.GetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, 0)) { + LOGE("Different conflict resolve policy."); + return -E_INVALID_ARGS; + } + + if (kvDB->GetMyProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false) != + properties.GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false)) { + LOGE("Different dual tuple sync mode"); + return -E_MODE_MISMATCH; + } + + if (!CheckSecOptions(properties, kvDB->GetMyProperties())) { + return -E_INVALID_ARGS; + } + + CipherType cacheType; + CipherType inputType; + CipherPassword cachePasswd; + CipherPassword inputPasswd; + kvDB->GetMyProperties().GetPassword(cacheType, cachePasswd); + properties.GetPassword(inputType, inputPasswd); + if (isNeedCheckPasswd && (cachePasswd != inputPasswd || !IsSameCipher(cacheType, inputType))) { + LOGE("Identification not matched"); + return -E_INVALID_PASSWD_OR_CORRUPTED_DB; + } + + return CheckSchema(kvDB, properties); +} + +// Attention. After call FindKvDB and kvdb is not null, you need to call DecObjRef. +IKvDB* KvDBManager::FindKvDB(const std::string &identifier) const +{ + std::lock_guard lockGuard(kvDBLock_); + auto kvdb = singleVerNaturalStores_.find(identifier); + if (kvdb != singleVerNaturalStores_.end()) { + // Increase ref counter here. + RefObject::IncObjRef(kvdb->second); + return kvdb->second; + } + return nullptr; +} + +void KvDBManager::Dump(int fd) +{ + std::lock_guard lockGuard(kvDBLock_); + for (auto &entry : singleVerNaturalStores_) { + RefObject::IncObjRef(entry.second); + entry.second->Dump(fd); + RefObject::DecObjRef(entry.second); + } +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/kvdb_observer_handle.cpp b/mock/distributeddb/storage/src/kvdb_observer_handle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c089da5a3c587dbfeb4c8896f9ce5945ff01dca --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_observer_handle.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "kvdb_observer_handle.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +KvDBObserverHandle::KvDBObserverHandle(uint32_t mode) + : mode_(mode) +{} + +KvDBObserverHandle::~KvDBObserverHandle() +{ + for (auto &listener : listeners_) { + int errCode = listener->Drop(true); + if (errCode != E_OK) { + LOGE("Drop listener failed!"); + } + listener = nullptr; + } +} + +void KvDBObserverHandle::InsertListener(NotificationChain::Listener *listener) +{ + listeners_.push_back(listener); + return; +} + +uint32_t KvDBObserverHandle::GetObserverMode() const +{ + return mode_; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/kvdb_observer_handle.h b/mock/distributeddb/storage/src/kvdb_observer_handle.h new file mode 100644 index 0000000000000000000000000000000000000000..2833d10cf0117beb35487577bdcc9ce328658cb8 --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_observer_handle.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_DB_OBSERVER_HANDLE_H +#define KV_DB_OBSERVER_HANDLE_H + +#include + +#include "macro_utils.h" +#include "notification_chain.h" + +namespace DistributedDB { +class KvDBObserverHandle { +public: + explicit KvDBObserverHandle(uint32_t mode); + ~KvDBObserverHandle(); + DISABLE_COPY_ASSIGN_MOVE(KvDBObserverHandle); + void InsertListener(NotificationChain::Listener *listener); + uint32_t GetObserverMode() const; +private: + std::list listeners_; + uint32_t mode_; +}; +} // namespace DistributedDB + +#endif // KV_DB_OBSERVER_H diff --git a/mock/distributeddb/storage/src/kvdb_properties.cpp b/mock/distributeddb/storage/src/kvdb_properties.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c10569d8213c9230958c4ff4719f5b1e9531942e --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_properties.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kvdb_properties.h" + +#include "db_constant.h" + +namespace DistributedDB { +const std::string KvDBProperties::FILE_NAME = "fileName"; +const std::string KvDBProperties::MEMORY_MODE = "memoryMode"; +const std::string KvDBProperties::ENCRYPTED_MODE = "isEncryptedDb"; +const std::string KvDBProperties::FIRST_OPEN_IS_READ_ONLY = "firstOpenIsReadOnly"; +const std::string KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY = "createDirByStoreIdOnly"; +const std::string KvDBProperties::SECURITY_LABEL = "securityLabel"; +const std::string KvDBProperties::SECURITY_FLAG = "securityFlag"; +const std::string KvDBProperties::CONFLICT_RESOLVE_POLICY = "conflictResolvePolicy"; +const std::string KvDBProperties::CHECK_INTEGRITY = "checkIntegrity"; +const std::string KvDBProperties::RM_CORRUPTED_DB = "rmCorruptedDb"; +const std::string KvDBProperties::COMPRESS_ON_SYNC = "needCompressOnSync"; +const std::string KvDBProperties::COMPRESSION_RATE = "compressionRate"; + +KvDBProperties::KvDBProperties() + : cipherType_(CipherType::AES_256_GCM) +{} + +KvDBProperties::~KvDBProperties() {} + +std::string KvDBProperties::GetStoreSubDirectory(int type) +{ + switch (type) { + case LOCAL_TYPE: + return DBConstant::LOCAL_SUB_DIR; + case MULTI_VER_TYPE: + return DBConstant::MULTI_SUB_DIR; + case SINGLE_VER_TYPE: + return DBConstant::SINGLE_SUB_DIR; + default: + return "unknown"; + } +} + +void KvDBProperties::GetPassword(CipherType &type, CipherPassword &password) const +{ + type = cipherType_; + password = password_; +} + +void KvDBProperties::SetPassword(CipherType type, const CipherPassword &password) +{ + cipherType_ = type; + password_ = password; +} + +bool KvDBProperties::IsSchemaExist() const +{ + return schema_.IsSchemaValid(); +} + +void KvDBProperties::SetSchema(const SchemaObject &schema) +{ + schema_ = schema; +} + +SchemaObject KvDBProperties::GetSchema() const +{ + return schema_; +} + +int KvDBProperties::GetSecLabel() const +{ + return GetIntProp(KvDBProperties::SECURITY_LABEL, 0); +} + +int KvDBProperties::GetSecFlag() const +{ + return GetIntProp(KvDBProperties::SECURITY_FLAG, 0); +} + +const SchemaObject &KvDBProperties::GetSchemaConstRef() const +{ + return schema_; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/kvdb_utils.cpp b/mock/distributeddb/storage/src/kvdb_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0cedc59e6c021317c1bf0df10b02c9abb4361573 --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_utils.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "kvdb_utils.h" + +#include "platform_specific.h" +#include "log_print.h" +#include "db_constant.h" +#include "db_errno.h" +#include "db_common.h" +#include "sqlite_utils.h" + +namespace DistributedDB { +void KvDBUtils::GetStoreDirectory(std::string &directory, const std::string &identifierName) +{ + if (!directory.empty() && directory.back() != '/') { + directory += "/"; + } + directory += identifierName; + return; +} + +int KvDBUtils::RemoveKvDB(const std::string &dirAll, const std::string &dirStoreOnly, const std::string &dbName) +{ + int errCodeAll = KvDBUtils::RemoveKvDB(dirAll, dbName); + if (errCodeAll != -E_NOT_FOUND && errCodeAll != E_OK) { + return errCodeAll; + } + int errCodeOnlyStore = KvDBUtils::RemoveKvDB(dirStoreOnly, dbName); + if (errCodeOnlyStore != -E_NOT_FOUND && errCodeOnlyStore != E_OK) { + return errCodeOnlyStore; + } + if ((errCodeAll == -E_NOT_FOUND) && (errCodeOnlyStore == -E_NOT_FOUND)) { + return -E_NOT_FOUND; + } + return E_OK; +} + +int KvDBUtils::RemoveKvDB(const std::string &dir, const std::string &dbName) +{ + std::string dbFileName = dir; + GetStoreDirectory(dbFileName, dbName); + dbFileName += DBConstant::SQLITE_DB_EXTENSION; + int errCode = E_OK; + if (OS::CheckPathExistence(dbFileName)) { + errCode = DBCommon::RemoveAllFilesOfDirectory(dir, true); + if (errCode != E_OK) { + LOGE("Failed to delete the db file! errno[%d], errCode[%d]", errno, errCode); + return -E_REMOVE_FILE; + } + } else { + errCode = -E_NOT_FOUND; + LOGD("Db file not existed! errCode[%d]", errCode); + } + return errCode; +} + +int KvDBUtils::GetKvDbSize(const std::string &dirAll, const std::string &dirStoreOnly, + const std::string &dbName, uint64_t &size) +{ + int errCodeAll = SQLiteUtils::GetDbSize(dirAll, dbName, size); + if (errCodeAll != -E_NOT_FOUND && errCodeAll != E_OK) { + return errCodeAll; + } + + int errCodeOnlyStore = SQLiteUtils::GetDbSize(dirStoreOnly, dbName, size); + if (errCodeOnlyStore != -E_NOT_FOUND && errCodeOnlyStore != E_OK) { + return errCodeOnlyStore; + } + if ((errCodeAll == -E_NOT_FOUND) && (errCodeOnlyStore == -E_NOT_FOUND)) { + return -E_NOT_FOUND; + } + return E_OK; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/storage/src/kvdb_utils.h b/mock/distributeddb/storage/src/kvdb_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..b55ac8dac228f448f4f0cd2e13619dd06cd28852 --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_utils.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KVDB_UTILLS_H +#define KVDB_UTILLS_H +#include + +#include "store_types.h" +#include "macro_utils.h" + +namespace DistributedDB { +class KvDBUtils final { +public: + DISABLE_COPY_ASSIGN_MOVE(KvDBUtils); + ~KvDBUtils() {} + + static void GetStoreDirectory(std::string &directory, const std::string &identifierName); + + static int GetKvDbSize(const std::string &dirAll, const std::string &dirStoreOnly, + const std::string &dbName, uint64_t &size); + + static int RemoveKvDB(const std::string &dir, const std::string &dbName); + static int RemoveKvDB(const std::string &dirAll, const std::string &dirStoreOnly, const std::string &dbName); +}; +} // namespace DistributedDB +#endif // LOCAL_KVDB_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/kvdb_windowed_result_set.cpp b/mock/distributeddb/storage/src/kvdb_windowed_result_set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca269bf6cf41eefd4a427813284b8e32b28117a8 --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_windowed_result_set.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "kvdb_windowed_result_set.h" + +#include "db_errno.h" + +namespace DistributedDB { +KvDBWindowedResultSet::KvDBWindowedResultSet() + : window_(nullptr) +{ +} + +KvDBWindowedResultSet::~KvDBWindowedResultSet() +{ + window_ = nullptr; +} + +int KvDBWindowedResultSet::Open(bool isMemDb) +{ + return -E_NOT_SUPPORT; +} + +int KvDBWindowedResultSet::GetCount() const +{ + return 0; +} + +int KvDBWindowedResultSet::GetPosition() const +{ + return -1; // return invalid position +} + +int KvDBWindowedResultSet::MoveTo(int position) const +{ + return -E_NOT_SUPPORT; +} + +int KvDBWindowedResultSet::GetEntry(Entry &entry) const +{ + return -E_NOT_SUPPORT; +} + +void KvDBWindowedResultSet::Close() +{ +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/kvdb_windowed_result_set.h b/mock/distributeddb/storage/src/kvdb_windowed_result_set.h new file mode 100644 index 0000000000000000000000000000000000000000..2c5fe3b7d7eece7188b54bf1559a454a52f0cff9 --- /dev/null +++ b/mock/distributeddb/storage/src/kvdb_windowed_result_set.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_DB_WINDOWED_RESULT_SET_H +#define KV_DB_WINDOWED_RESULT_SET_H + +#include "ikvdb_result_set.h" +#include "result_entries_window.h" + +namespace DistributedDB { +class KvDBWindowedResultSet : public IKvDBResultSet { +public: + KvDBWindowedResultSet(); + ~KvDBWindowedResultSet() override; + DISABLE_COPY_ASSIGN_MOVE(KvDBWindowedResultSet); + + // Initialize logic + int Open(bool isMemDb) override; + + // Get total entries count. + // >= 0: count, < 0: errCode. + int GetCount() const override; + + // Get current read position. + // >= 0: position, < 0: errCode + int GetPosition() const override; + + // Move the read position to an absolute position value. + int MoveTo(int position) const override; + + // Get the entry of current position. + int GetEntry(Entry &entry) const override; + + // Finalize logic + void Close() override; + +private: + ResultEntriesWindow *window_; +}; +} // namespace DistributedDB + +#endif // KV_DB_WINDOWED_RESULT_SET_H diff --git a/mock/distributeddb/storage/src/local_kvdb.h b/mock/distributeddb/storage/src/local_kvdb.h new file mode 100644 index 0000000000000000000000000000000000000000..22f8fa5aef5f2256e379b3d89f5c8b12d44027a4 --- /dev/null +++ b/mock/distributeddb/storage/src/local_kvdb.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOCAL_KVDB_H +#define LOCAL_KVDB_H + +#include "generic_kvdb.h" + +namespace DistributedDB { +class LocalKvDB : public GenericKvDB { +public: + LocalKvDB() = default; + ~LocalKvDB() override {} + + DISABLE_COPY_ASSIGN_MOVE(LocalKvDB); +}; +} // namespace DistributedDB + +#endif // LOCAL_KVDB_H diff --git a/mock/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.cpp b/mock/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8c859c6613cb05361df2c273bfc4a80da4118e20 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "generic_multi_ver_kv_entry.h" + +#include "multi_ver_value_object.h" +#include "parcel.h" + +namespace DistributedDB { +GenericMultiVerKvEntry::GenericMultiVerKvEntry() + : operFlag_(0), + timestamp_(0), + oriTimestamp_(0) +{} + +GenericMultiVerKvEntry::~GenericMultiVerKvEntry() +{} + +int GenericMultiVerKvEntry::GetSerialData(std::vector &data) const +{ + std::vector valueObjectSerial; + int errCode = valueObject_.GetSerialData(valueObjectSerial); + if (errCode != E_OK) { + return errCode; + } + + size_t totalLength = Parcel::GetVectorCharLen(key_) + Parcel::GetVectorCharLen(valueObjectSerial) + + Parcel::GetUInt64Len() * 3; // 3 means number of 3 uint64_t parameters: operFlag, timestamp and oriTimestamp. + + data.resize(totalLength); + Parcel parcel(data.data(), data.size()); + + errCode = parcel.WriteVectorChar(key_); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteVectorChar(valueObjectSerial); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteUInt64(operFlag_); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteUInt64(timestamp_); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteUInt64(oriTimestamp_); + if (errCode != E_OK) { + return errCode; + } + + return E_OK; +} + +int GenericMultiVerKvEntry::GetValueHash(std::vector &valueHashes) const +{ + return valueObject_.GetValueHash(valueHashes); +} + +int GenericMultiVerKvEntry::DeSerialData(const std::vector &data) +{ + Parcel parcel(const_cast(data.data()), data.size()); + parcel.ReadVectorChar(key_); + std::vector objectData; + parcel.ReadVectorChar(objectData); + parcel.ReadUInt64(operFlag_); + parcel.ReadUInt64(timestamp_); + parcel.ReadUInt64(oriTimestamp_); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + return valueObject_.DeSerialData(objectData); +} + +int GenericMultiVerKvEntry::SetKey(const Key &key) +{ + key_ = key; + return E_OK; +} + +int GenericMultiVerKvEntry::GetKey(Key &key) const +{ + key = key_; + return E_OK; +} + +int GenericMultiVerKvEntry::SetValue(const Value &value) +{ + return valueObject_.DeSerialData(value); +} + +int GenericMultiVerKvEntry::GetValue(Value &value)const +{ + return valueObject_.GetSerialData(value); +} + +void GenericMultiVerKvEntry::SetOperFlag(uint64_t flag) +{ + operFlag_ = flag; +} + +void GenericMultiVerKvEntry::GetOperFlag(uint64_t &flag) const +{ + flag = operFlag_; +} + +uint32_t GenericMultiVerKvEntry::GetDataLength() const +{ + return valueObject_.GetDataLength(); +} + +void GenericMultiVerKvEntry::SetDataLength(uint32_t length) +{ + valueObject_.SetDataLength(length); +} + +void GenericMultiVerKvEntry::SetTimestamp(uint64_t timestamp) +{ + timestamp_ = timestamp; +} + +void GenericMultiVerKvEntry::GetTimestamp(uint64_t ×tamp) const +{ + timestamp = timestamp_; +} + +void GenericMultiVerKvEntry::SetOriTimestamp(uint64_t oriTimestamp) +{ + oriTimestamp_ = oriTimestamp; +} + +void GenericMultiVerKvEntry::GetOriTimestamp(uint64_t &oriTimestamp) const +{ + oriTimestamp = oriTimestamp_; +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.h b/mock/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.h new file mode 100644 index 0000000000000000000000000000000000000000..9fc13a46cc7c55078214f6804ee17ac85a73ea90 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GENERIC_MULTI_VER_KVENTRY_H +#define GENERIC_MULTI_VER_KVENTRY_H + +#ifndef OMIT_MULTI_VER +#include + +#include "macro_utils.h" +#include "multi_ver_kv_entry.h" +#include "multi_ver_value_object.h" + +namespace DistributedDB { +class GenericMultiVerKvEntry : public MultiVerKvEntry { +public: + GenericMultiVerKvEntry(); + ~GenericMultiVerKvEntry() override; + DISABLE_COPY_ASSIGN_MOVE(GenericMultiVerKvEntry); + + int GetSerialData(std::vector &data) const override; + + int GetValueHash(std::vector &valueHashes) const override; + + void GetTimestamp(uint64_t ×tamp) const override; + void SetTimestamp(uint64_t timestamp) override; + + void GetOriTimestamp(uint64_t &oriTimestamp) const; + void SetOriTimestamp(uint64_t oriTimestamp); + + int DeSerialData(const std::vector &data); + + int SetKey(const Key &key); + int GetKey(Key &key) const; + + int SetValue(const Value &value); + int GetValue(Value &value) const; + + void SetOperFlag(uint64_t flag); + void GetOperFlag(uint64_t &flag) const; + + void SetDataLength(uint32_t length); + uint32_t GetDataLength() const; + +private: + std::vector key_; + MultiVerValueObject valueObject_; + uint64_t operFlag_; + uint64_t timestamp_; + uint64_t oriTimestamp_; +}; +} // namespace DistributedDB + +#endif // GENERIC_MULTI_VER_KVENTRY_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/ikvdb_multi_ver_data_storage.h b/mock/distributeddb/storage/src/multiver/ikvdb_multi_ver_data_storage.h new file mode 100644 index 0000000000000000000000000000000000000000..49e5f598ab5116d07228b9d2874e1e7619fff557 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/ikvdb_multi_ver_data_storage.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KV_DB_MULTI_VER_DATA_STORAGE_H +#define I_KV_DB_MULTI_VER_DATA_STORAGE_H + +#ifndef OMIT_MULTI_VER +#include +#include + +#include "db_types.h" +#include "ikvdb_multi_ver_transaction.h" +#include "multi_ver_def.h" +#include "multi_ver_kv_entry.h" + +namespace DistributedDB { +enum class KvDataType { + KV_DATA_LOCAL, + KV_DATA_SYNC_P2P, +}; + +struct UpdateVerTimestamp { + uint64_t timestamp = 0ull; + bool isNeedUpdate = false; +}; + +class IKvDBMultiVerDataStorage { +public: + struct Property final { + std::string path; + std::string identifierName; + bool isNeedCreate = true; + CipherType cipherType = CipherType::AES_256_GCM; + CipherPassword passwd; + }; + + virtual ~IKvDBMultiVerDataStorage() {}; + virtual int Open(const Property &property) = 0; + virtual int StartWrite(KvDataType dataType, IKvDBMultiVerTransaction *&transaction) = 0; + virtual int CommitWritePhaseOne(IKvDBMultiVerTransaction *transaction, + const UpdateVerTimestamp &multiVerTimestamp) = 0; + virtual int RollbackWritePhaseOne(IKvDBMultiVerTransaction *transaction, const Version &versionInfo) = 0; + virtual int RollbackWrite(IKvDBMultiVerTransaction *transaction) = 0; + virtual void CommitWritePhaseTwo(IKvDBMultiVerTransaction *transaction) = 0; + virtual IKvDBMultiVerTransaction *StartRead(KvDataType dataType, const Version &versionInfo, int &errCode) = 0; + virtual void ReleaseTransaction(IKvDBMultiVerTransaction *transaction) = 0; + virtual void Close() = 0; + virtual int CheckVersion(const Property &property, bool &isDbExist) const = 0; + virtual int GetVersion(const Property &property, int &version, bool &isDbExisted) const = 0; + virtual int BackupCurrentDatabase(const Property &property, const std::string &dir) = 0; + virtual int ImportDatabase(const Property &property, const std::string &dir, const CipherPassword &passwd) = 0; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_MULTI_VER_DATA_STORAGE_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/ikvdb_multi_ver_transaction.h b/mock/distributeddb/storage/src/multiver/ikvdb_multi_ver_transaction.h new file mode 100644 index 0000000000000000000000000000000000000000..3cb433d574a9c6d1742f1cef63657ad8841dfdca --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/ikvdb_multi_ver_transaction.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_KV_DB_MULTI_VER_TRANSACTION_H +#define I_KV_DB_MULTI_VER_TRANSACTION_H + +#ifndef OMIT_MULTI_VER +#include "db_types.h" +#include "multi_ver_def.h" +#include "multi_ver_kv_entry.h" + +namespace DistributedDB { +class IKvDBMultiVerTransaction { +public: + virtual ~IKvDBMultiVerTransaction() {}; + virtual int Put(const Key &key, const Value &value) = 0; + virtual int Delete(const Key &key) = 0; + virtual int Clear() = 0; + virtual int Get(const Key &key, Value &value) const = 0; + virtual int GetEntries(const Key &keyPrefix, std::vector &entries) const = 0; + virtual int PutBatch(const std::vector &entries, bool isLocal, + std::vector &values) = 0; + virtual int GetEntriesByVersion(const Version &versionInfo, std::vector &entries) const = 0; + virtual int GetDiffEntries(const Version &begin, const Version &end, MultiVerDiffData &data) const = 0; + virtual int GetMaxVersion(MultiVerDataType type, Version &maxVersion) const = 0; + virtual int ClearEntriesByVersion(const Version &versionInfo) = 0; + virtual int StartTransaction() = 0; + virtual int RollBackTransaction() = 0; + virtual int CommitTransaction() = 0; + virtual int UpdateTimestampByVersion(const Version &version, Timestamp stamp) const = 0; + virtual bool IsDataChanged() const = 0; // only for write transaction. + virtual bool IsRecordCleared(const Timestamp timestamp) const = 0; + virtual Timestamp GetCurrentMaxTimestamp() const = 0; + virtual void SetVersion(const Version &versionInfo) = 0; + virtual Version GetVersion() const = 0; + virtual int GetEntriesByVersion(Version version, std::list &data) const = 0; + virtual int GetOverwrittenClearTypeEntries(Version clearVersion, + std::list &data) const = 0; + virtual int GetOverwrittenNonClearTypeEntries(Version version, const Key &hashKey, + std::list &data) const = 0; + virtual int DeleteEntriesByHashKey(Version version, const Key &hashKey) = 0; + virtual int GetValueForTrimSlice(const Key &hashKey, const Version vision, Value &value) const = 0; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_MULTI_VER_TRANSACTION_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_commit.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_commit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56e4c35e320346c0a951be98cbae7c2a61e3a28f --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_commit.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "log_print.h" +#include "multi_ver_commit.h" + +namespace DistributedDB { +MultiVerCommit::MultiVerCommit() + : versionInfo_(0), + timestamp_(0), + localFlag_(true) +{} + +MultiVerCommit::~MultiVerCommit() +{} + +Version MultiVerCommit::GetCommitVersion() const +{ + return versionInfo_; +} + +void MultiVerCommit::SetCommitVersion(const Version &versionInfo) +{ + versionInfo_ = versionInfo; + return; +} + +CommitID MultiVerCommit::GetCommitId() const +{ + return commitID_; +} + +void MultiVerCommit::SetCommitId(const CommitID &id) +{ + commitID_ = id; + return; +} + +CommitID MultiVerCommit::GetLeftParentId() const +{ + return leftParentID_; +} + +void MultiVerCommit::SetLeftParentId(const CommitID &id) +{ + leftParentID_ = id; + return; +} + +CommitID MultiVerCommit::GetRightParentId() const +{ + return rightParentID_; +} + +void MultiVerCommit::SetRightParentId(const CommitID &id) +{ + rightParentID_ = id; + return; +} + +Timestamp MultiVerCommit::GetTimestamp() const +{ + return timestamp_; +} + +void MultiVerCommit::SetTimestamp(Timestamp timestamp) +{ + timestamp_ = timestamp; + return; +} + +bool MultiVerCommit::GetLocalFlag() const +{ + return localFlag_; +} + +void MultiVerCommit::SetLocalFlag(bool localFlag) +{ + localFlag_ = localFlag; + return; +} + +DeviceID MultiVerCommit::GetDeviceInfo() const +{ + return deviceInfo_; +} + +void MultiVerCommit::SetDeviceInfo(const DeviceID &deviceInfo) +{ + deviceInfo_ = deviceInfo; + return; +} + +bool MultiVerCommit::CheckCommit() const +{ + if (commitID_.size() == 0 || commitID_.size() > MAX_COMMIT_ID_LENGTH || + leftParentID_.size() > MAX_COMMIT_ID_LENGTH || rightParentID_.size() > MAX_COMMIT_ID_LENGTH || + deviceInfo_.size() > MAX_COMMIT_DEV_LENGTH) { + LOGE("Check commit failed! Error length of commit ID."); + return false; + } + // commitId should not equal to the parentId; the left parent should not equal to the right + if (commitID_ == leftParentID_ || commitID_ == rightParentID_ || + (leftParentID_ == rightParentID_ && leftParentID_.size() != 0)) { + LOGE("Check commit failed! Wrong commit ID."); + return false; + } + return true; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_commit.h b/mock/distributeddb/storage/src/multiver/multi_ver_commit.h new file mode 100644 index 0000000000000000000000000000000000000000..7ba7c292edfadbea92cdd944455cb0d6c7a95df9 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_commit.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_COMMIT_H +#define MULTI_VER_COMMIT_H + +#ifndef OMIT_MULTI_VER +#include "ikvdb_commit.h" + +#include "multi_ver_def.h" +#include "macro_utils.h" + +namespace DistributedDB { +class MultiVerCommit final : public IKvDBCommit { +public: + MultiVerCommit(); + ~MultiVerCommit() override; + + DISABLE_COPY_ASSIGN_MOVE(MultiVerCommit); + + Version GetCommitVersion() const override; + void SetCommitVersion(const Version &versionInfo) override; + + CommitID GetCommitId() const override; + void SetCommitId(const CommitID &id) override; + + CommitID GetLeftParentId() const override; + void SetLeftParentId(const CommitID &id) override; + + CommitID GetRightParentId() const override; + void SetRightParentId(const CommitID &id) override; + + Timestamp GetTimestamp() const override; + void SetTimestamp(Timestamp timestamp) override; + + bool GetLocalFlag() const override; + void SetLocalFlag(bool localFlag) override; + + DeviceID GetDeviceInfo() const override; + void SetDeviceInfo(const DeviceID &deviceInfo) override; + + bool CheckCommit() const; + +private: + static const size_t MAX_VERSION_INFO_LENGTH = 4096; + static const size_t MAX_COMMIT_ID_LENGTH = 128; + static const size_t MAX_COMMIT_DEV_LENGTH = 256; + Version versionInfo_; + CommitID commitID_; + CommitID leftParentID_; + CommitID rightParentID_; + Timestamp timestamp_; + bool localFlag_; + DeviceID deviceInfo_; +}; +} + +#endif +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_kv_entry.h b/mock/distributeddb/storage/src/multiver/multi_ver_kv_entry.h new file mode 100644 index 0000000000000000000000000000000000000000..f0390500267803b85b3cab72b029182d006bb330 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_kv_entry.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_KV_ENTRY_H +#define MULTI_VER_KV_ENTRY_H + +#ifndef OMIT_MULTI_VER +#include + +#include "multi_ver_value_object.h" + +namespace DistributedDB { +class MultiVerKvEntry { +public: + virtual ~MultiVerKvEntry() {}; + + virtual int GetSerialData(std::vector &data) const = 0; + + virtual int GetValueHash(std::vector &valueHashes) const = 0; + + virtual void GetTimestamp(uint64_t ×tamp) const = 0; + + virtual void SetTimestamp(uint64_t timestamp) = 0; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_KV_ENTRY_H +#endif diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25857f603edd5f37fb6c7b964e4de0bfdcb0fec9 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.cpp @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_kvdata_storage.h" + +#include "db_constant.h" +#include "db_errno.h" +#include "log_print.h" +#include "ikvdb_factory.h" +#include "sqlite_local_kvdb.h" +#include "parcel.h" + +namespace DistributedDB { +namespace { + const uint8_t HASH_COUNT_MAGIC = '$'; + const uint32_t EXPECT_ENTRIES_NUM = 2; + struct DatabaseIdentifierCfg { + std::string databaseDir; + std::string identifier; + std::string fileName; + }; +} + +static IKvDB *OpenKvDB(const DatabaseIdentifierCfg &config, CipherType type, const CipherPassword &passwd, int &errCode) +{ + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + if (factory == nullptr) { + LOGE("Failed to open IKvDB! Get factory failed."); + return nullptr; + } + + IKvDB *kvDB = factory->CreateKvDb(LOCAL_KVDB, errCode); + if (kvDB == nullptr) { + LOGE("Create local kvdb failed:%d", errCode); + return nullptr; + } + + KvDBProperties dbProperties; + dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + dbProperties.SetStringProp(KvDBProperties::DATA_DIR, config.databaseDir); + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, config.fileName); + dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, config.identifier); + dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + dbProperties.SetPassword(type, passwd); + + errCode = kvDB->Open(dbProperties); + if (errCode != E_OK) { + LOGE("Failed to open IKvDB! err:%d", errCode); + RefObject::KillAndDecObjRef(kvDB); + kvDB = nullptr; + return nullptr; + } + // Need to refactor in the future + int version = ((config.fileName == DBConstant::MULTI_VER_VALUE_STORE) ? + MULTI_VER_VALUESLICE_STORAGE_VERSION_CURRENT : MULTI_VER_METADATA_STORAGE_VERSION_CURRENT); + errCode = static_cast(kvDB)->SetVersion(dbProperties, version); + if (errCode != E_OK) { + LOGE("[KvStorage][OpenDB] SetVersion fail, errCode=%d.", errCode); + RefObject::KillAndDecObjRef(kvDB); + kvDB = nullptr; + return nullptr; + } + + return kvDB; +} + +static int PutData(IKvDBConnection *kvDBConnection, const Key &key, const Value &value) +{ + if (kvDBConnection == nullptr) { + return -E_INVALID_DB; + } + + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE || value.size() > DBConstant::MAX_VALUE_SIZE) { + return -E_INVALID_ARGS; + } + + IOption option; + int errCode = kvDBConnection->Put(option, key, value); + if (errCode != E_OK) { + LOGE("put data failed:%d", errCode); + } + + return errCode; +} + +static int DeleteData(IKvDBConnection *kvDBConnection, const Key &key) +{ + if (kvDBConnection == nullptr) { + return -E_INVALID_DB; + } + + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + + IOption option; + int errCode = kvDBConnection->Delete(option, key); + if (errCode != E_OK) { + LOGE("Delete data failed:%d", errCode); + } + + return errCode; +} + +static int GetData(const IKvDBConnection *kvDBConnection, const Key &key, Value &value) +{ + if (kvDBConnection == nullptr) { + return -E_INVALID_DB; + } + + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + + IOption option; + int errCode = kvDBConnection->Get(option, key, value); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + LOGE("Get data failed:%d", errCode); + } + + return errCode; +} + +static int GetEntries(const IKvDBConnection *kvDBConnection, const Key &keyPrefix, std::vector &entries) +{ + if (kvDBConnection == nullptr) { + return -E_INVALID_DB; + } + + if (keyPrefix.empty() || keyPrefix.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + + IOption option; + int errCode = kvDBConnection->GetEntries(option, keyPrefix, entries); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + LOGE("Get entries failed:%d", errCode); + } + + return errCode; +} + +static int GetSliceCount(std::vector &&entries, uint32_t &count) +{ + std::vector buffer = (entries[0].key.size() > entries[1].key.size()) ? + std::move(entries[0].value) : std::move(entries[1].value); + Parcel parcel(buffer.data(), buffer.size()); + uint32_t size = parcel.ReadUInt32(count); + if (size != sizeof(count) || parcel.IsError()) { + LOGE("Get slice count size:%u", size); + return -E_PARSE_FAIL; + } + return E_OK; +} + +static int PutSliceCount(IKvDBConnection *kvDBConnection, const Key &sliceKey, uint32_t count) +{ + Key countKey(sliceKey); + countKey.push_back(HASH_COUNT_MAGIC); + std::vector buffer(sizeof(uint32_t), 0); + Parcel parcel(buffer.data(), buffer.size()); + int errCode = parcel.WriteUInt32(count); + if (errCode != E_OK) { + return errCode; + } + IOption option; + errCode = kvDBConnection->Put(option, countKey, buffer); + if (errCode != E_OK) { + LOGE("Put slice count failed:%d", errCode); + } + return errCode; +} + +static int PutSlice(IKvDBConnection *kvDBConnection, const Key &key, const Value &value, bool isAddCount) +{ + std::vector entries; + int errCode = GetEntries(kvDBConnection, key, entries); + uint32_t dataCount = 1; + switch (errCode) { + case E_OK: + if (entries.size() != EXPECT_ENTRIES_NUM) { + return -E_INCORRECT_DATA; + } + errCode = GetSliceCount(std::move(entries), dataCount); + if (errCode != E_OK) { + return errCode; + } + dataCount++; + errCode = PutSliceCount(kvDBConnection, key, dataCount); + return errCode; + case -E_NOT_FOUND: + errCode = PutData(kvDBConnection, key, value); + if (errCode != E_OK) { + return errCode; + } + dataCount = isAddCount ? 1 : 0; + errCode = PutSliceCount(kvDBConnection, key, dataCount); + return errCode; + default: + return errCode; + } +} + +static int DeleteSlice(IKvDBConnection *kvDBConnection, const Key &key) +{ + std::vector entries; + int errCode = GetEntries(kvDBConnection, key, entries); + if (errCode != E_OK) { + return errCode; + } + if (entries.size() != EXPECT_ENTRIES_NUM) { + return -E_INCORRECT_DATA; + } + uint32_t dataCount = 0; + Key countKey(key); + errCode = GetSliceCount(std::move(entries), dataCount); + if (errCode != E_OK) { + return errCode; + } + if (dataCount > 1) { + dataCount--; + errCode = PutSliceCount(kvDBConnection, key, dataCount); + return errCode; + } else { + errCode = DeleteData(kvDBConnection, key); + if (errCode != E_OK) { + return errCode; + } + countKey.push_back(HASH_COUNT_MAGIC); + errCode = DeleteData(kvDBConnection, countKey); + return errCode; + } +} + +MultiVerKvDataStorage::MultiVerKvDataStorage() + : kvStorage_(nullptr), + metaStorage_(nullptr), + kvStorageConnection_(nullptr), + metaStorageConnection_(nullptr) +{} + +MultiVerKvDataStorage::~MultiVerKvDataStorage() +{ + Close(); +} + +int MultiVerKvDataStorage::CheckVersion(const Property &property, bool &isDbAllExist) const +{ + int metaDataVer = 0; + int valueSliceVer = 0; + bool isMetaDbExist = false; + bool isSliceDbExist = false; + int errCode = GetVersion(property, metaDataVer, isMetaDbExist, valueSliceVer, isSliceDbExist); + if (errCode != E_OK) { + LOGE("[KvStorage][CheckVer] GetVersion failed, errCode=%d.", errCode); + return errCode; + } + if (isMetaDbExist != isSliceDbExist) { + // In case failure happens during open progress, some dbFile will not exist, we should recover from this + LOGW("[KvStorage][CheckVer] Detect File Lost, isMetaDbExist=%d, isSliceDbExist=%d.", + isMetaDbExist, isSliceDbExist); + } + isDbAllExist = isMetaDbExist && isSliceDbExist; + if (!isMetaDbExist && !isSliceDbExist) { + // If both dbFile not exist, just return. + return E_OK; + } + LOGD("[KvStorage][CheckVer] MetaDbVer=%d, CurMetaVer=%d, SliceDbVer=%d, CurSliceVer=%d.", metaDataVer, + MULTI_VER_METADATA_STORAGE_VERSION_CURRENT, valueSliceVer, MULTI_VER_VALUESLICE_STORAGE_VERSION_CURRENT); + // For the dbFile not exist, version value will be 0, do not affect version check below + if (metaDataVer > MULTI_VER_METADATA_STORAGE_VERSION_CURRENT || + valueSliceVer > MULTI_VER_VALUESLICE_STORAGE_VERSION_CURRENT) { + LOGE("[KvStorage][CheckVer] Version Not Support!"); + return -E_VERSION_NOT_SUPPORT; + } + return E_OK; +} + +int MultiVerKvDataStorage::GetVersion(const Property &property, int &metaVer, bool &isMetaDbExist, + int &sliceVer, bool &isSliceDbExist) const +{ + SQLiteLocalKvDB *localKvdb = new (std::nothrow) SQLiteLocalKvDB(); + if (localKvdb == nullptr) { + return -E_INVALID_DB; + } + + KvDBProperties dbProperties; + dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, property.isNeedCreate); + dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.dataDir); + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_VALUE_STORE); + dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName); + dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + dbProperties.SetPassword(property.cipherType, property.passwd); + int errCode = localKvdb->GetVersion(dbProperties, sliceVer, isSliceDbExist); + if (errCode != E_OK) { + LOGE("[KvStorage][GetVer] Get valueSlice storage version fail, errCode=%d.", errCode); + RefObject::DecObjRef(localKvdb); + localKvdb = nullptr; + return errCode; + } + + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_META_STORE); + errCode = localKvdb->GetVersion(dbProperties, metaVer, isMetaDbExist); + if (errCode != E_OK) { + LOGE("[KvStorage][GetVer] Get metaData storage version fail, errCode=%d.", errCode); + RefObject::DecObjRef(localKvdb); + localKvdb = nullptr; + return errCode; + } + + RefObject::DecObjRef(localKvdb); + localKvdb = nullptr; + return E_OK; +} + +int MultiVerKvDataStorage::Open(const Property &property) +{ + int errCode = E_OK; + if (kvStorage_ == nullptr) { + DatabaseIdentifierCfg config = {property.dataDir, property.identifierName, DBConstant::MULTI_VER_VALUE_STORE}; + kvStorage_ = OpenKvDB(config, property.cipherType, property.passwd, errCode); + if (kvStorage_ == nullptr) { + LOGE("open kv storage failed"); + goto END; + } + } + + if (metaStorage_ == nullptr) { + DatabaseIdentifierCfg config = {property.dataDir, property.identifierName, DBConstant::MULTI_VER_META_STORE}; + metaStorage_ = OpenKvDB(config, property.cipherType, property.passwd, errCode); + if (metaStorage_ == nullptr) { + LOGE("open meta storage failed"); + goto END; + } + } + + kvStorageConnection_ = kvStorage_->GetDBConnection(errCode); + if (errCode != E_OK) { + goto END; + } + + metaStorageConnection_ = metaStorage_->GetDBConnection(errCode); + if (errCode != E_OK) { + goto END; + } + +END: + if (errCode != E_OK) { + Close(); + } + return errCode; +} + +void MultiVerKvDataStorage::Close() +{ + if (kvStorageConnection_ != nullptr) { + kvStorageConnection_->Close(); + kvStorageConnection_ = nullptr; + } + + if (metaStorageConnection_ != nullptr) { + metaStorageConnection_->Close(); + metaStorageConnection_ = nullptr; + } + + if (kvStorage_ != nullptr) { + RefObject::KillAndDecObjRef(kvStorage_); + kvStorage_ = nullptr; + } + + if (metaStorage_ != nullptr) { + RefObject::KillAndDecObjRef(metaStorage_); + metaStorage_ = nullptr; + } +} + +int MultiVerKvDataStorage::PutMetaData(const Key &key, const Value &value) +{ + return PutData(metaStorageConnection_, key, value); +} + +int MultiVerKvDataStorage::GetMetaData(const Key &key, Value &value) const +{ + return GetData(metaStorageConnection_, key, value); +} + +int MultiVerKvDataStorage::RunRekeyLogic(CipherType type, const CipherPassword &passwd) +{ + int errCode = static_cast(kvStorage_)->RunRekeyLogic(type, passwd); + if (errCode != E_OK) { + LOGE("value storage rekey failed:%d", errCode); + return errCode; + } + errCode = static_cast(metaStorage_)->RunRekeyLogic(type, passwd); + if (errCode != E_OK) { + LOGE("meta storage rekey failed:%d", errCode); + return errCode; + } + return E_OK; +} + +int MultiVerKvDataStorage::RunExportLogic(CipherType type, const CipherPassword &passwd, const std::string &dbDir) const +{ + // execute export + std::string valueDbName = dbDir + "/value_storage.db"; + int errCode = static_cast(kvStorage_)->RunExportLogic(type, passwd, valueDbName); + if (errCode != E_OK) { + LOGE("value storage export failed:%d", errCode); + return errCode; + } + + std::string metaDbName = dbDir + "/meta_storage.db"; + errCode = static_cast(metaStorage_)->RunExportLogic(type, passwd, metaDbName); + if (errCode != E_OK) { + LOGE("meta storage export failed:%d", errCode); + return errCode; + } + return E_OK; +} + +int MultiVerKvDataStorage::BackupCurrentDatabase(const Property &property, const std::string &dir) +{ + KvDBProperties dbProperties; + dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.dataDir); + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_META_STORE); + dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName); + dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + dbProperties.SetPassword(property.cipherType, property.passwd); + int errCode = SQLiteLocalKvDB::BackupCurrentDatabase(dbProperties, dir); + if (errCode != E_OK) { + return errCode; + } + + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_VALUE_STORE); + return SQLiteLocalKvDB::BackupCurrentDatabase(dbProperties, dir); +} + +int MultiVerKvDataStorage::ImportDatabase(const Property &property, const std::string &dir, + const CipherPassword &passwd) +{ + KvDBProperties dbProperties; + dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.dataDir); + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_META_STORE); + dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName); + dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + dbProperties.SetPassword(property.cipherType, property.passwd); + int errCode = SQLiteLocalKvDB::ImportDatabase(dbProperties, dir, passwd); + if (errCode != E_OK) { + return errCode; + } + + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_VALUE_STORE); + return SQLiteLocalKvDB::ImportDatabase(dbProperties, dir, passwd); +} + +SliceTransaction *MultiVerKvDataStorage::GetSliceTransaction(bool isWrite, int &errCode) +{ + auto connect = kvStorage_->GetDBConnection(errCode); + if (connect == nullptr) { + return nullptr; + } + auto transaction = new (std::nothrow) SliceTransaction(isWrite, connect); + if (transaction == nullptr) { + errCode = -E_OUT_OF_MEMORY; + connect->Close(); + return nullptr; + } + errCode = E_OK; + return transaction; +} + +void MultiVerKvDataStorage::ReleaseSliceTransaction(SliceTransaction *&transaction) +{ + if (transaction == nullptr) { + return; + } + transaction->Close(); + delete transaction; + transaction = nullptr; + return; +} + +SliceTransaction::SliceTransaction(bool isWrite, IKvDBConnection *connect) + : isWrite_(isWrite), + connect_(connect) +{} + +SliceTransaction::~SliceTransaction() +{} + +int SliceTransaction::Close() +{ + if (connect_ == nullptr) { + return E_OK; + } + return connect_->Close(); +} + +int SliceTransaction::PutData(const Key &key, const Value &value, bool isAddCount) +{ + if (!isWrite_) { + return -E_INVALID_CONNECTION; + } + return PutSlice(connect_, key, value, isAddCount); +} + +int SliceTransaction::GetData(const Key &key, Value &value) const +{ + return ::DistributedDB::GetData(connect_, key, value); +} + +int SliceTransaction::DeleteData(const Key &key) +{ + if (!isWrite_) { + return -E_INVALID_CONNECTION; + } + return DeleteSlice(connect_, key); +} + +int SliceTransaction::StartTransaction() +{ + if (connect_ == nullptr) { + return -E_INVALID_CONNECTION; + } + return connect_->StartTransaction(); +} + +int SliceTransaction::CommitTransaction() +{ + if (connect_ == nullptr) { + return -E_INVALID_CONNECTION; + } + return connect_->Commit(); +} + +int SliceTransaction::RollbackTransaction() +{ + if (connect_ == nullptr) { + return -E_INVALID_CONNECTION; + } + return connect_->RollBack(); +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.h b/mock/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.h new file mode 100644 index 0000000000000000000000000000000000000000..82cae19d320e68a252b722d920c6e71e2345b774 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_KV_DATA_STORAGE_H +#define MULTI_VER_KV_DATA_STORAGE_H + +#ifndef OMIT_MULTI_VER +#include +#include + +#include "ikvdb.h" +#include "macro_utils.h" + +namespace DistributedDB { +class SliceTransaction { +public: + SliceTransaction(bool isWrite, IKvDBConnection *connect); + ~SliceTransaction(); + int Close(); + int PutData(const Key &key, const Value &value, bool isAddCount); + int GetData(const Key &key, Value &value) const; + int DeleteData(const Key &key); + int StartTransaction(); + int CommitTransaction(); + int RollbackTransaction(); +private: + bool isWrite_; + IKvDBConnection *connect_; +}; + +class MultiVerKvDataStorage { +public: + struct Property final { + std::string dataDir; + std::string identifierName; + bool isNeedCreate = true; + CipherType cipherType = CipherType::AES_256_GCM; + CipherPassword passwd; + }; + + MultiVerKvDataStorage(); + ~MultiVerKvDataStorage(); + + DISABLE_COPY_ASSIGN_MOVE(MultiVerKvDataStorage); + + int Open(const Property &property); + + void Close(); + + int PutMetaData(const Key &key, const Value &value); + + int GetMetaData(const Key &key, Value &value) const; + + SliceTransaction *GetSliceTransaction(bool isWrite, int &errCode); + + void ReleaseSliceTransaction(SliceTransaction *&transaction); + + int RunRekeyLogic(CipherType type, const CipherPassword &passwd); + + int RunExportLogic(CipherType type, const CipherPassword &passwd, const std::string &dbDir) const; + + int CheckVersion(const Property &property, bool &isDbAllExist) const; + + int BackupCurrentDatabase(const Property &property, const std::string &dir); + + int ImportDatabase(const Property &property, const std::string &dir, const CipherPassword &passwd); + +private: + int GetVersion(const Property &property, int &metaVer, bool &isMetaDbExist, + int &sliceVer, bool &isSliceDbExist) const; + + IKvDB *kvStorage_; + IKvDB *metaStorage_; + IKvDBConnection *kvStorageConnection_; + IKvDBConnection *metaStorageConnection_; + mutable std::mutex metaDataMutex_; + mutable std::mutex kvDataMutex_; +}; +} + +#endif // MULTI_VER_KV_DATA_STORAGE_H +#endif diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f0ca69a85080b0a771378bff3d5e66464bf4e38 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store.cpp @@ -0,0 +1,1195 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_natural_store.h" + +#include +#include + +#include "securec.h" + +#include "db_constant.h" +#include "ikvdb_factory.h" +#include "db_common.h" +#include "endian_convert.h" +#include "log_print.h" +#include "db_errno.h" +#include "multi_ver_storage_engine.h" +#include "multi_ver_natural_store_connection.h" +#include "generic_multi_ver_kv_entry.h" +#include "sqlite_multi_ver_data_storage.h" +#include "multi_ver_natural_store_commit_storage.h" +#include "multi_ver_vacuum_executor_impl.h" +#include "kvdb_utils.h" +#include "sqlite_utils.h" +#include "platform_specific.h" +#include "package_file.h" +#include "multi_ver_database_oper.h" + +namespace DistributedDB { +namespace { + // file block doesn't support the atomic of the upgrade temporarily. + struct VersionFileBlock { + static const uint64_t MAGIC_NUMBER = 0x37F8C35AULL; + uint64_t magic = MAGIC_NUMBER; // magic number. + uint32_t fileVersion = VERSION_FILE_VERSION_CURRENT; // file format version. + uint32_t version = 0U; // version of the database. + uint8_t tag[MULTI_VER_TAG_SIZE] = {0}; // tag of the multi ver branch. + uint8_t reserved[72] = {0}; // reserved data. + uint8_t checkSum[32] = {0}; // check sum + }; + + void TransferHostFileBlockToNet(VersionFileBlock &block) + { + block.magic = HostToNet(block.magic); + block.fileVersion = HostToNet(block.fileVersion); + block.version = HostToNet(block.version); + } + + void TransferNetFileBlockToHost(VersionFileBlock &block) + { + block.magic = NetToHost(block.magic); + block.fileVersion = NetToHost(block.fileVersion); + block.version = NetToHost(block.version); + } + + int CalcFileBlockCheckSum(VersionFileBlock &block) + { + std::vector vect(reinterpret_cast(&block), + reinterpret_cast(&block) + sizeof(block) - sizeof(block.checkSum)); + std::vector hashVect; + int errCode = DBCommon::CalcValueHash(vect, hashVect); + if (errCode != E_OK) { + return errCode; + } + errCode = memcpy_s(block.checkSum, sizeof(block.checkSum), hashVect.data(), hashVect.size()); + if (errCode != EOK) { + return -E_SECUREC_ERROR; + } + return E_OK; + } + + int CheckFileBlock(VersionFileBlock &block) + { + uint64_t readMagic = NetToHost(block.magic); + if (readMagic != block.MAGIC_NUMBER) { + LOGE("Invalid file head"); + return -E_UNEXPECTED_DATA; + } + + std::vector vect(reinterpret_cast(&block), + reinterpret_cast(&block) + sizeof(block) - sizeof(block.checkSum)); + std::vector hashVect; + int errCode = DBCommon::CalcValueHash(vect, hashVect); + if (errCode != E_OK) { + return errCode; + } + if (memcmp(hashVect.data(), block.checkSum, sizeof(block.checkSum)) != 0) { + LOGE("Check block error"); + return -E_UNEXPECTED_DATA; + } + + return E_OK; + } + + int CreateNewVersionFile(const std::string &versionFileDir, uint32_t version, std::vector &tag) + { + VersionFileBlock block; + block.version = version; + RAND_bytes(block.tag, sizeof(block.tag)); + int errCode = memset_s(block.reserved, sizeof(block.reserved), 0, sizeof(block.reserved)); + if (errCode != EOK) { + return -E_SECUREC_ERROR; + } + + TransferHostFileBlockToNet(block); + errCode = CalcFileBlockCheckSum(block); + if (errCode != E_OK) { + return errCode; + } + FILE *versionFile = fopen(versionFileDir.c_str(), "wb+"); + if (versionFile == nullptr) { + LOGE("Open the version file error:%d", errno); + return -E_SYSTEM_API_FAIL; + } + size_t writeSize = fwrite(static_cast(&block), 1, sizeof(VersionFileBlock), versionFile); + if (writeSize != sizeof(VersionFileBlock)) { + LOGE("Write version file head error:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + } else { + errCode = E_OK; + tag.assign(block.tag, block.tag + sizeof(block.tag)); + } + + fclose(versionFile); + versionFile = nullptr; + return errCode; + } + + int ChangeVersionFile(const std::string &versionFileDir, uint32_t version, std::vector &tag, + bool isChangeTag) + { + FILE *versionFile = fopen(versionFileDir.c_str(), "rb+"); + if (versionFile == nullptr) { + LOGE("Open the version file error:%d", errno); + return -E_SYSTEM_API_FAIL; + } + VersionFileBlock block; + size_t operateSize = fread(static_cast(&block), 1, sizeof(VersionFileBlock), versionFile); + if (operateSize != sizeof(VersionFileBlock)) { + fclose(versionFile); + LOGE("Read file error:%d", errno); + return -E_SYSTEM_API_FAIL; + }; + int errCode = CheckFileBlock(block); + if (errCode != E_OK) { + goto END; + } + TransferHostFileBlockToNet(block); + block.version = version; + + if (isChangeTag) { + RAND_bytes(block.tag, sizeof(block.tag)); + tag.assign(block.tag, block.tag + sizeof(block.tag)); + } + + TransferHostFileBlockToNet(block); + errCode = CalcFileBlockCheckSum(block); + if (errCode != E_OK) { + goto END; + } + + fseeko64(versionFile, 0LL, SEEK_SET); + operateSize = fwrite(&block, 1, sizeof(VersionFileBlock), versionFile); + if (operateSize != sizeof(VersionFileBlock)) { + LOGE("write the file error:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + goto END; + } + END: + fclose(versionFile); + versionFile = nullptr; + return errCode; + } + + int GetVersionAndTag(const std::string &versionFileDir, uint32_t &version, std::vector &tag) + { + FILE *versionFile = fopen(versionFileDir.c_str(), "rb+"); + if (versionFile == nullptr) { + LOGE("Open the version file error:%d", errno); + return -E_SYSTEM_API_FAIL; + } + int errCode = E_OK; + VersionFileBlock block; + size_t readSize = fread(static_cast(&block), 1, sizeof(VersionFileBlock), versionFile); + if (readSize != sizeof(VersionFileBlock)) { + LOGE("read the file error:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + goto END; + }; + errCode = CheckFileBlock(block); + if (errCode != E_OK) { + LOGE("Check the file block error"); + goto END; + } + TransferNetFileBlockToHost(block); + version = block.version; + tag.assign(block.tag, block.tag + sizeof(block.tag)); + END: + fclose(versionFile); + versionFile = nullptr; + return errCode; + } +} + +MultiVerVacuum MultiVerNaturalStore::shadowTrimmer_; +MultiVerNaturalStore::MultiVerNaturalStore() + : multiVerData_(nullptr), + commitHistory_(nullptr), + multiVerKvStorage_(nullptr), + multiVerEngine_(nullptr), + trimmerImpl_(nullptr), + maxRecordTimestamp_(0), + maxCommitVersion_(0) +{} + +MultiVerNaturalStore::~MultiVerNaturalStore() +{ + Clear(); + UnRegisterNotificationEventType(NATURAL_STORE_COMMIT_EVENT); +} + +void MultiVerNaturalStore::Clear() +{ + if (trimmerImpl_ != nullptr) { + shadowTrimmer_.Abort(GetStringIdentifier()); + delete trimmerImpl_; + trimmerImpl_ = nullptr; + } + { + std::lock_guard lock(commitHistMutex_); + if (commitHistory_ != nullptr) { + commitHistory_->Close(); + delete commitHistory_; + commitHistory_ = nullptr; + } + } + { + std::lock_guard lock(multiDataMutex_); + if (multiVerData_ != nullptr) { + multiVerData_->Close(); + delete multiVerData_; + multiVerData_ = nullptr; + } + } + + { + std::lock_guard lock(syncerKvMutex_); + if (multiVerKvStorage_ != nullptr) { + multiVerKvStorage_->Close(); + delete multiVerKvStorage_; + multiVerKvStorage_ = nullptr; + } + } + multiVerEngine_ = nullptr; +} + +int MultiVerNaturalStore::InitStorages(const KvDBProperties &kvDBProp, bool isChangeTag) +{ + std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifierDir = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + bool isNeedCreate = kvDBProp.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + CipherType cipherType; + CipherPassword passwd; + kvDBProp.GetPassword(cipherType, passwd); + + IKvDBMultiVerDataStorage::Property multiVerProp = {dataDir, identifierDir, isNeedCreate, cipherType, passwd}; + IKvDBCommitStorage::Property commitProp = {dataDir, identifierDir, isNeedCreate, cipherType, passwd}; + MultiVerKvDataStorage::Property multiVerKvProp = {dataDir, identifierDir, isNeedCreate, cipherType, passwd}; + + int errCode = DBCommon::CreateStoreDirectory(dataDir, identifierDir, DBConstant::MULTI_SUB_DIR, isNeedCreate); + if (errCode != E_OK) { + return errCode; + } + + errCode = CheckVersion(kvDBProp); + if (errCode != E_OK) { + LOGE("Upgrade multi ver failed:%d", errCode); + return errCode; + } + + errCode = multiVerData_->Open(multiVerProp); + if (errCode != E_OK) { + LOGE("MultiVer::InitStorages open multiVerData fail! errCode[%d]", errCode); + return errCode; + } + + errCode = commitHistory_->Open(commitProp); + if (errCode != E_OK) { + LOGE("MultiVer::InitStorages open commitHistory fail! errCode[%d]", errCode); + return errCode; + } + + errCode = multiVerKvStorage_->Open(multiVerKvProp); + if (errCode != E_OK) { + LOGE("Open multi ver kv storage failed:%d", errCode); + return errCode; + } + + errCode = RecoverFromException(); + if (errCode != E_OK) { + LOGE("Recover multi version storage failed:%d", errCode); + return errCode; + } + return InitStorageContext(isChangeTag); +} + +int MultiVerNaturalStore::CheckSubStorageVersion(const KvDBProperties &kvDBProp, bool &isSubStorageAllExist) const +{ + std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifierDir = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + bool isNeedCreate = kvDBProp.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + CipherType cipherType; + CipherPassword passwd; + kvDBProp.GetPassword(cipherType, passwd); + + IKvDBMultiVerDataStorage::Property multiVerProp = {dataDir, identifierDir, isNeedCreate, cipherType, passwd}; + IKvDBCommitStorage::Property commitProp = {dataDir, identifierDir, isNeedCreate, cipherType, passwd}; + MultiVerKvDataStorage::Property multiVerKvProp = {dataDir, identifierDir, true, cipherType, passwd}; + + bool isDataStorageExist = false; + bool isCommitStorageExist = false; + bool isKvStorageAllExist = false; + int errCode = multiVerData_->CheckVersion(multiVerProp, isDataStorageExist); + if (errCode != E_OK) { + return errCode; + } + errCode = commitHistory_->CheckVersion(commitProp, isCommitStorageExist); + if (errCode != E_OK) { + return errCode; + } + errCode = multiVerKvStorage_->CheckVersion(multiVerKvProp, isKvStorageAllExist); + if (errCode != E_OK) { + return errCode; + } + if ((isDataStorageExist != isCommitStorageExist) || (isCommitStorageExist != isKvStorageAllExist)) { + // In case failure happens during open progress, some dbFile will not exist, we should recover from this + LOGW("[MultiVerStore][CheckSubVer] Detect File Lost, isDataExist=%d, isCommitExist=%d, isKvAllExist=%d.", + isDataStorageExist, isCommitStorageExist, isKvStorageAllExist); + } + isSubStorageAllExist = isDataStorageExist && isCommitStorageExist && isKvStorageAllExist; + return E_OK; +} + +int MultiVerNaturalStore::CreateStorages() +{ + int errCode = E_OK; + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + if (factory == nullptr) { + return -E_INVALID_DB; + } + multiVerData_ = factory->CreateMultiVerStorage(errCode); + if (multiVerData_ == nullptr) { + return errCode; + } + + commitHistory_ = factory->CreateMultiVerCommitStorage(errCode); + if (commitHistory_ == nullptr) { + return errCode; + } + + multiVerKvStorage_ = new (std::nothrow) MultiVerKvDataStorage; + if (multiVerKvStorage_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + return E_OK; +} + +int MultiVerNaturalStore::ClearTempFile(const KvDBProperties &kvDBProp) +{ + std::unique_ptr operation = std::make_unique(this, multiVerData_, + commitHistory_, multiVerKvStorage_); + (void)operation->ClearExportedTempFiles(kvDBProp); + int errCode = operation->RekeyRecover(kvDBProp); + if (errCode != E_OK) { + LOGE("Recover for open db failed in multi version:%d", errCode); + return errCode; + } + + errCode = operation->ClearImportTempFile(kvDBProp); + if (errCode != E_OK) { + LOGE("Recover import temp file for open db failed in multi version:%d", errCode); + } + return errCode; +} + +// Open the database +int MultiVerNaturalStore::Open(const KvDBProperties &kvDBProp) +{ + StorageEngineAttr poolSize = {0, 1, 0, 16}; // 1 write 16 read at most. + int errCode = CreateStorages(); + if (errCode != E_OK) { + goto ERROR; + } + + MyProp() = kvDBProp; + errCode = ClearTempFile(kvDBProp); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = InitStorages(kvDBProp); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = RegisterNotificationEventType(NATURAL_STORE_COMMIT_EVENT); + if (errCode != E_OK) { + LOGE("RegisterEventType failed!"); + goto ERROR; + } + + multiVerEngine_ = std::make_unique(); + errCode = multiVerEngine_->InitDatabases(this, multiVerData_, commitHistory_, multiVerKvStorage_, poolSize); + if (errCode != E_OK) { + goto ERROR; + } + // Start the trimming; + trimmerImpl_ = new (std::nothrow) MultiVerVacuumExecutorImpl(this); + if (trimmerImpl_ == nullptr) { + errCode = -E_OUT_OF_MEMORY; + goto ERROR; + } + + shadowTrimmer_.Launch(GetStringIdentifier(), trimmerImpl_); + StartSyncer(); + return E_OK; +ERROR: + Clear(); + return errCode; +} + +void MultiVerNaturalStore::Close() +{ + // Abort the trimming; + SyncAbleKvDB::Close(); + Clear(); +} + +GenericKvDBConnection *MultiVerNaturalStore::NewConnection(int &errCode) +{ + auto connection = new (std::nothrow) MultiVerNaturalStoreConnection(this); + if (connection == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + + errCode = E_OK; + return connection; +} + +// Get interface for syncer. +IKvDBSyncInterface *MultiVerNaturalStore::GetSyncInterface() +{ + return this; +} + +// Get interface type of this kvdb. +int MultiVerNaturalStore::GetInterfaceType() const +{ + return SYNC_MVD; +} + +// Get the interface ref-count, in order to access asynchronously. +void MultiVerNaturalStore::IncRefCount() +{ + IncObjRef(this); +} + +// Drop the interface ref-count. +void MultiVerNaturalStore::DecRefCount() +{ + DecObjRef(this); +} + +// Get the identifier of this kvdb. +std::vector MultiVerNaturalStore::GetIdentifier() const +{ + std::string identifier = MyProp().GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + std::vector identifierVect(identifier.begin(), identifier.end()); + return identifierVect; +} + +std::string MultiVerNaturalStore::GetStringIdentifier() const +{ + std::string identifier = MyProp().GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + std::vector idVect(identifier.begin(), identifier.end()); + return VEC_TO_STR(idVect); +} + +// Get the max timestamp of all entries in database. +void MultiVerNaturalStore::GetMaxTimestamp(Timestamp &stamp) const +{ + std::lock_guard lock(maxTimeMutex_); + stamp = maxRecordTimestamp_; +} + +void MultiVerNaturalStore::SetMaxTimestamp(Timestamp stamp) +{ + std::lock_guard lock(maxTimeMutex_); + maxRecordTimestamp_ = (stamp > maxRecordTimestamp_) ? stamp : maxRecordTimestamp_; +} + +// Get meta data associated with the given key. +int MultiVerNaturalStore::GetMetaData(const Key &key, Value &value) const +{ + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->GetMetaData(key, value); + ReleaseHandle(handle); + return errCode; +} + +// Put meta data as a key-value entry. +int MultiVerNaturalStore::PutMetaData(const Key &key, const Value &value) +{ + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->PutMetaData(key, value); + ReleaseHandle(handle); + return errCode; +} + +int MultiVerNaturalStore::DeleteMetaData(const std::vector &keys) +{ + return -E_NOT_SUPPORT; +} + +// Get all meta data keys. +int MultiVerNaturalStore::GetAllMetaKeys(std::vector &keys) const +{ + return E_OK; +} + +bool MultiVerNaturalStore::IsCommitExisted(const MultiVerCommitNode &commit) const +{ + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return false; + } + + bool result = handle->IsCommitExisted(commit, errCode); + ReleaseHandle(handle); + return result; +} + +int MultiVerNaturalStore::GetDeviceLatestCommit(std::map &commitMap) const +{ + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return -E_BUSY; + } + + errCode = handle->GetDeviceLatestCommit(commitMap); + ReleaseHandle(handle); + return errCode; +} + +int MultiVerNaturalStore::GetCommitTree(const std::map &commitMap, + std::vector &commits) const +{ + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return -E_BUSY; + } + + errCode = handle->GetCommitTree(commitMap, commits); + ReleaseHandle(handle); + return errCode; +} + +int MultiVerNaturalStore::GetCommitData(const MultiVerCommitNode &commit, std::vector &entries) const +{ + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return -E_BUSY; + } + + errCode = handle->GetCommitData(commit, entries); + ReleaseHandle(handle); + return errCode; +} + +MultiVerKvEntry *MultiVerNaturalStore::CreateKvEntry(const std::vector &data) +{ + auto kvEntry = new (std::nothrow) GenericMultiVerKvEntry; + if (kvEntry == nullptr) { + return nullptr; + } + + int errCode = kvEntry->DeSerialData(data); + if (errCode != E_OK) { + LOGE("deserialize data into kv entry failed:%d", errCode); + delete kvEntry; + kvEntry = nullptr; + } + return kvEntry; +} + +void MultiVerNaturalStore::ReleaseKvEntry(const MultiVerKvEntry *entry) +{ + if (entry != nullptr) { + delete entry; + entry = nullptr; + } +} + +bool MultiVerNaturalStore::IsValueSliceExisted(const ValueSliceHash &value) const +{ + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return false; + } + + bool result = handle->IsValueSliceExisted(value, errCode); + ReleaseHandle(handle); + return result; +} + +int MultiVerNaturalStore::GetValueSlice(const ValueSliceHash &hashValue, ValueSlice &sliceValue) const +{ + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return -E_BUSY; + } + + errCode = handle->GetValueSlice(hashValue, sliceValue); + ReleaseHandle(handle); + return errCode; +} + +int MultiVerNaturalStore::PutValueSlice(const ValueSliceHash &hashValue, const ValueSlice &sliceValue) const +{ + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return -E_BUSY; + } + + errCode = handle->PutValueSlice(hashValue, sliceValue, false); + ReleaseHandle(handle); + return errCode; +} + +int MultiVerNaturalStore::PutCommitData(const MultiVerCommitNode &commit, const std::vector &entries, + const std::string &deviceName) +{ + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return -E_BUSY; + } + + errCode = handle->PutCommitData(commit, entries, deviceName); + ReleaseHandle(handle); + return errCode; +} + +int MultiVerNaturalStore::MergeSyncCommit(const MultiVerCommitNode &commit, + const std::vector &commits) +{ + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return -E_BUSY; + } + + errCode = handle->MergeSyncCommit(commit, commits); + ReleaseHandle(handle); + return errCode; +} + +void MultiVerNaturalStore::NotifyStartSyncOperation() +{ + shadowTrimmer_.Pause(GetStringIdentifier()); +} + +void MultiVerNaturalStore::NotifyFinishSyncOperation() +{ + shadowTrimmer_.Continue(GetStringIdentifier(), true); +} + +int MultiVerNaturalStore::TransferSyncCommitDevInfo(MultiVerCommitNode &commit, const std::string &devId, + bool isSyncedIn) const +{ + std::string hashDevId = DBCommon::TransferHashString(devId); + if (isSyncedIn) { + // The size of the device info must be hash_size + tag_size; + if (commit.deviceInfo.size() == hashDevId.size() + MULTI_VER_TAG_SIZE) { + // If the hash device info is matched with the local, just remove the hash device info. + if (commit.deviceInfo.compare(0, hashDevId.size(), hashDevId) == 0) { + commit.deviceInfo = commit.deviceInfo.substr(hashDevId.size(), MULTI_VER_TAG_SIZE); + } + return E_OK; + } + LOGE("Unexpected dev info for sync in:%zu", commit.deviceInfo.size()); + return -E_UNEXPECTED_DATA; + } else { + // If the device info only contains the tag info, it must be local node. + if (commit.deviceInfo.size() == MULTI_VER_TAG_SIZE) { + commit.deviceInfo.insert(0, hashDevId); + } else if (commit.deviceInfo.size() != hashDevId.size() + MULTI_VER_TAG_SIZE) { + LOGE("Unexpected dev info for sync out:%zu", commit.deviceInfo.size()); + return -E_UNEXPECTED_DATA; + } + return E_OK; + } +} + +int MultiVerNaturalStore::Rekey(const CipherPassword &passwd) +{ + if (multiVerEngine_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = multiVerEngine_->TryToDisable(false, OperatePerm::REKEY_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + StopSyncer(); + shadowTrimmer_.Pause(GetStringIdentifier()); + errCode = multiVerEngine_->TryToDisable(true, OperatePerm::REKEY_MONOPOLIZE_PERM); + if (errCode != E_OK) { + multiVerEngine_->Enable(OperatePerm::REKEY_MONOPOLIZE_PERM); + shadowTrimmer_.Continue(GetStringIdentifier(), true); + StartSyncer(); + return errCode; + } + + std::unique_ptr operation = std::make_unique(this, multiVerData_, + commitHistory_, multiVerKvStorage_); + errCode = operation->Rekey(passwd); + + multiVerEngine_->Enable(OperatePerm::REKEY_MONOPOLIZE_PERM); + shadowTrimmer_.Continue(GetStringIdentifier(), true); + StartSyncer(); + + return errCode; +} + +int MultiVerNaturalStore::Export(const std::string &filePath, const CipherPassword &passwd) +{ + if (multiVerEngine_ == nullptr) { + return -E_INVALID_DB; + } + std::string localDev; + int errCode = GetLocalIdentity(localDev); + if (errCode != E_OK) { + LOGE("Failed to GetLocalIdentity!"); + } + // Exclusively write resources + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + std::unique_ptr operation = std::make_unique(this, multiVerData_, + commitHistory_, multiVerKvStorage_); + operation->SetLocalDevId(localDev); + errCode = operation->Export(filePath, passwd); + + ReleaseHandle(handle); + + return errCode; +} + +int MultiVerNaturalStore::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (multiVerEngine_ == nullptr) { + return -E_INVALID_DB; + } + std::string localDev; + int errCode = GetLocalIdentity(localDev); + if (errCode != E_OK) { + LOGE("Failed to get the local identity!"); + localDev.resize(0); + } + errCode = multiVerEngine_->TryToDisable(false, OperatePerm::IMPORT_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + StopSyncer(); + shadowTrimmer_.Abort(GetStringIdentifier()); + std::unique_ptr operation; + errCode = multiVerEngine_->TryToDisable(true, OperatePerm::IMPORT_MONOPOLIZE_PERM); + if (errCode != E_OK) { + goto END; + } + operation = std::make_unique(this, multiVerData_, commitHistory_, multiVerKvStorage_); + operation->SetLocalDevId(localDev); + errCode = operation->Import(filePath, passwd); +END: + multiVerEngine_->Enable(OperatePerm::IMPORT_MONOPOLIZE_PERM); + shadowTrimmer_.Launch(GetStringIdentifier(), trimmerImpl_); + StartSyncer(); + return errCode; +} + +uint64_t MultiVerNaturalStore::GetCurrentTimestamp() +{ + return GetTimestamp(); +} + +int MultiVerNaturalStore::GetDiffEntries(const CommitID &begin, const CommitID &end, MultiVerDiffData &data) const +{ + // Get one connection. + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->GetDiffEntries(begin, end, data); + ReleaseHandle(handle); + return errCode; +} + +int MultiVerNaturalStore::RecoverFromException() +{ + // Get the latest local version and the head node. + if (multiVerData_ == nullptr || commitHistory_ == nullptr) { + return -E_INVALID_DB; + } + + IKvDBMultiVerTransaction *transaction = nullptr; + int errCode = multiVerData_->StartWrite(KvDataType::KV_DATA_SYNC_P2P, transaction); + if (transaction == nullptr) { + goto END; + } + errCode = transaction->StartTransaction(); + if (errCode != E_OK) { + goto END; + } + + errCode = CompareVerDataAndLog(transaction); + if (errCode != E_OK) { + LOGE("Compare the version data and log failed:%d", errCode); + transaction->RollBackTransaction(); + goto END; + } + errCode = transaction->CommitTransaction(); +END: + if (transaction != nullptr) { + multiVerData_->ReleaseTransaction(transaction); + transaction = nullptr; + } + return errCode; +} + +int MultiVerNaturalStore::CompareVerDataAndLog(IKvDBMultiVerTransaction *transaction) const +{ + // Get the latest local version, we only care the local data. + Version maxLocalVersion = 0; + int errCode = transaction->GetMaxVersion(MultiVerDataType::NATIVE_TYPE, maxLocalVersion); + if (errCode != E_OK) { + return errCode; + } + + CommitID headerId = commitHistory_->GetHeader(errCode); + if (errCode != E_OK) { + return errCode; + } + + if (headerId.empty()) { + if (maxLocalVersion != 0) { + return transaction->ClearEntriesByVersion(maxLocalVersion); + } + return E_OK; + } + + IKvDBCommit *commitHead = commitHistory_->GetCommit(headerId, errCode); + if (commitHead == nullptr) { + return errCode; + } + + // compare the version + if (commitHead->GetCommitVersion() < maxLocalVersion) { + LOGD("Delete entries"); + errCode = transaction->ClearEntriesByVersion(maxLocalVersion); + } else { + errCode = E_OK; + } + + commitHistory_->ReleaseCommit(commitHead); + commitHead = nullptr; + return errCode; +} + +Version MultiVerNaturalStore::GetMaxCommitVersion() const +{ + return maxCommitVersion_; +} + +void MultiVerNaturalStore::SetMaxCommitVersion(const Version &version) +{ + maxCommitVersion_ = (version > maxCommitVersion_) ? version : maxCommitVersion_; +} + +MultiVerStorageExecutor *MultiVerNaturalStore::GetHandle(bool isWrite, int &errCode, + bool isTrimming, OperatePerm perm) const +{ + if (multiVerEngine_ == nullptr) { + errCode = -E_INVALID_DB; + return nullptr; + } + + if (isWrite && !isTrimming) { + // stop the trimming + shadowTrimmer_.Pause(GetStringIdentifier()); + } + StorageExecutor *handle = nullptr; + if (isTrimming) { + handle = multiVerEngine_->FindExecutor(isWrite, OperatePerm::NORMAL_PERM, errCode, 0); + } else { + handle = multiVerEngine_->FindExecutor(isWrite, perm, errCode); + } + + if (handle == nullptr) { + if (isWrite && !isTrimming) { + // restart the trimming + shadowTrimmer_.Continue(GetStringIdentifier(), false); + } + } else { + if (!handle->GetWritable() && isTrimming) { + static_cast(handle)->InitCurrentReadVersion(); + } + } + return static_cast(handle); +} + +void MultiVerNaturalStore::ReleaseHandle(MultiVerStorageExecutor *&handle, bool isTrimming) const +{ + if (multiVerEngine_ == nullptr || handle == nullptr) { + return; + } + bool isCorrupted = handle->GetCorruptedStatus(); + bool isWrite = handle->GetWritable(); + StorageExecutor *databaseHandle = handle; + multiVerEngine_->Recycle(databaseHandle); + handle = nullptr; + if (isCorrupted) { + CorruptNotify(); + } + if (isWrite && !isTrimming) { + // restart the trimming. + LOGI("Release handle and continue vacuum data!"); + shadowTrimmer_.Continue(GetStringIdentifier(), true); + } +} + +int MultiVerNaturalStore::InitStorageContext(bool isChangeTag) +{ + int errCode = InitStorageContextVersion(isChangeTag); + if (errCode != E_OK) { + return errCode; + } + + maxCommitVersion_ = commitHistory_->GetMaxCommitVersion(errCode); + if (errCode != E_OK) { + LOGE("Get the max commit version failed:%d", errCode); + } + return errCode; +} + +int MultiVerNaturalStore::InitStorageContextVersion(bool isChangeTag) +{ + std::string verFilePath; + int errCode = GetVersionFilePath(MyProp(), verFilePath); + if (errCode != E_OK) { + return errCode; + } + + if (!OS::CheckPathExistence(verFilePath)) { + return CreateNewVersionFile(verFilePath, MULTI_VER_STORE_VERSION_CURRENT, branchTag_); + } + if (isChangeTag) { + return ChangeVersionFile(verFilePath, MULTI_VER_STORE_VERSION_CURRENT, branchTag_, isChangeTag); + } + uint32_t version = 0; + return GetVersionAndTag(verFilePath, version, branchTag_); +} + +void MultiVerNaturalStore::GetCurrentTag(std::vector &tag) const +{ + tag = branchTag_; +} + +void MultiVerNaturalStore::AddVersionConstraintToList(Version version) +{ + std::lock_guard lock(versionConstraintMutex_); + versionConstraints_.insert(version); +} + +void MultiVerNaturalStore::RemoveVersionConstraintFromList(Version version) +{ + std::lock_guard lock(versionConstraintMutex_); + auto iter = versionConstraints_.find(version); + if (iter != versionConstraints_.end()) { + versionConstraints_.erase(iter); + // Auto launch the vacuum. + shadowTrimmer_.AutoRelaunchOnce(GetStringIdentifier()); + } +} + +Version MultiVerNaturalStore::GetMaxTrimmableVersion() const +{ + std::lock_guard lock(versionConstraintMutex_); + if (versionConstraints_.empty()) { + return UINT64_MAX; + } + return *(versionConstraints_.begin()); +} + +int MultiVerNaturalStore::TransObserverTypeToRegisterFunctionType(int observerType, RegisterFuncType &type) const +{ + if (observerType == static_cast(NATURAL_STORE_COMMIT_EVENT)) { + type = OBSERVER_MULTI_VERSION_NS_COMMIT_EVENT; + return E_OK; + } + return -E_NOT_SUPPORT; +} + +const KvDBProperties &MultiVerNaturalStore::GetDbProperties() const +{ + return GetMyProperties(); +} + +int MultiVerNaturalStore::RemoveKvDB(const KvDBProperties &properties) +{ + std::string storeOnlyDir; + std::string storeDir; + GenericKvDB::GetStoreDirectory(properties, KvDBProperties::MULTI_VER_TYPE, storeDir, storeOnlyDir); + int errCodeVersion = KvDBUtils::RemoveKvDB(storeDir, storeOnlyDir, DBConstant::MULTI_VER_DATA_STORE); + int errCodeCommit = KvDBUtils::RemoveKvDB(storeDir, storeOnlyDir, DBConstant::MULTI_VER_COMMIT_STORE); + int errCodeValue = KvDBUtils::RemoveKvDB(storeDir, storeOnlyDir, DBConstant::MULTI_VER_VALUE_STORE); + int errCodeMeta = KvDBUtils::RemoveKvDB(storeDir, storeOnlyDir, DBConstant::MULTI_VER_META_STORE); + LOGD("Delete the versionStorage:%d, commitStorage:%d, valueStorage:%d, metaStorage:%d", + errCodeVersion, errCodeCommit, errCodeValue, errCodeMeta); + DBCommon::RemoveAllFilesOfDirectory(storeDir, true); + DBCommon::RemoveAllFilesOfDirectory(storeOnlyDir, true); + if (errCodeVersion == E_OK && errCodeCommit == E_OK) { + return E_OK; + } + if (errCodeVersion == -E_NOT_FOUND && errCodeCommit == -E_NOT_FOUND) { + return -E_NOT_FOUND; + } + if (errCodeVersion == E_OK && errCodeCommit == -E_NOT_FOUND) { + return E_OK; + } + if (errCodeVersion == -E_NOT_FOUND && errCodeCommit == E_OK) { + return E_OK; + } + return errCodeCommit; +} + +int MultiVerNaturalStore::GetKvDBSize(const KvDBProperties &properties, uint64_t &size) const +{ + std::string storeOnlyDir; + std::string storeDir; + GenericKvDB::GetStoreDirectory(properties, KvDBProperties::MULTI_VER_TYPE, storeDir, storeOnlyDir); + + std::vector storageNames = { + DBConstant::MULTI_VER_DATA_STORE, + DBConstant::MULTI_VER_COMMIT_STORE, + DBConstant::MULTI_VER_VALUE_STORE, + DBConstant::MULTI_VER_META_STORE + }; + + // there only calculate db related file size + for (const auto &storageName : storageNames) { + uint64_t dbSize = 0; + int errCode = KvDBUtils::GetKvDbSize(storeDir, storeOnlyDir, storageName, dbSize); + if (errCode == E_OK) { + size += dbSize; + continue; + } + + if (errCode == -E_NOT_FOUND) { + return -E_NOT_FOUND; + } + + size = 0; + return errCode; + } + return E_OK; +} + +KvDBProperties &MultiVerNaturalStore::GetDbPropertyForUpdate() +{ + return MyProp(); +} + +int MultiVerNaturalStore::CheckVersion(const KvDBProperties &kvDBProp) const +{ + LOGD("[MultiVerStore][CheckVer] Current Overall Version: %u.", MULTI_VER_STORE_VERSION_CURRENT); + bool isVerFileExist = false; + int errCode = CheckOverallVersionViaVersionFile(kvDBProp, isVerFileExist); + if (errCode != E_OK) { + return errCode; + } + bool isSubStorageExist = false; + errCode = CheckSubStorageVersion(kvDBProp, isSubStorageExist); + if (errCode != E_OK) { + return errCode; + } + if (isVerFileExist != isSubStorageExist) { + LOGW("[MultiVerStore][CheckVer] Detect File Lost, isVerFileExist=%d, isSubStorageExist=%d.", + isVerFileExist, isSubStorageExist); + } + return E_OK; +} + +int MultiVerNaturalStore::CheckOverallVersionViaVersionFile(const KvDBProperties &kvDBProp, bool &isVerFileExist) const +{ + std::string verFilePath; + int errCode = GetVersionFilePath(kvDBProp, verFilePath); + if (errCode != E_OK) { + return errCode; + } + // Absent of version file may because: 1: Newly created database; 2: An already created database lost version file. + // In both case, we returned E_OK here. After each sub storage be successfully open and upgrade, create verFile. + if (!OS::CheckPathExistence(verFilePath)) { + LOGD("[MultiVerStore][CheckOverVer] No Version File."); + isVerFileExist = false; + return E_OK; + } + isVerFileExist = true; + + uint32_t overallVersion = 0; + std::vector branchTagInVerFile; + errCode = GetVersionAndTag(verFilePath, overallVersion, branchTagInVerFile); + if (errCode != E_OK) { + LOGE("[MultiVerStore][CheckOverVer] GetVersionAndTag fail, errCode=%d.", errCode); + return errCode; + } + LOGD("[MultiVerStore][CheckOverVer] overallVersion=%u, tag=%s.", overallVersion, VEC_TO_STR(branchTagInVerFile)); + if (overallVersion > MULTI_VER_STORE_VERSION_CURRENT) { + LOGE("[MultiVerStore][CheckOverVer] Version Not Support!"); + return -E_VERSION_NOT_SUPPORT; + } + return E_OK; +} + +int MultiVerNaturalStore::GetVersionFilePath(const KvDBProperties &kvDBProp, std::string &outPath) const +{ + std::string verFiledir; + int errCode = GetWorkDir(kvDBProp, verFiledir); + if (errCode != E_OK) { + LOGE("[MultiVerStore][GetVerFilePath] GetWorkDir fail, errCode=%d", errCode); + return errCode; + } + outPath = verFiledir + "/" + DBConstant::MULTI_SUB_DIR + "/version"; + return E_OK; +} + +int MultiVerNaturalStore::DeleteMetaDataByPrefixKey(const Key &keyPrefix) const +{ + (void)keyPrefix; + return -E_NOT_SUPPORT; +} + +void MultiVerNaturalStore::SetDataInterceptor(const PushDataInterceptor &interceptor) +{ + (void)interceptor; + return; +} + +DEFINE_OBJECT_TAG_FACILITIES(MultiVerNaturalStore) +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store.h b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store.h new file mode 100644 index 0000000000000000000000000000000000000000..28145b1d85607651079a88ae8d674439354997e3 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_NATURAL_STORE_H +#define MULTI_VER_NATURAL_STORE_H + +#ifndef OMIT_MULTI_VER +#include "sync_able_kvdb.h" +#include "multi_ver_kvdb_sync_interface.h" +#include "kv_store_changed_data.h" +#include "ikvdb_multi_ver_data_storage.h" +#include "ikvdb_commit_storage.h" +#include "macro_utils.h" +#include "multi_ver_kvdata_storage.h" +#include "multi_ver_storage_executor.h" +#include "multi_ver_storage_engine.h" +#include "multi_ver_vacuum.h" + +namespace DistributedDB { +enum NaturalStoreNotificationEventType { + NATURAL_STORE_COMMIT_EVENT = 0 +}; +class MultiVerVacuumExecutorImpl; +class MultiVerNaturalStore final: public SyncAbleKvDB, public MultiVerKvDBSyncInterface { +public: + MultiVerNaturalStore(); + ~MultiVerNaturalStore() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(MultiVerNaturalStore); + + // Open the database + int Open(const KvDBProperties &kvDBProp) override; + + // Invoked automatically when connection count is zero + void Close() override; + + // Create a connection object. + GenericKvDBConnection *NewConnection(int &errCode) override; + + // Get interface for syncer. + IKvDBSyncInterface *GetSyncInterface() override; + + // Get interface type of this kvdb. + int GetInterfaceType() const override; + + // Get the interface ref-count, in order to access asynchronously. + void IncRefCount() override; + + // Drop the interface ref-count. + void DecRefCount() override; + + // Get the identifier of this kvdb. + std::vector GetIdentifier() const override; + + // Get the max timestamp of all entries in database. + void GetMaxTimestamp(Timestamp &stamp) const override; + + // Get meta data associated with the given key. + int GetMetaData(const Key &key, Value &value) const override; + + // Put meta data as a key-value entry. + int PutMetaData(const Key &key, const Value &value) override; + + // Delete multiple meta data records in a transaction. + int DeleteMetaData(const std::vector &keys) override; + // Delete multiple meta data records with key prefix in a transaction. + int DeleteMetaDataByPrefixKey(const Key &keyPrefix) const override; + + // Get all meta data keys. + int GetAllMetaKeys(std::vector &keys) const override; + + bool IsCommitExisted(const MultiVerCommitNode &commit) const override; + + int GetDeviceLatestCommit(std::map &) const override; + + int GetCommitTree(const std::map &, + std::vector &) const override; + + int GetCommitData(const MultiVerCommitNode &commit, std::vector &entries) const override; + + MultiVerKvEntry *CreateKvEntry(const std::vector &data) override; + + void ReleaseKvEntry(const MultiVerKvEntry *entry) override; + + bool IsValueSliceExisted(const ValueSliceHash &value) const override; + + int GetValueSlice(const ValueSliceHash &hashValue, ValueSlice &sliceValue) const override; + + int PutValueSlice(const ValueSliceHash &hashValue, const ValueSlice &sliceValue) const override; + + int PutCommitData(const MultiVerCommitNode &commit, const std::vector &entries, + const std::string &deviceName) override; + + int MergeSyncCommit(const MultiVerCommitNode &commit, const std::vector &commits) override; + + void NotifyStartSyncOperation() override; + + void NotifyFinishSyncOperation() override; + + int TransferSyncCommitDevInfo(MultiVerCommitNode &commit, const std::string &devId, bool isSyncedIn) const override; + + int Rekey(const CipherPassword &passwd) override; + + int Export(const std::string &filePath, const CipherPassword &passwd) override; + + int Import(const std::string &filePath, const CipherPassword &passwd) override; + + int GetDiffEntries(const CommitID &begin, const CommitID &end, MultiVerDiffData &data) const; + + uint64_t GetCurrentTimestamp(); + + // Set the max timestamp + void SetMaxTimestamp(Timestamp stamp); + + Version GetMaxCommitVersion() const; + + void SetMaxCommitVersion(const Version &version); + + MultiVerStorageExecutor *GetHandle(bool isWrite, int &errCode, + bool isTrimming = false, OperatePerm perm = OperatePerm::NORMAL_PERM) const; + + void ReleaseHandle(MultiVerStorageExecutor *&handle, bool isTrimming = false) const; + + void GetCurrentTag(std::vector &tag) const; + + // Just provide the version constraint for trimmming data(include observer and the snapshot) + void AddVersionConstraintToList(Version version); + + void RemoveVersionConstraintFromList(Version version); + + // Get the max trimmable version, if no need trimming, return 0; if need trimming all return the MAX_UINT64. + Version GetMaxTrimmableVersion() const; + + int TransObserverTypeToRegisterFunctionType(int observerType, RegisterFuncType &type) const override; + + const KvDBProperties &GetDbProperties() const override; + + int RemoveKvDB(const KvDBProperties &properties) override; + + int GetKvDBSize(const KvDBProperties &properties, uint64_t &size) const override; + + KvDBProperties &GetDbPropertyForUpdate(); + + int InitStorages(const KvDBProperties &kvDBProp, bool isChangeTag = false); + + void SetDataInterceptor(const PushDataInterceptor &interceptor) override; + +private: + + int CheckSubStorageVersion(const KvDBProperties &kvDBProp, bool &isSubStorageAllExist) const; + + int CreateStorages(); + + int CreateStoreDirectory(const std::string &directory, const std::string &identifierName); + + void Clear(); + + int RecoverFromException(); + + int CompareVerDataAndLog(IKvDBMultiVerTransaction *transaction) const; + + int ClearTempFile(const KvDBProperties &kvDBProp); + + int InitStorageContext(bool isChangeTag); + + int InitStorageContextVersion(bool isChangeTag); + + std::string GetStringIdentifier() const; + + int CheckVersion(const KvDBProperties &kvDBProp) const; + + int CheckOverallVersionViaVersionFile(const KvDBProperties &kvDBProp, bool &isVerFileExist) const; + + int GetVersionFilePath(const KvDBProperties &kvDBProp, std::string &outPath) const; + + DECLARE_OBJECT_TAG(MultiVerNaturalStore); + + static MultiVerVacuum shadowTrimmer_; + IKvDBMultiVerDataStorage *multiVerData_; + IKvDBCommitStorage *commitHistory_; + MultiVerKvDataStorage *multiVerKvStorage_; + std::unique_ptr multiVerEngine_; + MultiVerVacuumExecutorImpl *trimmerImpl_; + mutable std::mutex commitHistMutex_; + mutable std::mutex multiDataMutex_; + mutable std::mutex syncerKvMutex_; + mutable std::mutex maxTimeMutex_; + mutable std::mutex versionConstraintMutex_; + mutable uint64_t maxRecordTimestamp_; + Version maxCommitVersion_; + std::vector branchTag_; + std::multiset versionConstraints_; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_NATURAL_STORE_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ece9cb43877abf7f70b520e4ee029466d20380d --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_natural_store_commit_notify_data.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +MultiVerNaturalStoreCommitNotifyData::MultiVerNaturalStoreCommitNotifyData(MultiVerNaturalStore *db, + const CommitID &startCommitID, const CommitID &endCommitID, Version curVersion) + : db_(db), + startCommitID_(startCommitID), + endCommitID_(endCommitID), + isFilled_(false), + version_(curVersion) +{} + +MultiVerNaturalStoreCommitNotifyData::~MultiVerNaturalStoreCommitNotifyData() +{ + if (db_ != nullptr) { + db_->RemoveVersionConstraintFromList(version_); + } + + db_ = nullptr; +} + +const std::list MultiVerNaturalStoreCommitNotifyData::GetInsertedEntries(int &errCode) const +{ + errCode = FillInnerData(); + if (errCode != E_OK) { + LOGE("Failed to fill inner data in GetInsertedEntries(), err:%d", errCode); + } + return diffData_.inserted; +} + +const std::list MultiVerNaturalStoreCommitNotifyData::GetUpdatedEntries(int &errCode) const +{ + errCode = FillInnerData(); + if (errCode != E_OK) { + LOGE("Failed to fill inner data in GetUpdatedEntries(), err:%d", errCode); + } + return diffData_.updated; +} + +const std::list MultiVerNaturalStoreCommitNotifyData::GetDeletedEntries(int &errCode) const +{ + errCode = FillInnerData(); + if (errCode != E_OK) { + LOGE("Failed to fill inner data in GetDeletedEntries(), err:%d", errCode); + } + return diffData_.deleted; +} + +bool MultiVerNaturalStoreCommitNotifyData::IsCleared() const +{ + int errCode = FillInnerData(); + if (errCode != E_OK) { + LOGE("Failed to fill inner data in IsCleared(), err:%d", errCode); + } + return diffData_.isCleared; +} + +bool MultiVerNaturalStoreCommitNotifyData::IsChangedDataEmpty() const +{ + int errCode = FillInnerData(); + if (errCode != E_OK) { + LOGE("Failed to fill inner data in IsEmpty(), err:%d", errCode); + } + return !diffData_.isCleared && + diffData_.inserted.empty() && + diffData_.updated.empty() && + diffData_.deleted.empty(); +} + +int MultiVerNaturalStoreCommitNotifyData::FillInnerData() const +{ + std::lock_guard lock(fillMutex_); + if (isFilled_) { + return E_OK; + } + if (db_ == nullptr) { + LOGE("Failed to fill inner data, db is nullptr"); + return -E_INVALID_DB; + } + + int errCode = db_->GetDiffEntries(startCommitID_, endCommitID_, diffData_); + if (errCode != E_OK) { + LOGE("Failed to get diff entries when filling inner data, err:%d", errCode); + return errCode; + } + isFilled_ = true; + return E_OK; +} + +DEFINE_OBJECT_TAG_FACILITIES(MultiVerNaturalStoreCommitNotifyData) +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.h b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.h new file mode 100644 index 0000000000000000000000000000000000000000..90d6ee80829f008028a1deed46728ef95f42b1d4 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_NATURAL_STORE_COMMIT_NOTIFY_DATA_H +#define MULTI_VER_NATURAL_STORE_COMMIT_NOTIFY_DATA_H + +#ifndef OMIT_MULTI_VER +#include + +#include "kvdb_commit_notify_filterable_data.h" +#include "multi_ver_natural_store.h" + +namespace DistributedDB { +class MultiVerNaturalStoreCommitNotifyData final : public KvDBCommitNotifyFilterAbleData { +public: + MultiVerNaturalStoreCommitNotifyData(MultiVerNaturalStore *db, const CommitID &startCommitID, + const CommitID &endCommitID, Version curVersion); + ~MultiVerNaturalStoreCommitNotifyData(); + DISABLE_COPY_ASSIGN_MOVE(MultiVerNaturalStoreCommitNotifyData); + + const std::list GetInsertedEntries(int &errCode) const override; + + const std::list GetUpdatedEntries(int &errCode) const override; + + const std::list GetDeletedEntries(int &errCode) const override; + + bool IsCleared() const override; + + bool IsChangedDataEmpty() const override; + +private: + int FillInnerData() const; + + DECLARE_OBJECT_TAG(MultiVerNaturalStoreCommitNotifyData); + + mutable MultiVerNaturalStore *db_; + CommitID startCommitID_; + CommitID endCommitID_; + mutable MultiVerDiffData diffData_; + mutable bool isFilled_; + mutable std::mutex fillMutex_; + Version version_; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_NATURAL_STORE_COMMIT_NOTIFY_DATA_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.cpp new file mode 100644 index 0000000000000000000000000000000000000000..02133d50847d9ed6feaafc1a31daf1fd98a512ae --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.cpp @@ -0,0 +1,914 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_natural_store_commit_storage.h" + +#include + +#include "db_errno.h" +#include "db_constant.h" +#include "log_print.h" +#include "multi_ver_commit.h" +#include "ikvdb_factory.h" +#include "parcel.h" +#include "db_common.h" +#include "sqlite_local_kvdb.h" +#include "kvdb_utils.h" + +namespace DistributedDB { +using std::string; +using std::vector; +using std::list; +using std::map; +using std::make_pair; +using std::stack; + +namespace { + const size_t MAX_COMMIT_ST_LENGTH = 4096; + const Version VERSION_MAX = 0xFFFFFFFFFFFFFFFF; + const string MULTI_VER_COMMIT_DB_NAME = "commit_logs.db"; +} + +const string MultiVerNaturalStoreCommitStorage::HEADER_KEY = "header commit"; + +MultiVerNaturalStoreCommitStorage::MultiVerNaturalStoreCommitStorage() + : commitStorageDatabase_(nullptr), + commitStorageDBConnection_(nullptr) +{} + +MultiVerNaturalStoreCommitStorage::~MultiVerNaturalStoreCommitStorage() +{ + Close(); +} + +int MultiVerNaturalStoreCommitStorage::CheckVersion(const Property &property, bool &isDbExist) const +{ + int dbVer = 0; + int errCode = GetVersion(property, dbVer, isDbExist); + if (errCode != E_OK) { + LOGE("[CommitStorage][CheckVer] GetVersion failed, errCode=%d.", errCode); + return errCode; + } + if (!isDbExist) { + return E_OK; + } + LOGD("[CommitStorage][CheckVer] DbVersion=%d, CurVersion=%d.", dbVer, MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT); + if (dbVer > MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT) { + LOGE("[CommitStorage][CheckVer] Version Not Support!"); + return -E_VERSION_NOT_SUPPORT; + } + return E_OK; +} + +int MultiVerNaturalStoreCommitStorage::GetVersion(const IKvDBCommitStorage::Property &property, + int &version, bool &isDbExisted) +{ + SQLiteLocalKvDB *localKvdb = new (std::nothrow) SQLiteLocalKvDB(); + if (localKvdb == nullptr) { + return -E_INVALID_DB; + } + + KvDBProperties dbProperties; + dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, property.isNeedCreate); + dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path); + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE); + dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName); + dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + dbProperties.SetPassword(property.cipherType, property.passwd); + + int errCode = localKvdb->GetVersion(dbProperties, version, isDbExisted); + RefObject::DecObjRef(localKvdb); + localKvdb = nullptr; + return errCode; +} + +int MultiVerNaturalStoreCommitStorage::Open(const IKvDBCommitStorage::Property &property) +{ + if (commitStorageDatabase_ != nullptr && commitStorageDBConnection_ != nullptr) { + return E_OK; + } + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + if (factory == nullptr) { + LOGE("Failed to open IKvDB! Get factory failed."); + return -E_INVALID_DB; + } + int errCode = E_OK; + commitStorageDatabase_ = factory->CreateCommitStorageDB(errCode); + if (commitStorageDatabase_ == nullptr) { + LOGE("Failed to create commit storage database:%d", errCode); + return -E_INVALID_DB; + } + + KvDBProperties dbProperties; + dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, property.isNeedCreate); + dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path); + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE); + dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName); + dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + dbProperties.SetPassword(property.cipherType, property.passwd); + + errCode = commitStorageDatabase_->Open(dbProperties); + if (errCode != E_OK) { + LOGE("Failed to open commit storage database! err:%d", errCode); + RefObject::KillAndDecObjRef(commitStorageDatabase_); + commitStorageDatabase_ = nullptr; + return errCode; + } + commitStorageDBConnection_ = commitStorageDatabase_->GetDBConnection(errCode); + if (commitStorageDBConnection_ == nullptr) { + LOGE("Failed to get connection for commit storage! err:%d", errCode); + RefObject::KillAndDecObjRef(commitStorageDatabase_); + commitStorageDatabase_ = nullptr; + return errCode; + } + // Need to refactor in the future + errCode = static_cast(commitStorageDatabase_)->SetVersion(dbProperties, + MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT); + if (errCode != E_OK) { + LOGE("[CommitStorage][Open] SetVersion fail, errCode=%d.", errCode); + Close(); + return errCode; + } + return E_OK; +} + +void MultiVerNaturalStoreCommitStorage::Close() +{ + if (commitStorageDatabase_ != nullptr && commitStorageDBConnection_ != nullptr) { + commitStorageDBConnection_->Close(); + commitStorageDBConnection_ = nullptr; + } + if (commitStorageDatabase_ != nullptr) { + IKvDB::DecObjRef(commitStorageDatabase_); + commitStorageDatabase_ = nullptr; + } +} + +int MultiVerNaturalStoreCommitStorage::Remove(const IKvDBCommitStorage::Property &property) +{ + if (commitStorageDatabase_ != nullptr && commitStorageDBConnection_ != nullptr) { + commitStorageDBConnection_->Close(); + commitStorageDBConnection_ = nullptr; + RefObject::DecObjRef(commitStorageDatabase_); + commitStorageDatabase_ = nullptr; + } + + std::string dataDir = property.path + ("/" + property.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/"); + int errCode = KvDBUtils::RemoveKvDB(dataDir, DBConstant::MULTI_VER_COMMIT_STORE); + if (errCode != E_OK) { + LOGE("Failed to remove commit storage database! err:%d", errCode); + return errCode; + } + return E_OK; +} + +IKvDBCommit *MultiVerNaturalStoreCommitStorage::AllocCommit(int &errCode) const +{ + auto commit = new (std::nothrow) MultiVerCommit(); + if (commit != nullptr) { + errCode = E_OK; + } else { + errCode = -E_OUT_OF_MEMORY; + LOGE("Failed to alloc commit! Bad alloc."); + } + return commit; +} + +IKvDBCommit *MultiVerNaturalStoreCommitStorage::GetCommit(const CommitID &commitId, int &errCode) const +{ + if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) { + LOGE("Failed to get commit! Commit storage do not open."); + errCode = -E_INVALID_DB; + return nullptr; + } + Key key; + TransferCommitIDToKey(commitId, key); + IOption option; + Value value; + errCode = commitStorageDBConnection_->Get(option, key, value); + if (errCode != E_OK) { + if (errCode != -E_NOT_FOUND) { + LOGE("Failed to get the commit:%d", errCode); + } + return nullptr; + } + + IKvDBCommit *commit = AllocCommit(errCode); + if (commit == nullptr) { + return nullptr; + } + + errCode = TransferValueToCommit(value, *commit); + if (errCode != E_OK) { + delete commit; + commit = nullptr; + } + return commit; +} + +int MultiVerNaturalStoreCommitStorage::StartVacuum() +{ + if (commitStorageDBConnection_ == nullptr) { + LOGE("commitStorage Connection not existed!"); + return -E_INVALID_CONNECTION; + } + return commitStorageDBConnection_->StartTransaction(); +} + +int MultiVerNaturalStoreCommitStorage::CancelVacuum() +{ + if (commitStorageDBConnection_ == nullptr) { + LOGE("commitStorage Connection not existed!"); + return -E_INVALID_CONNECTION; + } + return commitStorageDBConnection_->RollBack(); +} + +int MultiVerNaturalStoreCommitStorage::FinishlVacuum() +{ + if (commitStorageDBConnection_ == nullptr) { + LOGE("commitStorage Connection not existed!"); + return -E_INVALID_CONNECTION; + } + return commitStorageDBConnection_->Commit(); +} + +int MultiVerNaturalStoreCommitStorage::GetAllCommitsInTree(std::list &commits) const +{ + std::map commitsTable; + CommitID headerId; + int errCode = GetAllCommits(commitsTable, headerId); + if (errCode != E_OK || commitsTable.empty()) { // error or no commit. + return errCode; + } + + std::stack commitStack; + commitStack.push(headerId); + while (!commitStack.empty()) { + auto currentCommitIter = commitsTable.find(commitStack.top()); + if (currentCommitIter == commitsTable.end()) { + // not found the node in the commit tree. + commits.clear(); + errCode = -E_UNEXPECTED_DATA; + break; + } + + commitStack.pop(); + if (currentCommitIter->second == nullptr) { + // if the node has been released or traveled. + continue; + } + + AddParentsToStack(currentCommitIter->second, commitsTable, commitStack); + MultiVerCommitNode commitNode; + // Get the current commit info + commitNode.commitId = currentCommitIter->first; + commitNode.deviceInfo = currentCommitIter->second->GetDeviceInfo(); + commitNode.isLocal = (currentCommitIter->second->GetLocalFlag() ? + MultiVerCommitNode::LOCAL_FLAG : MultiVerCommitNode::NON_LOCAL_FLAG); + commitNode.leftParent = currentCommitIter->second->GetLeftParentId(); + commitNode.rightParent = currentCommitIter->second->GetRightParentId(); + commitNode.timestamp = currentCommitIter->second->GetTimestamp(); + commitNode.version = currentCommitIter->second->GetCommitVersion(); + commits.push_back(commitNode); + + ReleaseCommit(currentCommitIter->second); + currentCommitIter->second = nullptr; // has been traveled, set to nullptr. + } + + commits.sort([] (const MultiVerCommitNode &thisNode, const MultiVerCommitNode &thatNode) { + return (thisNode.version > thatNode.version); + }); + ReleaseUnusedCommits(commitsTable); + + return errCode; +} + +int MultiVerNaturalStoreCommitStorage::AddCommit(const IKvDBCommit &commitEntry, bool isHeader) +{ + int errCode = CheckAddedCommit(commitEntry); + if (errCode != E_OK) { + return errCode; + } + + Key key; + TransferCommitIDToKey(commitEntry.GetCommitId(), key); + Value value; + errCode = TransferCommitToValue(commitEntry, value); + if (errCode != E_OK) { + return errCode; + } + IOption option; + errCode = commitStorageDBConnection_->StartTransaction(); + if (errCode != E_OK) { + return errCode; + } + + errCode = commitStorageDBConnection_->Put(option, key, value); + if (errCode != E_OK) { + goto END; + } + + if (isHeader) { + errCode = SetHeaderInner(commitEntry.GetCommitId()); + } +END: + if (errCode != E_OK) { + commitStorageDBConnection_->RollBack(); + } else { + errCode = commitStorageDBConnection_->Commit(); + } + + return errCode; +} + +int MultiVerNaturalStoreCommitStorage::RemoveCommit(const CommitID &commitId) +{ + if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) { + LOGE("Failed to get commit! Commit storage do not open."); + return -E_INVALID_DB; + } + int errCode = commitStorageDBConnection_->StartTransaction(); + if (errCode != E_OK) { + LOGE("Failed to remove commit when start transaction! err:%d", errCode); + return errCode; + } + Key key; + IOption option; + CommitID header = GetHeader(errCode); + if (header == commitId) { + IKvDBCommit *commit = GetCommit(commitId, errCode); + if (commit == nullptr) { + LOGE("Failed to remove commit when get header commit! err:%d", errCode); + goto ERROR; + } + errCode = SetHeader(commit->GetLeftParentId()); + ReleaseCommit(commit); + commit = nullptr; + if (errCode != E_OK) { + LOGE("Failed to remove commit when set header commit! err:%d", errCode); + goto ERROR; + } + } else { + LOGE("Failed to remove commit! The commit is not the header."); + errCode = -E_UNEXPECTED_DATA; + goto ERROR; + } + TransferCommitIDToKey(commitId, key); + errCode = commitStorageDBConnection_->Delete(option, key); + if (errCode != E_OK) { + LOGI("Failed to remove commit when remove commit! err:%d", errCode); + goto ERROR; + } + errCode = commitStorageDBConnection_->Commit(); + if (errCode != E_OK) { + LOGE("Failed to remove commit when commit! err:%d", errCode); + goto ERROR; + } + return E_OK; +ERROR: + (void)commitStorageDBConnection_->RollBack(); + return errCode; +} + +void MultiVerNaturalStoreCommitStorage::ReleaseCommit(const IKvDBCommit *commit) const +{ + if (commit != nullptr) { + delete commit; + commit = nullptr; + } +} + +int MultiVerNaturalStoreCommitStorage::SetHeader(const CommitID &commitId) +{ + if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) { + LOGE("Failed to get commit! Commit storage do not open."); + return -E_INVALID_DB; + } + + if (commitId.size() != 0) { + int errCode = E_OK; + if (!CommitExist(commitId, errCode)) { + LOGE("Failed to set header! The commit does not exist."); + return errCode; + } + } + + return SetHeaderInner(commitId); +} + +CommitID MultiVerNaturalStoreCommitStorage::GetHeader(int &errCode) const +{ + CommitID headerCommitID; + if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) { + LOGE("Failed to get commit for uninitialized store"); + errCode = -E_INVALID_DB; + return headerCommitID; + } + Key key; + TransferStringToKey(HEADER_KEY, key); + IOption option; + Value value; + errCode = commitStorageDBConnection_->Get(option, key, value); + if (errCode != E_OK) { + if (errCode == -E_NOT_FOUND) { // not find the header, means no header. + LOGI("Not find the header."); + errCode = E_OK; + } else { + LOGE("Get the commit header failed:%d", errCode); + return headerCommitID; + } + } + TransferValueToCommitID(value, headerCommitID); + return headerCommitID; +} + +bool MultiVerNaturalStoreCommitStorage::CommitExist(const CommitID &commitId, int &errCode) const +{ + IKvDBCommit *commit = GetCommit(commitId, errCode); + if (commit == nullptr) { + return false; + } else { + ReleaseCommit(commit); + commit = nullptr; + return true; + } +} + +void MultiVerNaturalStoreCommitStorage::ReleaseUnusedCommits( + std::map &commits) const +{ + // need release the unmerged commits + for (auto &item : commits) { + if (item.second != nullptr) { + ReleaseCommit(item.second); + item.second = nullptr; + } + } + commits.clear(); +} + +void MultiVerNaturalStoreCommitStorage::ReleaseLatestCommits( + std::map &latestCommits) const +{ + // need release the commits for exception. + for (auto &item : latestCommits) { + if (item.second != nullptr) { + ReleaseCommit(item.second); + item.second = nullptr; + } + } + latestCommits.clear(); +} + +void MultiVerNaturalStoreCommitStorage::ReleaseCommitList(list &commits) const +{ + for (auto &item : commits) { + if (item != nullptr) { + ReleaseCommit(item); + item = nullptr; + } + } + commits.clear(); +} + +int MultiVerNaturalStoreCommitStorage::GetLatestCommits(std::map &latestCommits) const +{ + latestCommits.clear(); + map commits; + CommitID headerId; + int errCode = GetAllCommits(commits, headerId); + if (errCode != E_OK || commits.empty()) { // error or no commit. + return errCode; + } + + std::stack commitStack; + commitStack.push(headerId); + while (!commitStack.empty()) { + CommitID frontId = commitStack.top(); + auto currentCommitIter = commits.find(frontId); + if (currentCommitIter == commits.end()) { + // not found the node in the commit tree. + LOGE("Not found the commit for the latest commits!"); + ReleaseLatestCommits(latestCommits); + errCode = -E_UNEXPECTED_DATA; + break; + } + + commitStack.pop(); + if (currentCommitIter->second == nullptr) { + // if the node has been released or traveled. + continue; + } + + AddParentsToStack(currentCommitIter->second, commits, commitStack); + + // Get the current commit info + DeviceID deviceInfo = currentCommitIter->second->GetDeviceInfo(); + auto latestCommit = latestCommits.find(deviceInfo); + if (latestCommit == latestCommits.end()) { + // not found any node of the device in the commit tree. + latestCommits.insert(make_pair(deviceInfo, currentCommitIter->second)); + } else if (CompareCommit(latestCommit->second, currentCommitIter->second)) { + // if the current commit version is bigger than the stored. + ReleaseCommit(latestCommit->second); + latestCommit->second = currentCommitIter->second; + } else { + ReleaseCommit(currentCommitIter->second); + } + currentCommitIter->second = nullptr; // has been traveled, set to nullptr. + } + + ReleaseUnusedCommits(commits); + return errCode; +} + +void MultiVerNaturalStoreCommitStorage::GetLocalVersionThredForLatestCommits( + const map &allCommits, const map &latestCommits, + map &latestCommitVersions) +{ + for (const auto &latestCommit : latestCommits) { + auto commitIter = allCommits.find(latestCommit.second); + if (commitIter != allCommits.end()) { + // found in the local store, just set the threshold. + latestCommitVersions.insert(make_pair(latestCommit.first, commitIter->second->GetCommitVersion())); + } else { + // not found in the local store, means that newer than local. + latestCommitVersions.insert(make_pair(latestCommit.first, VERSION_MAX)); + } + } +} + +void MultiVerNaturalStoreCommitStorage::AddParentsToStack(const IKvDBCommit *commit, + const std::map &allCommits, std::stack &commitStack) +{ + if (commit == nullptr) { + return; + } + + auto leftParentId = commit->GetLeftParentId(); + auto rightParentId = commit->GetRightParentId(); + if (!rightParentId.empty()) { + auto iter = allCommits.find(rightParentId); + if (iter != allCommits.end() && iter->second != nullptr) { + // if the right parent has not been traveled, just push into the stack. + commitStack.push(rightParentId); + } + } + if (!leftParentId.empty()) { + auto iter = allCommits.find(leftParentId); + if (iter != allCommits.end() && iter->second != nullptr) { + // if the left parent has not been traveled, just push into the stack. + commitStack.push(leftParentId); + } + } +} + +int MultiVerNaturalStoreCommitStorage::GetCommitTree(const map &latestCommits, + list &commits) const +{ + commits.clear(); + CommitID header; + map allCommits; + int errCode = GetAllCommits(allCommits, header); + // error or no commit. + if (errCode != E_OK || allCommits.empty()) { + return errCode; + } + map latestCommitVersions; + GetLocalVersionThredForLatestCommits(allCommits, latestCommits, latestCommitVersions); + std::stack commitStack; + commitStack.push(header); + while (!commitStack.empty()) { + CommitID frontId = commitStack.top(); + auto currentCommitIter = allCommits.find(frontId); + if (currentCommitIter == allCommits.end()) { + // not found the node in the commit tree. + LOGE("Not found the commit in the local tree!"); + ReleaseCommitList(commits); + errCode = -E_UNEXPECTED_DATA; + break; + } + commitStack.pop(); + if (currentCommitIter->second == nullptr) { + // if the commit has been traveled. + continue; + } + AddParentsToStack(currentCommitIter->second, allCommits, commitStack); + // Get the current commit info + DeviceID deviceInfo = currentCommitIter->second->GetDeviceInfo(); + auto latestCommit = latestCommitVersions.find(deviceInfo); + if (latestCommit == latestCommitVersions.end() || + latestCommit->second < currentCommitIter->second->GetCommitVersion()) { + // not found in the latest commits of the other device, + // or the current commit version is bigger than the threshold. + commits.push_back(currentCommitIter->second); + } else { + // means that the commit existed in the other device. + ReleaseCommit(currentCommitIter->second); + } + currentCommitIter->second = nullptr; + } + + ReleaseUnusedCommits(allCommits); + RefreshCommitTree(commits); // for version ascend. + return errCode; +} + +int MultiVerNaturalStoreCommitStorage::RunRekeyLogic(CipherType type, const CipherPassword &passwd) +{ + int errCode = static_cast(commitStorageDatabase_)->RunRekeyLogic(type, passwd); + if (errCode != E_OK) { + LOGE("commit logs rekey failed:%d", errCode); + } + return errCode; +} + +int MultiVerNaturalStoreCommitStorage::RunExportLogic(CipherType type, const CipherPassword &passwd, + const std::string &dbDir) +{ + // execute export + std::string newDbName = dbDir + "/" + MULTI_VER_COMMIT_DB_NAME; + int errCode = static_cast(commitStorageDatabase_)->RunExportLogic(type, passwd, newDbName); + if (errCode != E_OK) { + LOGE("commit logs export failed:%d", errCode); + } + return errCode; +} + +void MultiVerNaturalStoreCommitStorage::TransferCommitIDToKey(const CommitID &commitID, Key &key) +{ + key = commitID; +} + +int MultiVerNaturalStoreCommitStorage::TransferCommitToValue(const IKvDBCommit &commit, Value &value) +{ + // 3 uint64_t members. + uint32_t totalLength = Parcel::GetUInt64Len() * 3 + Parcel::GetVectorCharLen(commit.GetCommitId()) + + Parcel::GetVectorCharLen(commit.GetLeftParentId()) + Parcel::GetVectorCharLen(commit.GetRightParentId()) + + Parcel::GetStringLen(commit.GetDeviceInfo()); + if (totalLength > MAX_COMMIT_ST_LENGTH) { + LOGE("The commit length is over the max threshold"); + return -E_UNEXPECTED_DATA; + } + + value.resize(totalLength); + Parcel parcel(const_cast(value.data()), totalLength); + + int errCode = parcel.WriteUInt64(commit.GetTimestamp()); + if (errCode != E_OK) { + return errCode; + } + + uint64_t localFlag = static_cast((commit.GetLocalFlag() == true) ? 1 : 0); + errCode = parcel.WriteUInt64(localFlag); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteUInt64(commit.GetCommitVersion()); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteVectorChar(commit.GetCommitId()); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteVectorChar(commit.GetLeftParentId()); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteVectorChar(commit.GetRightParentId()); + if (errCode != E_OK) { + return errCode; + } + + return parcel.WriteString(commit.GetDeviceInfo()); +} + +int MultiVerNaturalStoreCommitStorage::TransferValueToCommit(const Value &value, IKvDBCommit &commit) +{ + size_t valueLength = value.size(); + if (valueLength == 0 || valueLength >= MAX_COMMIT_ST_LENGTH) { + LOGE("Failed to transfer value to commit struct! invalid value length:%zu.", valueLength); + return -E_UNEXPECTED_DATA; + } + + Timestamp timestamp = 0; + uint64_t localFlag = 1; + Version versionInfo; + + CommitID commitID; + CommitID leftParentID; + CommitID rightParentID; + DeviceID deviceInfo; + + Parcel parcel(const_cast(value.data()), valueLength); + parcel.ReadUInt64(timestamp); + parcel.ReadUInt64(localFlag); + parcel.ReadUInt64(versionInfo); + parcel.ReadVectorChar(commitID); + parcel.ReadVectorChar(leftParentID); + parcel.ReadVectorChar(rightParentID); + parcel.ReadString(deviceInfo); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + + // set commit value + commit.SetCommitVersion(versionInfo); + commit.SetCommitId(commitID); + commit.SetLeftParentId(leftParentID); + commit.SetRightParentId(rightParentID); + commit.SetTimestamp(timestamp); + commit.SetLocalFlag((localFlag == 1) ? true : false); + commit.SetDeviceInfo(deviceInfo); + return E_OK; +} + +void MultiVerNaturalStoreCommitStorage::TransferStringToKey(const string &str, Key &key) +{ + key.assign(str.begin(), str.end()); +} + +void MultiVerNaturalStoreCommitStorage::TransferCommitIDToValue(const CommitID &commitID, Value &value) +{ + value = commitID; +} + +void MultiVerNaturalStoreCommitStorage::TransferValueToCommitID(const Value &value, CommitID &commitID) +{ + commitID = value; +} + +bool MultiVerNaturalStoreCommitStorage::CompareCommit(const IKvDBCommit *first, + const IKvDBCommit *second) +{ + if (first == nullptr || second == nullptr) { + return false; + } + return first->GetCommitVersion() < second->GetCommitVersion(); +} + +int MultiVerNaturalStoreCommitStorage::GetAllCommits(map &commits, + CommitID &headerId) const +{ + if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) { + LOGE("Failed to get all commits for uninitialized store"); + return -E_INVALID_DB; + } + IOption option; + Key keyPrefix; + vector entries; + int errCode = commitStorageDBConnection_->GetEntries(option, keyPrefix, entries); + if (errCode != E_OK) { + if (errCode == -E_NOT_FOUND) { + errCode = E_OK; + } else { + LOGE("Failed to get commit entries from DB:%d", errCode); + } + + return errCode; + } + + Key header; + TransferStringToKey(HEADER_KEY, header); + + for (const auto &entry : entries) { + if (entry.key == header) { + headerId = entry.value; // get the header. + continue; + } + IKvDBCommit *commit = new (std::nothrow) MultiVerCommit(); + if (commit == nullptr) { + ReleaseUnusedCommits(commits); + LOGE("Failed to alloc commit! Bad alloc."); + return -E_OUT_OF_MEMORY; + } + errCode = TransferValueToCommit(entry.value, *commit); + if (errCode != E_OK) { + delete commit; + commit = nullptr; + ReleaseUnusedCommits(commits); + return errCode; + } + commits.insert(make_pair(commit->GetCommitId(), commit)); + } + return E_OK; +} + +int MultiVerNaturalStoreCommitStorage::SetHeaderInner(const CommitID &commitId) +{ + Key key; + Value value; + TransferStringToKey(HEADER_KEY, key); + TransferCommitIDToValue(commitId, value); + IOption option; + int errCode = commitStorageDBConnection_->Put(option, key, value); + if (errCode != E_OK) { + LOGE("Failed to set header! err:%d", errCode); + } + return errCode; +} + +int MultiVerNaturalStoreCommitStorage::CheckAddedCommit(const IKvDBCommit &commitEntry) const +{ + if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) { + LOGE("Failed to get commit! Commit storage do not open."); + return -E_INVALID_DB; + } + // Parameter check + if (!((static_cast(commitEntry)).CheckCommit())) { + LOGE("Failed to add commit! Commit is invalid."); + return -E_UNEXPECTED_DATA; + } + int errCode = E_OK; + if (commitEntry.GetLeftParentId().size() != 0) { + if (!CommitExist(commitEntry.GetLeftParentId(), errCode)) { + LOGE("Failed to add commit! The left parent commit does not exist."); + return errCode; + } + } + if (commitEntry.GetRightParentId().size() != 0) { + if (!CommitExist(commitEntry.GetRightParentId(), errCode)) { + LOGE("Failed to add commit! The right parent commit does not exist."); + return errCode; + } + } + + return E_OK; +} + +void MultiVerNaturalStoreCommitStorage::RefreshCommitTree(std::list &commits) +{ + if (commits.empty()) { + return; + } + commits.sort(CompareCommit); +} + +Version MultiVerNaturalStoreCommitStorage::GetMaxCommitVersion(int &errCode) const +{ + std::map commits; + CommitID headerId; + errCode = GetAllCommits(commits, headerId); + if (errCode != E_OK || commits.empty()) { // means no commit or error. + return 0; + } + + Version maxVersion = 0; + for (const auto &item : commits) { + if (item.second != nullptr) { + Version itemVersion = item.second->GetCommitVersion(); + maxVersion = (maxVersion < itemVersion) ? itemVersion : maxVersion; + } + } + ReleaseUnusedCommits(commits); + errCode = E_OK; + return maxVersion; +} + +int MultiVerNaturalStoreCommitStorage::BackupCurrentDatabase(const Property &property, const std::string &dir) +{ + KvDBProperties dbProperties; + dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path); + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE); + dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName); + dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + dbProperties.SetPassword(property.cipherType, property.passwd); + int errCode = SQLiteLocalKvDB::BackupCurrentDatabase(dbProperties, dir); + return errCode; +} + +int MultiVerNaturalStoreCommitStorage::ImportDatabase(const Property &property, const std::string &dir, + const CipherPassword &passwd) +{ + KvDBProperties dbProperties; + dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path); + dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE); + dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName); + dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + dbProperties.SetPassword(property.cipherType, property.passwd); + int errCode = SQLiteLocalKvDB::ImportDatabase(dbProperties, dir, passwd); + return errCode; +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.h b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.h new file mode 100644 index 0000000000000000000000000000000000000000..3a310de4f40efa9886a0fe83bee8ecd7c564bc23 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_NATURAL_STORE_COMMIT_STORAGE_H +#define MULTI_VER_NATURAL_STORE_COMMIT_STORAGE_H + +#ifndef OMIT_MULTI_VER +#include +#include + +#include "db_types.h" +#include "ikvdb.h" +#include "ikvdb_commit_storage.h" +#include "macro_utils.h" + +namespace DistributedDB { +class MultiVerNaturalStoreCommitStorage final : public IKvDBCommitStorage { +public: + MultiVerNaturalStoreCommitStorage(); + ~MultiVerNaturalStoreCommitStorage() override; + + DISABLE_COPY_ASSIGN_MOVE(MultiVerNaturalStoreCommitStorage); + + int CheckVersion(const Property &property, bool &isDbExist) const override; + + int Open(const IKvDBCommitStorage::Property &property) override; + + void Close() override; + + int Remove(const IKvDBCommitStorage::Property &property) override; + + IKvDBCommit *AllocCommit(int &errCode) const override; + + IKvDBCommit *GetCommit(const CommitID &commitId, int &errCode) const override; + + int AddCommit(const IKvDBCommit &commitEntry, bool isHeader) override; + int RemoveCommit(const CommitID &commitId) override; + + void ReleaseCommit(const IKvDBCommit *commit) const override; + + int SetHeader(const CommitID &commitId) override; + CommitID GetHeader(int &errCode) const override; + + bool CommitExist(const CommitID &commitId, int &errCode) const override; + + int GetLatestCommits(std::map &latestCommits) const override; + + Version GetMaxCommitVersion(int &errCode) const override; + + int GetCommitTree(const std::map &latestCommits, + std::list &commits) const override; + + int RunRekeyLogic(CipherType type, const CipherPassword &passwd); + + int RunExportLogic(CipherType type, const CipherPassword &passwd, const std::string &dbDir); + + int BackupCurrentDatabase(const Property &property, const std::string &dir) override; + + int ImportDatabase(const Property &property, const std::string &dir, const CipherPassword &passwd) override; + + int StartVacuum() override; + + int CancelVacuum() override; + + int FinishlVacuum() override; + + int GetAllCommitsInTree(std::list &commits) const override; + +private: + static void TransferCommitIDToKey(const CommitID &commitID, Key &key); + + static int TransferCommitToValue(const IKvDBCommit &commit, Value &value); + static int TransferValueToCommit(const Value &value, IKvDBCommit &commit); + + static void TransferStringToKey(const std::string &str, Key &key); + + static void TransferCommitIDToValue(const CommitID &commitID, Value &value); + static void TransferValueToCommitID(const Value &value, CommitID &commitID); + + static bool CompareCommit(const IKvDBCommit *first, const IKvDBCommit *second); + + static void GetLocalVersionThredForLatestCommits(const std::map &allCommits, + const std::map &latestCommits, std::map &latestCommitVersions); + + static void AddParentsToStack(const IKvDBCommit *commit, + const std::map &allCommits, std::stack &commitStack); + + static void RefreshCommitTree(std::list &commits); + + int GetAllCommits(std::map &commits, CommitID &header) const; + + int SetHeaderInner(const CommitID &commitId); + + int CheckAddedCommit(const IKvDBCommit &commitEntry) const; + + // Release the latest commits for exception. + void ReleaseLatestCommits(std::map &latestCommits) const; + + // Release the untraveled commits + void ReleaseUnusedCommits(std::map &commits) const; + + void ReleaseCommitList(std::list &commits) const; + + static int GetVersion(const IKvDBCommitStorage::Property &property, int &version, bool &isDbExisted); + +private: + static const std::string HEADER_KEY; + std::string branchTag_; + IKvDB *commitStorageDatabase_; + IKvDBConnection *commitStorageDBConnection_; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_NATURAL_STORE_COMMIT_STORAGE_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ba32ddb8c15733506184c6833a37aea6b0be25f --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.cpp @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_natural_store_connection.h" + +#include +#include +#include + +#include "log_print.h" +#include "db_errno.h" +#include "db_constant.h" +#include "multi_ver_natural_store_snapshot.h" +#include "multi_ver_natural_store.h" + +namespace DistributedDB { +MultiVerNaturalStoreConnection::MultiVerNaturalStoreConnection(MultiVerNaturalStore *kvDB) + : SyncAbleKvDBConnection(kvDB), + writeHandle_(nullptr) +{} + +MultiVerNaturalStoreConnection::~MultiVerNaturalStoreConnection() +{ + writeHandle_ = nullptr; +} + +// Get the value from the database +int MultiVerNaturalStoreConnection::Get(const IOption &option, const Key &key, Value &value) const +{ + int errCode = CheckDataStatus(key, {}, false); + if (errCode != E_OK) { + return errCode; + } + { + // Only for the read in the write transaction + std::lock_guard lock(writeMutex_); + if (writeHandle_ != nullptr) { + return writeHandle_->Get(key, value); + } + } + + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->InitCurrentReadVersion(); + if (errCode == E_OK) { + errCode = handle->Get(key, value); + } + + GetDB()->ReleaseHandle(handle); + return errCode; +} + +// Put the value to the database +int MultiVerNaturalStoreConnection::Put(const IOption &option, const Key &key, const Value &value) +{ + bool isAuto = false; + int errCode = CheckDataStatus(key, value, false); + if (errCode != E_OK) { + return errCode; + } + + std::lock_guard lock(writeMutex_); + errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("Start transaction failed:%d", errCode); + return errCode; + } + + errCode = writeHandle_->Put(key, value); + if (errCode != E_OK) { + LOGE("Put value err:%d", errCode); + if (isAuto) { + (void)(RollBackTransactionInner()); + } + return errCode; + } + + if (isAuto) { + errCode = CommitTransactionInner(); + } + + return errCode; +} + +// Delete the value from the database +int MultiVerNaturalStoreConnection::Delete(const IOption &option, const Key &key) +{ + int errCode = CheckDataStatus(key, {}, true); + if (errCode != E_OK) { + return errCode; + } + bool isAuto = false; + std::lock_guard lock(writeMutex_); + errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("start transaction to delete failed:%d", errCode); + return errCode; + } + + errCode = writeHandle_->Delete(key); + if (errCode != E_OK) { + if (isAuto) { + int rollbackErrCode = RollBackTransactionInner(); + LOGE("Connection Delete fail, rollback(state:%d) transaction!", rollbackErrCode); + } + return errCode; + } + + if (isAuto) { + errCode = CommitTransactionInner(); + } + return errCode; +} + +// Clear all the data from the database +int MultiVerNaturalStoreConnection::Clear(const IOption &option) +{ + bool isAuto = false; + std::lock_guard lock(writeMutex_); + int errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("start transaction to clear failed:%d", errCode); + return errCode; + } + + errCode = writeHandle_->Clear(); + if (errCode != E_OK) { + if (isAuto) { + int rollbackErrCode = RollBackTransactionInner(); + LOGD("Connection Clear, rollback(state:%d) transaction!", rollbackErrCode); + } + return errCode; + } + + if (isAuto) { + errCode = CommitTransactionInner(); + } + return errCode; +} + +// Get all the data from the database +int MultiVerNaturalStoreConnection::GetEntries(const IOption &option, + const Key &keyPrefix, std::vector &entries) const +{ + { + std::lock_guard lock(writeMutex_); + if (writeHandle_ != nullptr) { + return writeHandle_->GetEntries(keyPrefix, entries); + } + } + + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + return errCode; + } + errCode = handle->GetEntries(keyPrefix, entries); + GetDB()->ReleaseHandle(handle); + return errCode; +} + +// Put the batch values to the database. +int MultiVerNaturalStoreConnection::PutBatch(const IOption &option, const std::vector &entries) +{ + bool isAuto = false; + if (entries.empty() || entries.size() > DBConstant::MAX_BATCH_SIZE) { + return -E_INVALID_ARGS; + } + for (const auto &item : entries) { + if (CheckDataStatus(item.key, item.value, false) != E_OK) { + return -E_INVALID_ARGS; + } + } + + std::lock_guard lock(writeMutex_); + + // if the transaction is not started auto + int errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("start transaction failed:%d", errCode); + return errCode; + } + + for (const auto &item : entries) { + errCode = writeHandle_->Put(item.key, item.value); + if (errCode != E_OK) { + if (isAuto) { + (void)(RollBackTransactionInner()); + } + return errCode; + } + } + + if (isAuto) { + errCode = CommitTransactionInner(); + } + return errCode; +} + +// Delete the batch values from the database. +int MultiVerNaturalStoreConnection::DeleteBatch(const IOption &option, const std::vector &keys) +{ + if (keys.empty() || keys.size() > DBConstant::MAX_BATCH_SIZE) { + LOGE("[MultiVer]DeleteBatch size[%zu]!", keys.size()); + return -E_INVALID_ARGS; + } + if (!CheckDeletedKeys(keys)) { + return -E_INVALID_ARGS; + } + bool isAuto = false; + std::lock_guard lock(writeMutex_); + // if the transaction is not started auto + int errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("Start transaction failed:%d", errCode); + return errCode; + } + + // delete automatic + bool needCommit = false; + for (const auto &item : keys) { + errCode = writeHandle_->Delete(item); + if (errCode == E_OK) { + needCommit = true; + } else if (errCode != -E_NOT_FOUND) { + if (isAuto) { + (void)(RollBackTransactionInner()); + } + LOGE("Delete failed:%d", errCode); + return errCode; + } + } + + if (isAuto) { + if (needCommit) { + errCode = CommitTransactionInner(); + } else { + (void)(RollBackTransactionInner()); + errCode = -E_NOT_FOUND; + } + } else { + errCode = needCommit ? E_OK : -E_NOT_FOUND; + } + return errCode; +} + +int MultiVerNaturalStoreConnection::GetSnapshot(IKvDBSnapshot *&snapshot) const +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = E_OK; + auto handle = GetHandle(false, errCode); + if (handle == nullptr) { + LOGE("Get the handle for snapshot failed:%d", errCode); + return errCode; + } + + errCode = handle->InitCurrentReadVersion(); + if (errCode != E_OK) { + LOGE("Init the handle version for snapshot failed:%d", errCode); + GetDB()->ReleaseHandle(handle); + return errCode; + } + + snapshot = new (std::nothrow) MultiVerNaturalStoreSnapshot(handle); + if (snapshot == nullptr) { + GetDB()->ReleaseHandle(handle); + return -E_OUT_OF_MEMORY; + } + + std::lock_guard lock(snapshotMutex_); + snapshots_.insert(snapshot); + GetDB()->AddVersionConstraintToList(handle->GetCurrentReadVersion()); + return E_OK; +} + +// Release the created snapshot +void MultiVerNaturalStoreConnection::ReleaseSnapshot(IKvDBSnapshot *&snapshot) +{ + if (snapshot == nullptr) { + return; + } + + std::lock_guard lock(snapshotMutex_); + static_cast(snapshot)->Close(); + snapshots_.erase(snapshot); + delete snapshot; + snapshot = nullptr; + return; +} + +// Start the transaction +int MultiVerNaturalStoreConnection::StartTransaction() +{ + // Get the state of the transaction. + std::lock_guard lock(writeMutex_); + if (writeHandle_ != nullptr) { + LOGE("Transaction is already running"); + return -E_TRANSACT_STATE; + } + bool isAuto = false; + return StartTransactionInner(isAuto); +} + +// Commit the transaction +int MultiVerNaturalStoreConnection::Commit() +{ + std::lock_guard lock(writeMutex_); + return CommitTransactionInner(); +} + +// Roll back the transaction +int MultiVerNaturalStoreConnection::RollBack() +{ + std::lock_guard lock(writeMutex_); + return RollBackTransactionInner(); +} + +bool MultiVerNaturalStoreConnection::IsTransactionStarted() const +{ + std::lock_guard lock(writeMutex_); + if (writeHandle_ != nullptr) { + return true; + } + return false; +} + +// Close and delete the connection. +int MultiVerNaturalStoreConnection::PreClose() +{ + std::lock_guard snapshotLock(snapshotMutex_); + if (snapshots_.size() > 0) { + LOGE("the connection have unreleased snapshot, should not close."); + return -E_BUSY; + } + + std::lock_guard writeLock(writeMutex_); + if (writeHandle_ != nullptr) { + LOGE("the connection have transaction, should not close."); + (void)(RollBackTransactionInner()); + } + return E_OK; +} + +// Commit Transaction for local change data +int MultiVerNaturalStoreConnection::CommitTransactionInner() +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + // Get the state of the transaction. + if (writeHandle_ == nullptr) { + LOGE("Transaction has not been started."); + return -E_TRANSACT_STATE; + } + + int errCode = writeHandle_->CommitTransaction(); + GetDB()->ReleaseHandle(writeHandle_); + + return errCode; +} + +// If the transaction is started automatically, should roll back automatically +int MultiVerNaturalStoreConnection::RollBackTransactionInner() +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (writeHandle_ == nullptr) { + return -E_TRANSACT_STATE; + } + + int errCode = writeHandle_->RollBackTransaction(); + GetDB()->ReleaseHandle(writeHandle_); + + return errCode; +} + +int MultiVerNaturalStoreConnection::StartTransactionInner(bool &isAuto) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + isAuto = false; + if (writeHandle_ != nullptr) { + return E_OK; + } + + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + LOGE("Get write handle for transaction failed:%d", errCode); + return errCode; + } + errCode = handle->StartTransaction(); + if (errCode != E_OK) { + LOGE("Start transaction failed:%d", errCode); + GetDB()->ReleaseHandle(handle); + return errCode; + } + + writeHandle_ = handle; + isAuto = true; + + return E_OK; +} + +int MultiVerNaturalStoreConnection::TranslateObserverModeToEventTypes(unsigned mode, + std::list &eventTypes) const +{ + if (mode != NATURAL_STORE_COMMIT_EVENT) { + return -E_NOT_SUPPORT; + } else { + eventTypes.push_back(NATURAL_STORE_COMMIT_EVENT); + return E_OK; + } +} + +int MultiVerNaturalStoreConnection::Rekey(const CipherPassword &passwd) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + std::lock_guard lock(rekeyMutex_); + // Check the condition, have no more than one connection. + int errCode = kvDB_->TryToDisableConnection(OperatePerm::REKEY_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + + // Check the observer condition. + errCode = GenericKvDBConnection::PreCheckExclusiveStatus(); + if (errCode != E_OK) { + kvDB_->ReEnableConnection(OperatePerm::REKEY_MONOPOLIZE_PERM); + return errCode; + } + + // No need the check other + errCode = kvDB_->Rekey(passwd); + GenericKvDBConnection::ResetExclusiveStatus(); + kvDB_->ReEnableConnection(OperatePerm::REKEY_MONOPOLIZE_PERM); + return errCode; +} + +int MultiVerNaturalStoreConnection::Export(const std::string &filePath, const CipherPassword &passwd) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + return kvDB_->Export(filePath, passwd); +} + +int MultiVerNaturalStoreConnection::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + std::lock_guard lock(importMutex_); + int errCode = kvDB_->TryToDisableConnection(OperatePerm::IMPORT_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + + // Check the observer condition. + errCode = GenericKvDBConnection::PreCheckExclusiveStatus(); + if (errCode != E_OK) { + kvDB_->ReEnableConnection(OperatePerm::IMPORT_MONOPOLIZE_PERM); + return errCode; + } + errCode = kvDB_->Import(filePath, passwd); + GenericKvDBConnection::ResetExclusiveStatus(); + kvDB_->ReEnableConnection(OperatePerm::IMPORT_MONOPOLIZE_PERM); + return errCode; +} + +bool MultiVerNaturalStoreConnection::CheckDeletedKeys(const std::vector &keys) +{ + for (const auto &item : keys) { + if (item.empty() || item.size() > DBConstant::MAX_KEY_SIZE) { + return false; + } + } + return true; +} + +int MultiVerNaturalStoreConnection::CheckDataStatus(const Key &key, const Value &value, bool isDeleted) const +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + return static_cast(kvDB_)->CheckDataStatus(key, value, isDeleted); +} + +MultiVerStorageExecutor *MultiVerNaturalStoreConnection::GetHandle(bool isWrite, int &errCode) const +{ + MultiVerNaturalStore *multiVerNatureStore = GetDB(); + if (multiVerNatureStore == nullptr) { + errCode = -E_INVALID_DB; + return nullptr; + } + + return multiVerNatureStore->GetHandle(isWrite, errCode); +} + +DEFINE_OBJECT_TAG_FACILITIES(MultiVerNaturalStoreConnection) +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.h b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..86456481b6cd3b5bbc742a1dca8e37805ed3d317 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_NATURAL_STORE_CONNECTION_H +#define MULTI_VER_NATURAL_STORE_CONNECTION_H + +#ifndef OMIT_MULTI_VER +#include +#include + +#include "macro_utils.h" +#include "sync_able_kvdb_connection.h" +#include "multi_ver_def.h" +#include "multi_ver_kv_entry.h" +#include "multi_ver_storage_executor.h" + +namespace DistributedDB { +class MultiVerNaturalStore; + +enum class TransactState { + TRANSACT_IDLE, + TRANSACT_IN_PROGRESS, +}; + +class MultiVerNaturalStoreConnection : public SyncAbleKvDBConnection { +public: + explicit MultiVerNaturalStoreConnection(MultiVerNaturalStore *kvDB); + ~MultiVerNaturalStoreConnection() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(MultiVerNaturalStoreConnection); + + // Get the value from the database + int Get(const IOption &option, const Key &key, Value &value) const override; + + // Put the value to the database + int Put(const IOption &option, const Key &key, const Value &value) override; + + // Delete the value from the database + int Delete(const IOption &option, const Key &key) override; + + // Clear all the data from the database + int Clear(const IOption &option) override; + + // Get all the data from the database + int GetEntries(const IOption &option, const Key &keyPrefix, std::vector &entries) const override; + + // Put the batch values to the database. + int PutBatch(const IOption &option, const std::vector &entries) override; + + // Put the synced data by commit. + int PutCommitData(const MultiVerCommitNode &commit, const std::vector &entries); + + // Delete the batch values from the database. + int DeleteBatch(const IOption &option, const std::vector &keys) override; + + // Get the snapshot + int GetSnapshot(IKvDBSnapshot *&snapshot) const override; + + // Release the created snapshot + void ReleaseSnapshot(IKvDBSnapshot *&snapshot) override; + + // Start the transaction + int StartTransaction() override; + + // Commit the transaction + int Commit() override; + + // Roll back the transaction + int RollBack() override; + + // Check if the transaction already started manually + bool IsTransactionStarted() const override; + + // Called when close and delete the connection. + int PreClose() override; + + // Parse event types(from observer mode). + int TranslateObserverModeToEventTypes(unsigned mode, std::list &eventTypes) const override; + + int Rekey(const CipherPassword &passwd) override; + + int Export(const std::string &filePath, const CipherPassword &passwd) override; + + int Import(const std::string &filePath, const CipherPassword &passwd) override; + +private: + static bool CheckDeletedKeys(const std::vector &keys); + + int CheckDataStatus(const Key &key, const Value &value, bool isDeleted) const; + + int StartTransactionInner(bool &isAuto); + + int CommitTransactionInner(); + + int RollBackTransactionInner(); + + int CheckTransactionState(); + + MultiVerStorageExecutor *GetHandle(bool isWrite, int &errCode) const; + + DECLARE_OBJECT_TAG(MultiVerNaturalStoreConnection); + + MultiVerStorageExecutor *writeHandle_; + mutable std::set snapshots_; + mutable std::mutex snapshotMutex_; + mutable std::mutex writeMutex_; + std::mutex rekeyMutex_; + std::mutex importMutex_; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_NATURAL_STORE_CONNECTION_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5b11e3af0ac44bfe016045e280422591fecfe99a --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_natural_store_snapshot.h" + +#include "db_constant.h" +#include "db_errno.h" +#include "log_print.h" +#include "multi_ver_storage_executor.h" + +namespace DistributedDB { +MultiVerNaturalStoreSnapshot::MultiVerNaturalStoreSnapshot(StorageExecutor *handle) + : databaseHandle_(handle) +{} + +MultiVerNaturalStoreSnapshot::~MultiVerNaturalStoreSnapshot() +{ + databaseHandle_ = nullptr; +} + +int MultiVerNaturalStoreSnapshot::Get(const Key &key, Value &value) const +{ + if (databaseHandle_ == nullptr) { + return -E_INVALID_DB; + } + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + LOGE("[MultiSnapshot] Invalid key[%zu]", key.size()); + return -E_INVALID_ARGS; + } + return static_cast(databaseHandle_)->Get(key, value); +} + +int MultiVerNaturalStoreSnapshot::GetEntries(const Key &keyPrefix, std::vector &entries) const +{ + if (databaseHandle_ == nullptr) { + return -E_INVALID_DB; + } + if (keyPrefix.size() > DBConstant::MAX_KEY_SIZE) { + LOGE("[MultiSnapshot] Invalid prefix[%zu]", keyPrefix.size()); + return -E_INVALID_ARGS; + } + return static_cast(databaseHandle_)->GetEntries(keyPrefix, entries); +} + +void MultiVerNaturalStoreSnapshot::Close() +{ + if (databaseHandle_ != nullptr) { + static_cast(databaseHandle_)->Close(); + databaseHandle_ = nullptr; + } +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.h b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.h new file mode 100644 index 0000000000000000000000000000000000000000..86ae7e9bb06f0840efc9c96a62ab178e90464391 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_NATURAL_STORE_SNAPSHOT_H +#define MULTI_VER_NATURAL_STORE_SNAPSHOT_H + +#ifndef OMIT_MULTI_VER +#include "ikvdb_snapshot.h" +#include "storage_executor.h" + +namespace DistributedDB { +class MultiVerNaturalStoreSnapshot : public IKvDBSnapshot { +public: + explicit MultiVerNaturalStoreSnapshot(StorageExecutor *handle); + ~MultiVerNaturalStoreSnapshot() override; + + DISABLE_COPY_ASSIGN_MOVE(MultiVerNaturalStoreSnapshot); + + // Get the value according the key in the snapshot + int Get(const Key &key, Value &value) const override; + + // Get the data according the prefix key in the snapshot + int GetEntries(const Key &keyPrefix, std::vector &entries) const override; + + void Close(); + +private: + StorageExecutor *databaseHandle_; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_NATURAL_STORE_SNAPSHOT_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..acc4e7710c412ed69e5dea4f7b17322c498e6151 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_natural_store_transfer_data.h" + +#include "db_constant.h" +#include "log_print.h" +#include "db_errno.h" + +namespace DistributedDB { +int MultiVerNaturalStoreTransferData::SegmentAndTransferValueToHash(const Value &oriValue, + std::vector &partValues) const +{ + if (oriValue.size() <= sliceLengthThreshold_) { + return -E_UNEXPECTED_DATA; + } + + const uint32_t sizeByte = blockSizeByte_; + if (sizeByte == 0) { + return -E_UNEXPECTED_DATA; + } + + const size_t partNum = oriValue.size() / sizeByte; + + for (size_t i = 0; i < partNum; i++) { + Value tempValue(sizeByte); + // When the hash value is combined, the overlapped part is removed. So not need -1 at tail + std::copy(oriValue.begin() + i * sizeByte, oriValue.begin() + sizeByte * (i + 1), tempValue.begin()); + partValues.push_back(std::move(tempValue)); + } + Value tailValue(oriValue.size() - partNum * sizeByte); + std::copy(oriValue.begin() + partNum * sizeByte, oriValue.end(), tailValue.begin()); + if (!tailValue.empty()) { + partValues.push_back(tailValue); + } + + return E_OK; +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.h b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.h new file mode 100644 index 0000000000000000000000000000000000000000..5fa07f7150b03a1b8d33f21f0869ad4014b91e08 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_NATURAL_STORE_TRANSFER_DATA_H +#define MULTI_VER_NATURAL_STORE_TRANSFER_DATA_H + +#ifndef OMIT_MULTI_VER +#include "db_types.h" +#include "macro_utils.h" + +namespace DistributedDB { +class MultiVerNaturalStoreTransferData { +public: + MultiVerNaturalStoreTransferData() {}; + ~MultiVerNaturalStoreTransferData() {}; + + DISABLE_COPY_ASSIGN_MOVE(MultiVerNaturalStoreTransferData); + + int SegmentAndTransferValueToHash(const Value &oriValue, std::vector &partValues) const; + +private: + size_t sliceLengthThreshold_ = 4194304; // 4MB + size_t blockSizeByte_ = 4194304; // 4MB +}; +} // namespace DistributedDB + +#endif // MULTI_VER_NATURAL_STORE_CONNECTION_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_storage_engine.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_storage_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ec1015d9203cf44478d8d4c53610b655c7fd82d --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_storage_engine.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_storage_engine.h" + +#include "multi_ver_storage_executor.h" + +#include "db_errno.h" + +namespace DistributedDB { +MultiVerStorageEngine::MultiVerStorageEngine() + : kvDB_(nullptr), + dataStorage_(nullptr), + commitStorage_(nullptr), + kvDataStorage_(nullptr) +{} + +MultiVerStorageEngine::~MultiVerStorageEngine() +{ + kvDB_ = nullptr; + dataStorage_ = nullptr; + commitStorage_ = nullptr; + kvDataStorage_ = nullptr; +} + +int MultiVerStorageEngine::InitDatabases(IKvDB *kvDB, IKvDBMultiVerDataStorage *dataStorage, + IKvDBCommitStorage *commitStorage, MultiVerKvDataStorage *kvDataStorage, const StorageEngineAttr &poolSize) +{ + if (StorageEngine::CheckEngineAttr(poolSize)) { + return -E_INVALID_ARGS; + } + if (kvDB == nullptr || dataStorage == nullptr || + commitStorage == nullptr || kvDataStorage == nullptr) { + return -E_INVALID_DB; + } + engineAttr_ = poolSize; + kvDB_ = kvDB; + dataStorage_ = dataStorage; + commitStorage_ = commitStorage; + kvDataStorage_ = kvDataStorage; + return Init(); +} + +int MultiVerStorageEngine::CreateNewExecutor(bool isWrite, StorageExecutor *&handle) +{ + handle = new (std::nothrow) MultiVerStorageExecutor(kvDB_, dataStorage_, commitStorage_, kvDataStorage_, isWrite); + if (handle == nullptr) { + return -E_OUT_OF_MEMORY; + } + return E_OK; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_storage_engine.h b/mock/distributeddb/storage/src/multiver/multi_ver_storage_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..9333571126a479de2c628bec96507dd3be2cd333 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_storage_engine.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_STORAGE_ENGINE_H +#define MULTI_STORAGE_ENGINE_H + +#ifndef OMIT_MULTI_VER +#include "storage_engine.h" +#include "macro_utils.h" +#include "ikvdb.h" +#include "ikvdb_multi_ver_data_storage.h" +#include "ikvdb_commit_storage.h" +#include "multi_ver_kvdata_storage.h" + +namespace DistributedDB { +class MultiVerStorageEngine : public StorageEngine { +public: + MultiVerStorageEngine(); + ~MultiVerStorageEngine() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(MultiVerStorageEngine); + + int InitDatabases(IKvDB *kvDB, IKvDBMultiVerDataStorage *dataStorage, + IKvDBCommitStorage *commitStorage, MultiVerKvDataStorage *kvDataStorage, const StorageEngineAttr &poolSize); + +protected: + int CreateNewExecutor(bool isWrite, StorageExecutor *&handle) override; + +private: + IKvDB *kvDB_; + IKvDBMultiVerDataStorage *dataStorage_; + IKvDBCommitStorage *commitStorage_; + MultiVerKvDataStorage *kvDataStorage_; +}; +} // namespace DistributedDB +#endif // MULTI_STORAGE_ENGINE_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_storage_executor.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_storage_executor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..14b82f68e8b9d662ec3831254fde9d48678dffad --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_storage_executor.cpp @@ -0,0 +1,1503 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_storage_executor.h" + +#include + +#include "db_common.h" +#include "db_errno.h" +#include "log_print.h" +#include "multi_ver_natural_store.h" +#include "multi_ver_natural_store_commit_notify_data.h" +#include "multi_ver_natural_store_transfer_data.h" +#include "value_hash_calc.h" + +namespace DistributedDB { +MultiVerStorageExecutor::MultiVerStorageExecutor(IKvDB *kvDB, IKvDBMultiVerDataStorage *dataStorage, + IKvDBCommitStorage *commitStorage, MultiVerKvDataStorage *kvDataStorage, bool writable) + : StorageExecutor(writable), + kvDB_(kvDB), + dataStorage_(dataStorage), + commitStorage_(commitStorage), + kvDataStorage_(kvDataStorage), + transaction_(nullptr), + sliceTransaction_(nullptr) +{} + +MultiVerStorageExecutor::~MultiVerStorageExecutor() +{ + kvDB_ = nullptr; + dataStorage_ = nullptr; + commitStorage_ = nullptr; + kvDataStorage_ = nullptr; + transaction_ = nullptr; +} + +int MultiVerStorageExecutor::Reset() +{ + return E_OK; +} + +int MultiVerStorageExecutor::PutMetaData(const Key &key, const Value &value) +{ + if (kvDataStorage_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = kvDataStorage_->PutMetaData(key, value); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::GetMetaData(const Key &key, Value &value) const +{ + if (kvDataStorage_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = kvDataStorage_->GetMetaData(key, value); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::GetDeviceLatestCommit(std::map &commitMap) const +{ + if (commitStorage_ == nullptr) { + LOGE("The commit history module is null."); + return -E_INVALID_DB; + } + std::map latestCommits; + int errCode = commitStorage_->GetLatestCommits(latestCommits); + if (errCode != E_OK) { + LOGE("Get latest commits failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + for (auto &latestCommit : latestCommits) { + uint64_t localFlag = (latestCommit.second->GetLocalFlag() ? + MultiVerCommitNode::LOCAL_FLAG : MultiVerCommitNode::NON_LOCAL_FLAG); + MultiVerCommitNode commit = { + latestCommit.second->GetCommitId(), // commitId + latestCommit.second->GetLeftParentId(), // leftParent + latestCommit.second->GetRightParentId(), // rightParent + latestCommit.second->GetTimestamp(), // timestamp + latestCommit.second->GetCommitVersion(), // version + localFlag, // isLocal + latestCommit.second->GetDeviceInfo() // deviceInfo + }; + + commitStorage_->ReleaseCommit(latestCommit.second); + latestCommit.second = nullptr; + commitMap.insert(std::make_pair(latestCommit.first, std::move(commit))); + } + latestCommits.clear(); + return E_OK; +} + +int MultiVerStorageExecutor::GetCommitTree(const std::map &commitMap, + std::vector &commits) const +{ + if (commitStorage_ == nullptr) { + LOGE("The commit history module is null."); + return -E_INVALID_DB; + } + std::map latestCommits; + for (auto &latestCommit : commitMap) { + latestCommits.insert(std::make_pair(latestCommit.first, latestCommit.second.commitId)); + } + std::list commitTree; + int errCode = commitStorage_->GetCommitTree(latestCommits, commitTree); + if (errCode != E_OK) { + LOGE("Get commit tree failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + LOGD("Get commit tree size:%zu", commitTree.size()); + for (auto &commitNode : commitTree) { + if (commitNode == nullptr) { + continue; + } + uint64_t localFlag = (commitNode->GetLocalFlag() ? + MultiVerCommitNode::LOCAL_FLAG : MultiVerCommitNode::NON_LOCAL_FLAG); + MultiVerCommitNode commit = { + commitNode->GetCommitId(), // commitId + commitNode->GetLeftParentId(), // leftParent + commitNode->GetRightParentId(), // rightParent + commitNode->GetTimestamp(), // timestamp + commitNode->GetCommitVersion(), // version + localFlag, // isLocal + commitNode->GetDeviceInfo() // deviceInfo + }; + + commitStorage_->ReleaseCommit(commitNode); + commitNode = nullptr; + commits.push_back(std::move(commit)); + } + commitTree.clear(); + return E_OK; +} + +int MultiVerStorageExecutor::GetCommitData(const MultiVerCommitNode &commit, + std::vector &entries) const +{ + if ((commitStorage_ == nullptr) || (dataStorage_ == nullptr)) { + return -E_INVALID_DB; + } + // call the putting value method. + CommitID commitId = commit.commitId; + int errCode = E_OK; + Version version; + IKvDBCommit *commitNode = commitStorage_->GetCommit(commitId, errCode); + if (commitNode == nullptr) { + LOGE("Failed to get the commit:%d", errCode); + return CheckCorruptedStatus(errCode); + } + + // Get the commit and the version. + std::string devInfo = commitNode->GetDeviceInfo(); + version = commitNode->GetCommitVersion(); + commitStorage_->ReleaseCommit(commitNode); + commitNode = nullptr; + if (devInfo.size() != MULTI_VER_TAG_SIZE) { + LOGD("skip the foreign data"); + entries.clear(); + entries.shrink_to_fit(); + return E_OK; + } + + IKvDBMultiVerTransaction *transaction = dataStorage_->StartRead(KvDataType::KV_DATA_SYNC_P2P, version, errCode); + if (transaction == nullptr) { + LOGE("Failed to get the transaction:%d", errCode); + goto END; + } + + errCode = transaction->GetEntriesByVersion(version, entries); + if (errCode != E_OK) { + LOGE("Get entries by version failed:%d", errCode); + } +END: + if (transaction != nullptr) { + dataStorage_->ReleaseTransaction(transaction); + transaction = nullptr; + } + return CheckCorruptedStatus(errCode); +} + +bool MultiVerStorageExecutor::IsCommitExisted(const MultiVerCommitNode &commit, int &errCode) const +{ + if ((commitStorage_ == nullptr) || (dataStorage_ == nullptr)) { + LOGE("The commit history module or data storage is null."); + return false; + } + auto readCommit = commitStorage_->GetCommit(commit.commitId, errCode); + if (readCommit == nullptr) { + return false; + } + commitStorage_->ReleaseCommit(readCommit); + + bool result = false; + std::vector entries; + auto transaction = dataStorage_->StartRead(KvDataType::KV_DATA_SYNC_P2P, commit.version, errCode); + if (transaction == nullptr) { + LOGE("Failed to get the transaction:%d", errCode); + goto END; + } + + errCode = transaction->GetEntriesByVersion(commit.version, entries); + if (errCode != E_OK) { + LOGE("Get entries by version failed:%d", errCode); + goto END; + } + if (!entries.empty()) { + result = true; + } +END: + if (errCode != E_OK) { + result = false; + } + if (transaction != nullptr) { + dataStorage_->ReleaseTransaction(transaction); + } + + ReleaseMultiVerKvEntries(entries); + errCode = CheckCorruptedStatus(errCode); + return result; +} + +bool MultiVerStorageExecutor::IsValueSliceExisted(const ValueSliceHash &value, int &errCode) const +{ + if (kvDataStorage_ == nullptr) { + errCode = -E_INVALID_DB; + return false; + } + auto sliceTransaction = kvDataStorage_->GetSliceTransaction(false, errCode); + if (sliceTransaction == nullptr) { + (void)(CheckCorruptedStatus(errCode)); + return false; + } + Value valueReal; + errCode = sliceTransaction->GetData(value, valueReal); + kvDataStorage_->ReleaseSliceTransaction(sliceTransaction); + if (errCode == E_OK) { + return true; + } + (void)(CheckCorruptedStatus(errCode)); + return false; +} + +int MultiVerStorageExecutor::GetValueSlice(const ValueSliceHash &hashValue, ValueSlice &sliceValue) const +{ + return GetValueSliceInner(nullptr, hashValue, sliceValue); +} + +int MultiVerStorageExecutor::PutValueSlice(const ValueSliceHash &hashValue, const ValueSlice &sliceValue, + bool isAddCount) +{ + return PutValueSliceInner(nullptr, hashValue, sliceValue, isAddCount); +} + +int MultiVerStorageExecutor::GetValueSliceInner(const SliceTransaction *sliceTransaction, + const ValueSliceHash &hashValue, ValueSlice &sliceValue) const +{ + int errCode; + if (sliceTransaction != nullptr) { + errCode = sliceTransaction->GetData(hashValue, sliceValue); + return CheckCorruptedStatus(errCode); + } + if (kvDataStorage_ == nullptr) { + return -E_INVALID_DB; + } + + auto sliceTransact = kvDataStorage_->GetSliceTransaction(false, errCode); + if (sliceTransact == nullptr) { + return CheckCorruptedStatus(errCode); + } + + errCode = sliceTransact->GetData(hashValue, sliceValue); + kvDataStorage_->ReleaseSliceTransaction(sliceTransact); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::PutValueSliceInner(SliceTransaction *sliceTransaction, const ValueSliceHash &hashValue, + const ValueSlice &sliceValue, bool isAddCount) +{ + int errCode; + if (sliceTransaction != nullptr) { + errCode = sliceTransaction->PutData(hashValue, sliceValue, isAddCount); + return CheckCorruptedStatus(errCode); + } + + if (kvDataStorage_ == nullptr) { + return -E_INVALID_DB; + } + + auto transaction = kvDataStorage_->GetSliceTransaction(true, errCode); + if (transaction == nullptr) { + return CheckCorruptedStatus(errCode); + } + + errCode = transaction->PutData(hashValue, sliceValue, isAddCount); + kvDataStorage_->ReleaseSliceTransaction(transaction); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::DeleteValueSliceInner(SliceTransaction *sliceTransaction, + const ValueSliceHash &hashValue) +{ + int errCode; + if (sliceTransaction != nullptr) { + errCode = sliceTransaction->DeleteData(hashValue); + return CheckCorruptedStatus(errCode); + } + + if (kvDataStorage_ == nullptr) { + return -E_INVALID_DB; + } + + auto transaction = kvDataStorage_->GetSliceTransaction(true, errCode); + if (transaction == nullptr) { + return CheckCorruptedStatus(errCode); + } + + errCode = transaction->DeleteData(hashValue); + kvDataStorage_->ReleaseSliceTransaction(transaction); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::StartSliceTransaction() +{ + if (kvDataStorage_ == nullptr) { + return -E_INVALID_DB; + } + if (sliceTransaction_ != nullptr) { + return -E_UNEXPECTED_DATA; + } + int errCode; + sliceTransaction_ = kvDataStorage_->GetSliceTransaction(true, errCode); + if (sliceTransaction_ == nullptr) { + return errCode; + } + errCode = sliceTransaction_->StartTransaction(); + if (errCode != E_OK) { + kvDataStorage_->ReleaseSliceTransaction(sliceTransaction_); + } + return errCode; +} + +int MultiVerStorageExecutor::CommitSliceTransaction() +{ + if (sliceTransaction_ == nullptr) { + return -E_UNEXPECTED_DATA; + } + int errCode = sliceTransaction_->CommitTransaction(); + if (errCode != E_OK) { + LOGE("Commit slice transaction failed:%d", errCode); + } + if (kvDataStorage_ == nullptr) { + return -E_INVALID_DB; + } + kvDataStorage_->ReleaseSliceTransaction(sliceTransaction_); + sliceTransaction_ = nullptr; + return errCode; +} + +int MultiVerStorageExecutor::RollbackSliceTransaction() +{ + if (sliceTransaction_ == nullptr) { + return -E_UNEXPECTED_DATA; + } + int errCode = sliceTransaction_->RollbackTransaction(); + if (errCode != E_OK) { + LOGE("Commit slice transaction failed:%d", errCode); + } + if (kvDataStorage_ == nullptr) { + return -E_INVALID_DB; + } + kvDataStorage_->ReleaseSliceTransaction(sliceTransaction_); + sliceTransaction_ = nullptr; + return errCode; +} + +int MultiVerStorageExecutor::ReInitTransactionVersion(const MultiVerCommitNode &commit) +{ + if (commitStorage_ == nullptr) { + LOGE("The commit history module is null when reinit transaction version."); + return -E_INVALID_DB; + } + int errCode = StartTransaction(); + if (errCode != E_OK) { + LOGE("Start transaction failed:%d", errCode); + return errCode; + } + auto readCommit = commitStorage_->GetCommit(commit.commitId, errCode); + if (readCommit == nullptr) { + if (errCode != -E_NOT_FOUND) { + RollBackTransaction(); + LOGE("Get the commit error:%d", errCode); + return errCode; + } else { + errCode = E_OK; + } + } else { + LOGD("Reput the version:%" PRIu64, readCommit->GetCommitVersion()); + transaction_->SetVersion(readCommit->GetCommitVersion()); + commitStorage_->ReleaseCommit(readCommit); + } + + if (errCode != E_OK) { + RollBackTransaction(); + } + return errCode; +} + +int MultiVerStorageExecutor::AddSliceDataCount(const std::vector &values) +{ + for (const auto &item : values) { + MultiVerValueObject valueObject; + int errCode = valueObject.DeSerialData(item); + if (errCode != E_OK) { + return errCode; + } + if (!valueObject.IsHash()) { + continue; + } + std::vector valueHashList; + valueObject.GetValueHash(valueHashList); + for (auto &iter : valueHashList) { + Value filledData; + errCode = PutValueSliceInner(sliceTransaction_, iter, filledData, true); + if (errCode != E_OK) { + LOGE("Add the slice value count failed:%d", errCode); + return errCode; + } + } + } + return E_OK; +} +int MultiVerStorageExecutor::PutCommitData(const MultiVerCommitNode &commit, + const std::vector &entries, const std::string &deviceName) +{ + // Update the version while the commit has been put. + int errCode = ReInitTransactionVersion(commit); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + errCode = StartSliceTransaction(); + if (errCode != E_OK) { + RollBackTransaction(); + return CheckCorruptedStatus(errCode); + } + + if (transaction_ == nullptr) { + return -E_INVALID_DB; + } + + std::vector values; + errCode = transaction_->PutBatch(entries, false, values); + if (errCode != E_OK) { + LOGE("Put batch synced data failed:%d", errCode); + goto END; + } + errCode = AddSliceDataCount(values); + if (errCode != E_OK) { + goto END; + } + errCode = CommitSliceTransaction(); + if (errCode != E_OK) { + RollBackTransaction(); + } else { + errCode = CommitTransaction(commit, false); + } + return CheckCorruptedStatus(errCode); +END: + if (errCode != E_OK) { + (void)(RollbackSliceTransaction()); + RollBackTransaction(); + } + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::MergeSyncCommit(const MultiVerCommitNode &commit, + const std::vector &commits) +{ + if (commits.empty()) { + return E_OK; + } + // if all the nodes have two parents, no need to merge. + bool isAllMerged = true; + for (const auto &item : commits) { + if (item.rightParent.empty()) { + isAllMerged = false; + } + } + + if (isAllMerged) { + LOGI("all nodes have been merged"); + return E_OK; + } + + int errCode = MergeCommits(commits); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::MergeOneCommit(const MultiVerCommitNode &commit) +{ + std::vector entries; + int errCode = GetResolvedConflictEntries(commit, entries); + if (errCode != E_OK) { + return errCode; + } + + if (transaction_ == nullptr) { + return -E_INVALID_DB; + } + + std::vector values; + errCode = transaction_->PutBatch(entries, true, values); + if (errCode != E_OK) { + goto END; + } + + errCode = AddSliceDataCount(values); +END: + ReleaseMultiVerKvEntries(entries); + return errCode; +} + +int MultiVerStorageExecutor::MergeCommits(const std::vector &commits) +{ + const MultiVerCommitNode &rootCommitNode = commits.back(); + std::string rootNodeDeviceInfo = rootCommitNode.deviceInfo; + if (rootNodeDeviceInfo.size() != SHA256_DIGEST_LENGTH + MULTI_VER_TAG_SIZE) { + return -E_UNEXPECTED_DATA; + } + int errCode = StartTransaction(); + if (errCode != E_OK) { + return errCode; + } + errCode = StartSliceTransaction(); + if (errCode != E_OK) { + RollBackTransaction(); + return errCode; + } + for (const auto &item : commits) { + // only need to merge the node data which is from the same device + if (item.deviceInfo.size() != SHA256_DIGEST_LENGTH + MULTI_VER_TAG_SIZE && + item.deviceInfo.size() != MULTI_VER_TAG_SIZE) { + errCode = -E_UNEXPECTED_DATA; + break; + } + if (item.deviceInfo.size() == MULTI_VER_TAG_SIZE || + item.deviceInfo.compare(0, SHA256_DIGEST_LENGTH, rootNodeDeviceInfo, 0, SHA256_DIGEST_LENGTH) != 0) { + LOGD("Skip the version:%" PRIu64, item.version); + continue; + } + errCode = MergeOneCommit(item); + if (errCode != E_OK) { + break; + } + } + + if (errCode != E_OK) { + (void)(RollbackSliceTransaction()); + errCode = RollBackTransaction(); + } else { + errCode = CommitSliceTransaction(); + if (errCode == E_OK) { + errCode = CommitTransaction(rootCommitNode, true); + } else { + LOGE("Commit the slice transaction error, rollback the data transaction"); + RollBackTransaction(); + } + } + return errCode; +} + +int MultiVerStorageExecutor::GetDiffEntries(const CommitID &begin, const CommitID &end, MultiVerDiffData &data) const +{ + if ((commitStorage_ == nullptr) || (dataStorage_ == nullptr)) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + Version verBegin; + if (begin.empty()) { + verBegin = 0; + } else { + IKvDBCommit *commitBegin = commitStorage_->GetCommit(begin, errCode); + if (commitBegin == nullptr) { + verBegin = 0; + } else { + verBegin = commitBegin->GetCommitVersion(); + } + commitStorage_->ReleaseCommit(commitBegin); + commitBegin = nullptr; + } + + IKvDBCommit *commitEnd = commitStorage_->GetCommit(end, errCode); + if (commitEnd == nullptr) { + return CheckCorruptedStatus(errCode); + } + + Version verEnd = commitEnd->GetCommitVersion(); + commitStorage_->ReleaseCommit(commitEnd); + commitEnd = nullptr; + + IKvDBMultiVerTransaction *transaction = + dataStorage_->StartRead(KvDataType::KV_DATA_SYNC_P2P, verBegin, errCode); + if (transaction == nullptr) { + LOGE("Get diff data start read failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + + errCode = transaction->GetDiffEntries(verBegin, verEnd, data); + if (errCode != E_OK) { + LOGE("get diff entries failed:%d", errCode); + goto END; + } + + errCode = TransferDiffEntries(data); +END: + dataStorage_->ReleaseTransaction(transaction); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::Get(const Key &key, Value &value) const +{ + if (dataStorage_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = E_OK; + auto transaction = dataStorage_->StartRead(KvDataType::KV_DATA_SYNC_P2P, readVersion_, errCode); + if (transaction == nullptr) { + LOGE("Get read transaction failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + + Value rawValue; + errCode = transaction->Get(key, rawValue); + + dataStorage_->ReleaseTransaction(transaction); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + return TransferToUserValue(rawValue, value); +} + +int MultiVerStorageExecutor::GetEntries(const Key &keyPrefix, std::vector &entries) const +{ + if (dataStorage_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = E_OK; + auto transaction = dataStorage_->StartRead(KvDataType::KV_DATA_SYNC_P2P, readVersion_, errCode); + if (transaction == nullptr) { + LOGE("Get read transaction failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + + errCode = transaction->GetEntries(keyPrefix, entries); + + dataStorage_->ReleaseTransaction(transaction); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + for (auto &item : entries) { + Value userValue; + errCode = TransferToUserValue(item.value, userValue); + if (errCode != E_OK) { + entries.clear(); + entries.shrink_to_fit(); + break; + } + std::swap(userValue, item.value); + } + + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::Put(const Key &key, const Value &value) +{ + if (transaction_ == nullptr) { + return -E_INVALID_DB; + } + Value savedValue; + int errCode = TransferToSavedValue(value, savedValue); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + errCode = transaction_->Put(key, savedValue); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::Delete(const Key &key) +{ + if (transaction_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = transaction_->Delete(key); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::Clear() +{ + if (transaction_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = transaction_->Clear(); + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::StartAllDbTransaction() +{ + if (dataStorage_ == nullptr || commitStorage_ == nullptr) { + return -E_INVALID_DB; + } + + IKvDBMultiVerTransaction *transaction = nullptr; + int errCode = dataStorage_->StartWrite(KvDataType::KV_DATA_SYNC_P2P, transaction); + if (transaction == nullptr) { + LOGE("start write transaction failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + + // start data storage transaction + Version maxVersion = static_cast(kvDB_)->GetMaxCommitVersion(); + transaction->SetVersion(maxVersion); + + errCode = transaction->StartTransaction(); + if (errCode != E_OK) { + LOGE("Start dataStorage transaction failed:%d", errCode); + goto END; + } + + // start commit history transaction + errCode = commitStorage_->StartVacuum(); + if (errCode != E_OK) { + transaction->RollBackTransaction(); + LOGE("Start commitStorage transaction failed:%d", errCode); + goto END; + } + + // start slice data transaction + errCode = StartSliceTransaction(); + if (errCode != E_OK) { + transaction->RollBackTransaction(); + commitStorage_->CancelVacuum(); + LOGE("Start kvDataStorage transaction failed:%d", errCode); + goto END; + } + transaction_ = transaction; +END: + if (errCode != E_OK) { + dataStorage_->ReleaseTransaction(transaction); + transaction = nullptr; + transaction_ = nullptr; + return CheckCorruptedStatus(errCode); + } + + return errCode; +} + +int MultiVerStorageExecutor::StartTransaction(MultiTransactionType type) +{ + if (type == MultiTransactionType::ALL_DATA) { + return StartAllDbTransaction(); + } + + if (dataStorage_ == nullptr) { + return -E_INVALID_DB; + } + IKvDBMultiVerTransaction *transaction = nullptr; + int errCode = dataStorage_->StartWrite(KvDataType::KV_DATA_SYNC_P2P, transaction); + if (transaction == nullptr) { + LOGE("start write transaction failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + + // Get the current max version, and the current version is max version + 1. + Version maxVersion = static_cast(kvDB_)->GetMaxCommitVersion(); + transaction->SetVersion(++maxVersion); + errCode = transaction->StartTransaction(); + if (errCode != E_OK) { + dataStorage_->ReleaseTransaction(transaction); + transaction = nullptr; + LOGE("Start transaction failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + transaction_ = transaction; + return E_OK; +} + +int MultiVerStorageExecutor::CommitAllDbTransaction() +{ + if (dataStorage_ == nullptr || commitStorage_ == nullptr || transaction_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = transaction_->CommitTransaction(); + if (errCode != E_OK) { + (void)(RollbackSliceTransaction()); + commitStorage_->CancelVacuum(); + LOGE("commit phase one failed:%d", errCode); + goto END; + } + + // start slice data transaction + errCode = CommitSliceTransaction(); + if (errCode != E_OK) { + commitStorage_->CancelVacuum(); + LOGE("Finish kvDataStorage transaction failed:%d", errCode); + goto END; + } + + // start commit history transaction + errCode = commitStorage_->FinishlVacuum(); + if (errCode != E_OK) { + LOGE("Finish commitStorage transaction failed:%d", errCode); + goto END; + } + +END: + dataStorage_->ReleaseTransaction(transaction_); + transaction_ = nullptr; + + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::CommitTransaction(MultiTransactionType type) +{ + if (type == MultiTransactionType::ALL_DATA) { + return CommitAllDbTransaction(); + } + + if ((dataStorage_ == nullptr) || (transaction_ == nullptr)) { + return -E_INVALID_DB; + } + UpdateVerTimestamp multiVerTimestamp = {static_cast(kvDB_)->GetCurrentTimestamp(), true}; + Version commitVersion; + CommitID commitId; + int errCode = E_OK; + bool isDataChanged = transaction_->IsDataChanged(); + if (!isDataChanged) { + transaction_->RollBackTransaction(); + goto END; + } + + errCode = dataStorage_->CommitWritePhaseOne(transaction_, multiVerTimestamp); + if (errCode != E_OK) { + LOGE("commit phase one failed:%d", errCode); + goto END; + } + + commitVersion = transaction_->GetVersion(); + errCode = FillAndCommitLogEntry(commitVersion, commitId, multiVerTimestamp.timestamp); + if (errCode != E_OK) { + LOGE("rollback commit phase one failed:%d", errCode); + dataStorage_->RollbackWritePhaseOne(transaction_, commitVersion); + goto END; + } + LOGD("local commit version:%" PRIu64, commitVersion); + static_cast(kvDB_)->SetMaxTimestamp(multiVerTimestamp.timestamp); + dataStorage_->CommitWritePhaseTwo(transaction_); + static_cast(kvDB_)->SetMaxCommitVersion(commitVersion); +END: + dataStorage_->ReleaseTransaction(transaction_); + transaction_ = nullptr; + if (errCode == E_OK && isDataChanged) { + CommitNotifiedData(commitId); + } + + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::RollBackAllDbTransaction() +{ + if ((dataStorage_ == nullptr) || (commitStorage_ == nullptr)) { + return -E_INVALID_DB; + } + int errCode = dataStorage_->RollbackWrite(transaction_); + if (errCode != E_OK) { + LOGE("Data storage rollback fail!"); + (void)(commitStorage_->CancelVacuum()); + (void)(RollbackSliceTransaction()); + goto END; + } + + errCode = commitStorage_->CancelVacuum(); + if (errCode != E_OK) { + LOGE("Commit storage rollback fail!"); + (void)(RollbackSliceTransaction()); + goto END; + } + + errCode = RollbackSliceTransaction(); + if (errCode != E_OK) { + LOGE("Value slice rollback fail!"); + } + +END: + dataStorage_->ReleaseTransaction(transaction_); + transaction_ = nullptr; + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::RollBackTransaction(MultiTransactionType type) +{ + if (dataStorage_ == nullptr || transaction_ == nullptr) { + LOGE("invalid transaction for rollback"); + return -E_INVALID_DB; + } + + if (type == MultiTransactionType::ALL_DATA) { + return RollBackAllDbTransaction(); + } + + int errCode = dataStorage_->RollbackWrite(transaction_); + dataStorage_->ReleaseTransaction(transaction_); + transaction_ = nullptr; + return CheckCorruptedStatus(errCode); +} + +void MultiVerStorageExecutor::Close() +{ + MultiVerStorageExecutor *handle = this; + + MultiVerNaturalStore *multiVerNatureStore = static_cast(kvDB_); + if (multiVerNatureStore == nullptr) { + return; + } + + if (readVersion_ != 0) { + multiVerNatureStore->RemoveVersionConstraintFromList(readVersion_); + readVersion_ = 0; + } + multiVerNatureStore->ReleaseHandle(handle); +} + +int MultiVerStorageExecutor::InitCurrentReadVersion() +{ + if (commitStorage_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = E_OK; + CommitID commitId = commitStorage_->GetHeader(errCode); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + Version version = 0; + // if no head, just use the initial version. + if (!commitId.empty()) { + IKvDBCommit *commit = commitStorage_->GetCommit(commitId, errCode); + if (commit == nullptr) { + LOGE("get the header commit failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + + version = commit->GetCommitVersion(); + commitStorage_->ReleaseCommit(commit); + commit = nullptr; + } + readVersion_ = version; + return E_OK; +} + +int MultiVerStorageExecutor::TransferDiffEntries(MultiVerDiffData &data) const +{ + int errCode; + Value valueTmp; + for (auto &insertedItem : data.inserted) { + errCode = TransferToUserValue(insertedItem.value, valueTmp); + if (errCode != E_OK) { + return errCode; + } + std::swap(insertedItem.value, valueTmp); + } + + for (auto &updatedItem : data.updated) { + errCode = TransferToUserValue(updatedItem.value, valueTmp); + if (errCode != E_OK) { + return errCode; + } + std::swap(updatedItem.value, valueTmp); + } + + for (auto &deletedItem : data.deleted) { + errCode = TransferToUserValue(deletedItem.value, valueTmp); + if (errCode != E_OK) { + return errCode; + } + std::swap(deletedItem.value, valueTmp); + } + + return E_OK; +} + +int MultiVerStorageExecutor::TransferToUserValue(const Value &savedValue, Value &value) const +{ + MultiVerValueObject valueObject; + int errCode = valueObject.DeSerialData(savedValue); + if (errCode != E_OK) { + LOGE("Deserialize the multi ver saved value failed:%d", errCode); + return errCode; + } + if (!valueObject.IsHash()) { + return valueObject.GetValue(value); + } + + std::vector sliceHashVect; + errCode = valueObject.GetValueHash(sliceHashVect); + if (errCode != E_OK) { + return errCode; + } + value.clear(); + value.shrink_to_fit(); + for (const auto &item : sliceHashVect) { + Value itemValue; + errCode = GetValueSlice(item, itemValue); + if (errCode != E_OK) { + LOGE("Get hash entry error:%d", errCode); + break; + } + value.insert(value.end(), itemValue.begin(), itemValue.end()); + } + + return errCode; +} + +int MultiVerStorageExecutor::TransferToValueObject(const Value &value, MultiVerValueObject &valueObject) +{ + MultiVerNaturalStoreTransferData splitData; + std::vector partValues; + // Segment data into blocks by fixed size + // You can set Threshold and blocksize by SetSliceLengthThreshold, SetBlockSizeByte; + int errCode = splitData.SegmentAndTransferValueToHash(value, partValues); + if (errCode == E_OK) { + valueObject.SetFlag(MultiVerValueObject::HASH_FLAG); + + // Tansfer blocks data to hash value list + std::vector hashValues; + ValueSliceHash hashValue; + for (const auto &partValue : partValues) { + if (DBCommon::CalcValueHash(partValue, hashValue) != E_OK) { + return -E_INTERNAL_ERROR; + } + // Put hash value into table + errCode = PutValueSlice(hashValue, partValue, true); + if (errCode != E_OK) { + return errCode; + } + hashValues.push_back(std::move(hashValue)); + } + + valueObject.SetValueHash(hashValues); + } else { + valueObject.SetFlag(0); + valueObject.SetValue(value); + } + valueObject.SetDataLength(value.size()); + return E_OK; +} + +int MultiVerStorageExecutor::TransferToSavedValue(const Value &value, Value &savedValue) +{ + MultiVerValueObject valueObject; + int errCode = TransferToValueObject(value, valueObject); + if (errCode != E_OK) { + LOGE("Failed to get the serialize data of value object:%d", errCode); + return errCode; + } + + errCode = valueObject.GetSerialData(savedValue); + if (errCode != E_OK) { + LOGE("failed to get the serialize data of savedValue:%d", errCode); + return errCode; + } + + return E_OK; +} + +int MultiVerStorageExecutor::GetResolvedConflictEntries(const MultiVerCommitNode &commitItem, + std::vector &entries) const +{ + if (commitStorage_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = E_OK; + auto commit = commitStorage_->GetCommit(commitItem.commitId, errCode); + if (commit == nullptr) { + LOGE("failed to get the commit in merge:%d", errCode); + return errCode; + } + entries.clear(); + entries.shrink_to_fit(); + Version version = commit->GetCommitVersion(); + LOGD("Version is %" PRIu64, version); + if (transaction_ != nullptr) { + errCode = transaction_->GetEntriesByVersion(version, entries); + if (errCode != E_OK) { + LOGE("failed to get the entries by version:%d", errCode); + } + } + commitStorage_->ReleaseCommit(commit); + return errCode; +} + +void MultiVerStorageExecutor::CommitNotifiedData(const CommitID &commitId) +{ + CommitID startId; + Version currentVersion; + int errCode = GetParentCommitId(commitId, startId, currentVersion); + if (errCode != E_OK || currentVersion == 0) { // make sure that the version - 1 is valid. + LOGE("Notify: get the parent commit failed:%d", errCode); + return; + } + MultiVerNaturalStoreCommitNotifyData *committedData = + new (std::nothrow) MultiVerNaturalStoreCommitNotifyData( + static_cast(kvDB_), startId, commitId, currentVersion - 1); + if (committedData != nullptr) { + static_cast(kvDB_)->AddVersionConstraintToList(currentVersion - 1); + static_cast(kvDB_)->CommitNotify(NATURAL_STORE_COMMIT_EVENT, committedData); + committedData->DecObjRef(committedData); + committedData = nullptr; + } else { + LOGE("Failed to do commit notify because of OOM."); + } +} + +int MultiVerStorageExecutor::GetParentCommitId(const CommitID &commitId, CommitID &parentId, Version &curVersion) const +{ + if (commitStorage_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = E_OK; + IKvDBCommit *commit = commitStorage_->GetCommit(commitId, errCode); + if (commit == nullptr) { + LOGE("Get commit failed while getting the parent id:%d", errCode); + return CheckCorruptedStatus(errCode); + } + + parentId = commit->GetLeftParentId(); + curVersion = commit->GetCommitVersion(); + commitStorage_->ReleaseCommit(commit); + commit = nullptr; + return E_OK; +} + +int MultiVerStorageExecutor::AllocNewCommitId(CommitID &commitId) const +{ + // Only for allocate for temporary. + commitId.resize(COMMIT_ID_LENGTH); + RAND_bytes(commitId.data(), COMMIT_ID_LENGTH); + return E_OK; +} + +int MultiVerStorageExecutor::FillAndCommitLogEntry(const Version &versionInfo, CommitID &commitId, + uint64_t timestamp) const +{ + if (kvDB_ == nullptr || commitStorage_ == nullptr) { + return -E_INVALID_DB; + } + // Get the commit id. + int errCode = E_OK; + IKvDBCommit *commit = commitStorage_->AllocCommit(errCode); + if (commit == nullptr) { + LOGE("Failed to alloc the commit locally:%d", errCode); + return errCode; + } + + (void)(AllocNewCommitId(commitId)); + std::vector vectTag; + static_cast(kvDB_)->GetCurrentTag(vectTag); + std::string strTag(vectTag.begin(), vectTag.end()); + + // Get the commit struct. + CommitID header = commitStorage_->GetHeader(errCode); + if (errCode != E_OK) { + goto END; + } + + commit->SetLeftParentId(header); + commit->SetCommitId(commitId); + commit->SetCommitVersion(versionInfo); + commit->SetLocalFlag(true); + commit->SetTimestamp(timestamp); + commit->SetDeviceInfo(strTag); + + // write the commit history. + errCode = commitStorage_->AddCommit(*commit, true); + if (errCode != E_OK) { + LOGE("Add commit history failed:%d", errCode); + } + +END: + if (commit != nullptr) { + commitStorage_->ReleaseCommit(commit); + commit = nullptr; + } + return errCode; +} + +int MultiVerStorageExecutor::FillCommitByForeign(IKvDBCommit *commit, + const MultiVerCommitNode &multiVerCommit, const Version &versionInfo, const CommitID &commitId, bool isMerge) const +{ + if (isMerge) { + if (commitStorage_ == nullptr || kvDB_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + CommitID header = commitStorage_->GetHeader(errCode); + if (errCode != E_OK) { + return errCode; + } + std::vector vectTag; + static_cast(kvDB_)->GetCurrentTag(vectTag); + std::string strTag(vectTag.begin(), vectTag.end()); + + commit->SetCommitId(commitId); + commit->SetLeftParentId(header); + commit->SetRightParentId(multiVerCommit.commitId); + commit->SetLocalFlag(true); + Timestamp timestamp = static_cast(kvDB_)->GetCurrentTimestamp(); + commit->SetTimestamp(timestamp); + commit->SetDeviceInfo(strTag); + } else { + commit->SetCommitId(multiVerCommit.commitId); + commit->SetLeftParentId(multiVerCommit.leftParent); + commit->SetRightParentId(multiVerCommit.rightParent); + commit->SetTimestamp(multiVerCommit.timestamp); + commit->SetLocalFlag(false); + commit->SetDeviceInfo(multiVerCommit.deviceInfo); + } + + commit->SetCommitVersion(versionInfo); + return E_OK; +} + +int MultiVerStorageExecutor::FillAndCommitLogEntry(const Version &versionInfo, + const MultiVerCommitNode &multiVerCommit, CommitID &commitId, bool isMerge, Timestamp ×tamp) const +{ + if (commitStorage_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = E_OK; + IKvDBCommit *commit = commitStorage_->AllocCommit(errCode); + if (commit == nullptr) { + return errCode; + } + + if (isMerge) { + (void)(AllocNewCommitId(commitId)); + } + + errCode = FillCommitByForeign(commit, multiVerCommit, versionInfo, commitId, isMerge); + if (errCode != E_OK) { + LOGE("Failed to fill the sync commit:%d", errCode); + goto END; + } + + timestamp = isMerge ? static_cast(kvDB_)->GetCurrentTimestamp() : multiVerCommit.timestamp; + commit->SetTimestamp(timestamp); + + // write the commit history. + errCode = commitStorage_->AddCommit(*commit, isMerge); + if (errCode != E_OK) { + LOGE("Add commit history failed:%d", errCode); + } +END: + if (commit != nullptr) { + commitStorage_->ReleaseCommit(commit); + commit = nullptr; + } + + return errCode; +} + +int MultiVerStorageExecutor::CommitTransaction(const MultiVerCommitNode &multiVerCommit, bool isMerge) +{ + if ((transaction_ == nullptr) || (dataStorage_ == nullptr)) { + LOGE("invalid transaction for commit"); + return -E_INVALID_DB; + } + + Version commitVersion; + CommitID commitId; + UpdateVerTimestamp multiVerTimestamp = {0ull, false}; + bool isDataChanged = transaction_->IsDataChanged(); + + int errCode = dataStorage_->CommitWritePhaseOne(transaction_, multiVerTimestamp); + if (errCode != E_OK) { + LOGE("commit phase one failed:%d", errCode); + goto END; + } + + commitVersion = transaction_->GetVersion(); + errCode = FillAndCommitLogEntry(commitVersion, multiVerCommit, commitId, isMerge, multiVerTimestamp.timestamp); + if (errCode != E_OK) { + LOGE("rollback commit phase one failed:%d", errCode); + dataStorage_->RollbackWritePhaseOne(transaction_, commitVersion); + goto END; + } + + dataStorage_->CommitWritePhaseTwo(transaction_); + static_cast(kvDB_)->SetMaxTimestamp(multiVerTimestamp.timestamp); + static_cast(kvDB_)->SetMaxCommitVersion(commitVersion); + LOGD("sync commit version:%" PRIu64, commitVersion); +END: + dataStorage_->ReleaseTransaction(transaction_); + transaction_ = nullptr; + + if (errCode == E_OK && isMerge && isDataChanged) { + CommitNotifiedData(commitId); + } + + return CheckCorruptedStatus(errCode); +} + +void MultiVerStorageExecutor::ReleaseMultiVerKvEntries(std::vector &entries) +{ + for (auto &item : entries) { + if (item != nullptr) { + delete item; + item = nullptr; + } + } + entries.clear(); + entries.shrink_to_fit(); +} + +Version MultiVerStorageExecutor::GetCurrentReadVersion() const +{ + return readVersion_; +} + +int MultiVerStorageExecutor::GetAllCommitsInTree(std::list &commits) const +{ + if (commitStorage_ == nullptr) { + return -E_INVALID_DB; + } + + return commitStorage_->GetAllCommitsInTree(commits); +} + +int MultiVerStorageExecutor::GetEntriesByVersion(Version version, std::list &data) const +{ + if (dataStorage_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + IKvDBMultiVerTransaction *transaction = nullptr; + if (transaction_ == nullptr) { + transaction = dataStorage_->StartRead(KvDataType::KV_DATA_SYNC_P2P, version, errCode); + if (transaction == nullptr) { + LOGE("Failed to get the transaction:%d", errCode); + goto END; + } + } else { + transaction = transaction_; + } + + // Note that the transaction fails and the parameters are empty. + errCode = transaction->GetEntriesByVersion(version, data); +END: + if (transaction != transaction_) { + dataStorage_->ReleaseTransaction(transaction); + transaction = nullptr; + } + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::GetOverwrittenClearTypeEntries(Version clearVersion, + std::list &data) const +{ + if (dataStorage_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + IKvDBMultiVerTransaction *transaction = nullptr; + if (transaction_ == nullptr) { + transaction = dataStorage_->StartRead(KvDataType::KV_DATA_SYNC_P2P, clearVersion, errCode); + if (transaction == nullptr) { + LOGE("Failed to get the transaction:%d", errCode); + goto END; + } + } else { + transaction = transaction_; + } + + errCode = transaction->GetOverwrittenClearTypeEntries(clearVersion, data); +END: + if (transaction != transaction_) { + dataStorage_->ReleaseTransaction(transaction); + transaction = nullptr; + } + + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::GetOverwrittenNonClearTypeEntries(Version version, const Key &hashKey, + std::list &data) const +{ + if (dataStorage_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + IKvDBMultiVerTransaction *transaction = nullptr; + if (transaction_ == nullptr) { + transaction = dataStorage_->StartRead(KvDataType::KV_DATA_SYNC_P2P, version, errCode); + if (transaction == nullptr) { + LOGE("Failed to get the transaction:%d", errCode); + goto END; + } + } else { + transaction = transaction_; + } + + errCode = transaction->GetOverwrittenNonClearTypeEntries(version, hashKey, data); +END: + if (transaction != transaction_) { + dataStorage_->ReleaseTransaction(transaction); + transaction = nullptr; + } + + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::DeleteEntriesByHashKey(Version version, const Key &hashKey) +{ + if (transaction_ == nullptr) { + LOGI("You need start transaction before this operation!"); + return -E_NOT_PERMIT; + } + + Value savedValue; + int errCode = transaction_->GetValueForTrimSlice(hashKey, version, savedValue); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + errCode = transaction_->DeleteEntriesByHashKey(version, hashKey); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + MultiVerValueObject valueObject; + errCode = valueObject.DeSerialData(savedValue); + // savedValue empty is del or clear record + if (!valueObject.IsHash() || savedValue.empty()) { + return E_OK; + } + if (errCode != E_OK) { + return errCode; + } + + std::vector sliceHashVect; + errCode = valueObject.GetValueHash(sliceHashVect); + if (errCode != E_OK) { + return errCode; + } + + for (const auto &item : sliceHashVect) { + errCode = DeleteValueSliceInner(sliceTransaction_, item); + if (errCode != E_OK) { + LOGI("Value slice delete fail!"); + break; + } + } + + return CheckCorruptedStatus(errCode); +} + +int MultiVerStorageExecutor::UpdateTrimedFlag(Version version, const Key &hashKey) +{ + (void)version; + (void)hashKey; + return E_OK; +} + +int MultiVerStorageExecutor::UpdateTrimedFlag(const CommitID &commit) +{ + (void)commit; + return E_OK; +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_storage_executor.h b/mock/distributeddb/storage/src/multiver/multi_ver_storage_executor.h new file mode 100644 index 0000000000000000000000000000000000000000..4b3f99c65cfededd3961d5dc6b420760cd8a3e60 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_storage_executor.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_STORAGE_EXECUTOR_H +#define MULTI_VER_STORAGE_EXECUTOR_H + +#ifndef OMIT_MULTI_VER +#include "storage_executor.h" +#include "ikvdb.h" +#include "ikvdb_commit_storage.h" +#include "ikvdb_multi_ver_data_storage.h" +#include "macro_utils.h" +#include "multi_ver_kvdata_storage.h" + +namespace DistributedDB { +enum class MultiTransactionType { + NORMAL_DATA, + ALL_DATA, +}; + +class MultiVerStorageExecutor : public StorageExecutor { +public: + MultiVerStorageExecutor(IKvDB *kvDB, IKvDBMultiVerDataStorage *dataStorage, IKvDBCommitStorage *commitStorage, + MultiVerKvDataStorage *kvDataStorage, bool writable); + ~MultiVerStorageExecutor() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(MultiVerStorageExecutor); + + int Reset() override; + + int Put(const Key &key, const Value &value); + + int Get(const Key &key, Value &value) const; + + int GetEntries(const Key &keyPrefix, std::vector &entries) const; + + int Delete(const Key &key); + + int Clear(); + + int PutMetaData(const Key &key, const Value &value); + + int GetMetaData(const Key &key, Value &value) const; + + int GetDeviceLatestCommit(std::map &commitMap) const; + + int GetCommitTree(const std::map &commitMap, + std::vector &commits) const; + + bool IsCommitExisted(const MultiVerCommitNode &commit, int &errCode) const; + + int GetCommitData(const MultiVerCommitNode &commit, std::vector &entries) const; + + bool IsValueSliceExisted(const ValueSliceHash &value, int &errCode) const; + + int GetValueSlice(const ValueSliceHash &hashValue, ValueSlice &sliceValue) const; + + int PutValueSlice(const ValueSliceHash &hashValue, const ValueSlice &sliceValue, bool isAddCount); + + int PutCommitData(const MultiVerCommitNode &commit, const std::vector &entries, + const std::string &deviceName); + + int MergeSyncCommit(const MultiVerCommitNode &commit, const std::vector &commits); + + int GetDiffEntries(const CommitID &begin, const CommitID &end, MultiVerDiffData &data) const; + + int StartTransaction(MultiTransactionType type = MultiTransactionType::NORMAL_DATA); + + int CommitTransaction(MultiTransactionType type = MultiTransactionType::NORMAL_DATA); + + int RollBackTransaction(MultiTransactionType type = MultiTransactionType::NORMAL_DATA); + + int InitCurrentReadVersion(); + + void Close(); + + Version GetCurrentReadVersion() const; + + // Get all the commits with the view of one commit. + int GetAllCommitsInTree(std::list &commits) const; + + // Get all the hash key of one version. + int GetEntriesByVersion(Version version, std::list &data) const; + + // Get all the overwritten record whose version is less than the specified version and tag is less the cleard data. + int GetOverwrittenClearTypeEntries(Version clearVersion, std::list &data) const; + + // Get all the overwritten non-cleared record whose version is less than the specified version. + int GetOverwrittenNonClearTypeEntries(Version version, const Key &hashKey, + std::list &data) const; + + // Delete the data whose hash key is equal to the hashKey and version is less than the specified. + int DeleteEntriesByHashKey(Version version, const Key &hashKey); + + // Update the trimmed flag for the hash key with the specified version. + int UpdateTrimedFlag(Version version, const Key &hashKey); + + // Update the trimmed flag for the commit. + int UpdateTrimedFlag(const CommitID &commit); + +private: + static void ReleaseMultiVerKvEntries(std::vector &entries); + + int GetSliceCount(std::vector &&entries, uint32_t &count) const; + + int PutSliceCount(const Key &sliceKey, uint32_t count) const; + + int CommitTransaction(const MultiVerCommitNode &multiVerCommit, bool isMerge); + + int GetResolvedConflictEntries(const MultiVerCommitNode &commitItem, std::vector &entries) const; + + int TransferDiffEntries(MultiVerDiffData &data) const; + + int TransferToUserValue(const Value &savedValue, Value &value) const; + + int TransferToSavedValue(const Value &value, Value &savedValue); + + void CommitNotifiedData(const CommitID &commitId); + + int GetParentCommitId(const CommitID &commitId, CommitID &parentId, Version &curVersion) const; + + int AllocNewCommitId(CommitID &commitId) const; + + int FillAndCommitLogEntry(const Version &versionInfo, CommitID &commitId, uint64_t timestamp) const; + + int FillCommitByForeign(IKvDBCommit *commit, const MultiVerCommitNode &multiVerCommit, + const Version &versionInfo, const CommitID &commitId, bool isMerge) const; + + int FillAndCommitLogEntry(const Version &versionInfo, const MultiVerCommitNode &multiVerCommit, + CommitID &commitId, bool isMerge, Timestamp ×tamp) const; + + int MergeOneCommit(const MultiVerCommitNode &commit); + + int MergeCommits(const std::vector &commits); + + int CommitSyncCommits(); + + int StartAllDbTransaction(); + + int TransferToValueObject(const Value &value, MultiVerValueObject &valueObject); + + int RollBackAllDbTransaction(); + + int CommitAllDbTransaction(); + + int ReInitTransactionVersion(const MultiVerCommitNode &commit); + + int StartSliceTransaction(); + + int CommitSliceTransaction(); + + int RollbackSliceTransaction(); + + int GetValueSliceInner(const SliceTransaction *sliceTransaction, const ValueSliceHash &hashValue, + ValueSlice &sliceValue) const; + + int PutValueSliceInner(SliceTransaction *sliceTransaction, const ValueSliceHash &hashValue, + const ValueSlice &sliceValue, bool isAddCount); + + int DeleteValueSliceInner(SliceTransaction *sliceTransaction, const ValueSliceHash &hashValue); + + int AddSliceDataCount(const std::vector &values); + + static const int COMMIT_ID_LENGTH = 20; + IKvDB *kvDB_; + IKvDBMultiVerDataStorage *dataStorage_; + IKvDBCommitStorage *commitStorage_; + MultiVerKvDataStorage *kvDataStorage_; + IKvDBMultiVerTransaction *transaction_; + SliceTransaction *sliceTransaction_; + Version readVersion_ = 0; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_STORAGE_EXECUTOR_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_vacuum.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_vacuum.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9014b4707a9c5aecd9a906eeed347ab2c0b08db9 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_vacuum.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_vacuum.h" + +#include +#include +#include + +#include "db_errno.h" +#include "db_common.h" +#include "log_print.h" +#include "macro_utils.h" +#include "runtime_context.h" + +namespace DistributedDB { +std::atomic MultiVerVacuum::enabled_{true}; + +void MultiVerVacuum::Enable(bool isEnable) +{ + enabled_ = isEnable; +} + +int MultiVerVacuum::Launch(const std::string &dbIdentifier, MultiVerVacuumExecutor *dbHandle) +{ + if (!enabled_) { + LOGW("[Vacuum][Launch] Functionality Not Enabled!"); + return E_OK; + } + if (dbIdentifier.empty() || dbHandle == nullptr) { + return -E_INVALID_ARGS; + } + + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + if (dbMapVacuumTask_.count(dbIdentifier) == 0) { + dbMapVacuumTask_[dbIdentifier].runWaitOrder = incRunWaitOrder_++; + dbMapVacuumTask_[dbIdentifier].databaseHandle = dbHandle; + } else if (dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::ABORT_DONE || + dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::FINISH) { + // Reset vacuum task + dbMapVacuumTask_[dbIdentifier].status = VacuumTaskStatus::RUN_WAIT; + dbMapVacuumTask_[dbIdentifier].launchErrorHappen = false; + dbMapVacuumTask_[dbIdentifier].autoRelaunchOnce = false; + dbMapVacuumTask_[dbIdentifier].immediatelyRelaunchable = true; + dbMapVacuumTask_[dbIdentifier].runWaitOrder = incRunWaitOrder_++; + dbMapVacuumTask_[dbIdentifier].pauseNeedCount = 0; + dbMapVacuumTask_[dbIdentifier].databaseHandle = dbHandle; + } else { + dbMapVacuumTask_[dbIdentifier].launchErrorHappen = true; + LOGE("[Vacuum][Launch] Unexpected pre-status=%d!", static_cast(dbMapVacuumTask_[dbIdentifier].status)); + return -E_NOT_PERMIT; + } + ActivateBackgroundVacuumTaskExecution(); + return E_OK; +} + +int MultiVerVacuum::Pause(const std::string &dbIdentifier) +{ + if (!enabled_) { + return E_OK; + } + if (dbIdentifier.empty()) { + return -E_INVALID_ARGS; + } + + std::unique_lock vacuumTaskLockGuard(vacuumTaskMutex_); + if (dbMapVacuumTask_.count(dbIdentifier) == 0) { + return -E_NOT_FOUND; + } else if (dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::RUN_WAIT || + dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::PAUSE_DONE) { + dbMapVacuumTask_[dbIdentifier].status = VacuumTaskStatus::PAUSE_DONE; + dbMapVacuumTask_[dbIdentifier].immediatelyRelaunchable = false; + IncPauseNeedCount(dbMapVacuumTask_[dbIdentifier]); + } else if (dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::RUN_NING || + dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::PAUSE_WAIT) { + dbMapVacuumTask_[dbIdentifier].status = VacuumTaskStatus::PAUSE_WAIT; + dbMapVacuumTask_[dbIdentifier].immediatelyRelaunchable = false; + IncPauseNeedCount(dbMapVacuumTask_[dbIdentifier]); + vacuumTaskCv_.wait(vacuumTaskLockGuard, [this, &dbIdentifier] { + // In concurrency scenario that executor is about to finish this task, the final status may be FINISH. + // Even more, in case Abort be called immediately after task finished, the final status may be ABORT_DONE. + return dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::PAUSE_DONE || + dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::ABORT_DONE || + dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::FINISH; + }); + } else if (dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::FINISH) { + dbMapVacuumTask_[dbIdentifier].immediatelyRelaunchable = false; + IncPauseNeedCount(dbMapVacuumTask_[dbIdentifier]); + } else { + LOGE("[Vacuum][Pause] Unexpected pre-status=%d!", static_cast(dbMapVacuumTask_[dbIdentifier].status)); + return -E_NOT_PERMIT; + } + return E_OK; +} + +int MultiVerVacuum::Continue(const std::string &dbIdentifier, bool autoRelaunchOnce) +{ + if (!enabled_) { + return E_OK; + } + if (dbIdentifier.empty()) { + return -E_INVALID_ARGS; + } + + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + if (dbMapVacuumTask_.count(dbIdentifier) == 0) { + return -E_NOT_FOUND; + } else if (dbMapVacuumTask_[dbIdentifier].launchErrorHappen) { + LOGE("[Vacuum][Continue] LaunchErrorHappen detected, pre-status=%d!", + static_cast(dbMapVacuumTask_[dbIdentifier].status)); + return -E_NOT_PERMIT; + } else if (dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::PAUSE_DONE) { + DecPauseNeedCount(dbMapVacuumTask_[dbIdentifier]); + bool relaunchFlag = (dbMapVacuumTask_[dbIdentifier].autoRelaunchOnce || autoRelaunchOnce); + dbMapVacuumTask_[dbIdentifier].autoRelaunchOnce = relaunchFlag; + // Truly continue this task only when all pause had been counteracted + if (IsPauseNotNeed(dbMapVacuumTask_[dbIdentifier])) { + dbMapVacuumTask_[dbIdentifier].status = VacuumTaskStatus::RUN_WAIT; + dbMapVacuumTask_[dbIdentifier].runWaitOrder = incRunWaitOrder_++; + dbMapVacuumTask_[dbIdentifier].immediatelyRelaunchable = true; + ActivateBackgroundVacuumTaskExecution(); + } + } else if (dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::FINISH) { + // Update relaunch flag first + DecPauseNeedCount(dbMapVacuumTask_[dbIdentifier]); + bool relaunchFlag = (dbMapVacuumTask_[dbIdentifier].autoRelaunchOnce || autoRelaunchOnce); + dbMapVacuumTask_[dbIdentifier].autoRelaunchOnce = relaunchFlag; + // All pause had been counteracted, so this task is immediatelyRelaunchable, but not necessarily relaunch now. + if (IsPauseNotNeed(dbMapVacuumTask_[dbIdentifier])) { + dbMapVacuumTask_[dbIdentifier].immediatelyRelaunchable = true; + // Do autoRelaunch if need + if (dbMapVacuumTask_[dbIdentifier].autoRelaunchOnce) { + dbMapVacuumTask_[dbIdentifier].status = VacuumTaskStatus::RUN_WAIT; + dbMapVacuumTask_[dbIdentifier].runWaitOrder = incRunWaitOrder_++; + dbMapVacuumTask_[dbIdentifier].autoRelaunchOnce = false; + ActivateBackgroundVacuumTaskExecution(); + } + } + } else { + LOGE("[Vacuum][Continue] Unexpected pre-status=%d!", static_cast(dbMapVacuumTask_[dbIdentifier].status)); + return -E_NOT_PERMIT; + } + return E_OK; +} + +int MultiVerVacuum::Abort(const std::string &dbIdentifier) +{ + if (!enabled_) { + return E_OK; + } + if (dbIdentifier.empty()) { + return -E_INVALID_ARGS; + } + + // The pauseNeedCount must be zero in RUN_WAIT and RUN_NING case, but not always zero in FINISH case. + // If pause is called more than continue, status may be PAUSE_WAIT, PAUSE_DONE, which is not expected. + // The pauseNeedCount, runWaitOrder and autoRelaunchOnce will be reset when launch(Not Auto) if abort normally + std::unique_lock vacuumTaskLockGuard(vacuumTaskMutex_); + if (dbMapVacuumTask_.count(dbIdentifier) == 0) { + return -E_NOT_FOUND; + } else if (dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::RUN_WAIT || + dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::PAUSE_DONE || + dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::FINISH) { + dbMapVacuumTask_[dbIdentifier].status = VacuumTaskStatus::ABORT_DONE; + dbMapVacuumTask_[dbIdentifier].launchErrorHappen = false; + dbMapVacuumTask_[dbIdentifier].immediatelyRelaunchable = false; + // In this place, the background will not access information of this vacuum task + dbMapVacuumTask_[dbIdentifier].databaseHandle = nullptr; + ResetNodeAndRecordContextInfo(dbMapVacuumTask_[dbIdentifier]); + } else if (dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::RUN_NING || + dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::PAUSE_WAIT) { + dbMapVacuumTask_[dbIdentifier].status = VacuumTaskStatus::ABORT_WAIT; + dbMapVacuumTask_[dbIdentifier].immediatelyRelaunchable = false; + vacuumTaskCv_.wait(vacuumTaskLockGuard, [this, &dbIdentifier] { + // In concurrency scenario that executor is about to finish this task, the final status may be FINISH + return dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::ABORT_DONE || + dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::FINISH; + }); + // Resource is cleaned by background task, still set ABORT_DONE and reset launchErrorHappen and databaseHandle. + dbMapVacuumTask_[dbIdentifier].status = VacuumTaskStatus::ABORT_DONE; + dbMapVacuumTask_[dbIdentifier].launchErrorHappen = false; + dbMapVacuumTask_[dbIdentifier].databaseHandle = nullptr; + } else { + LOGE("[Vacuum][Abort] Unexpected pre-status=%d!", static_cast(dbMapVacuumTask_[dbIdentifier].status)); + return -E_NOT_PERMIT; + } + return E_OK; +} + +int MultiVerVacuum::AutoRelaunchOnce(const std::string &dbIdentifier) +{ + if (!enabled_) { + return E_OK; + } + if (dbIdentifier.empty()) { + return -E_INVALID_ARGS; + } + + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + if (dbMapVacuumTask_.count(dbIdentifier) == 0) { + return -E_NOT_FOUND; + } else if (dbMapVacuumTask_[dbIdentifier].launchErrorHappen) { + LOGE("[Vacuum][AutoRelaunch] LaunchErrorHappen detected, pre-status=%d!", + static_cast(dbMapVacuumTask_[dbIdentifier].status)); + return -E_NOT_PERMIT; + } else if (dbMapVacuumTask_[dbIdentifier].status == VacuumTaskStatus::FINISH && + dbMapVacuumTask_[dbIdentifier].immediatelyRelaunchable) { + // Relaunch this task immediately + dbMapVacuumTask_[dbIdentifier].status = VacuumTaskStatus::RUN_WAIT; + dbMapVacuumTask_[dbIdentifier].autoRelaunchOnce = false; + dbMapVacuumTask_[dbIdentifier].runWaitOrder = incRunWaitOrder_++; + } else { + // Set flag true in order to Relaunch this task once when it finish + dbMapVacuumTask_[dbIdentifier].autoRelaunchOnce = true; + } + ActivateBackgroundVacuumTaskExecution(); + return E_OK; +} + +int MultiVerVacuum::QueryStatus(const std::string &dbIdentifier, VacuumTaskStatus &outStatus) const +{ + if (dbIdentifier.empty()) { + return -E_INVALID_ARGS; + } + + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + if (dbMapVacuumTask_.count(dbIdentifier) == 0) { + return -E_NOT_FOUND; + } + + outStatus = dbMapVacuumTask_.at(dbIdentifier).status; + return E_OK; +} + +MultiVerVacuum::~MultiVerVacuum() +{ + // Mainly for stop the background task, resources automatically clean by this deconstruction + std::unique_lock vacuumTaskLockGuard(vacuumTaskMutex_); + for (auto &each : dbMapVacuumTask_) { + if (each.second.status == VacuumTaskStatus::RUN_WAIT || each.second.status == VacuumTaskStatus::PAUSE_DONE) { + // For RUN_WAIT and PAUSE_DONE, change to ABORT_DONE + each.second.status = VacuumTaskStatus::ABORT_DONE; + } else if (each.second.status == VacuumTaskStatus::RUN_NING || + each.second.status == VacuumTaskStatus::PAUSE_WAIT) { + // For RUN_NING and PAUSE_WAIT, change to ABORT_WAIT + each.second.status = VacuumTaskStatus::ABORT_WAIT; + } + // For ABORT_WAIT, ABORT_DONE and FINISH, remain as it is. + } + // Wait for background task to quit + vacuumTaskCv_.wait(vacuumTaskLockGuard, [this] { + return !isBackgroundVacuumTaskInExecution_; + }); +} + +void MultiVerVacuum::VacuumTaskExecutor() +{ + // Endless loop until nothing to do + while (true) { + std::string nextDatabase; + { + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + int errCode = SearchVacuumTaskToExecute(nextDatabase); + if (errCode != E_OK) { + LOGI("[Vacuum][Executor] No available task to execute, about to quit."); + isBackgroundVacuumTaskInExecution_ = false; + // Awake the deconstruction that background thread is about to quit + vacuumTaskCv_.notify_all(); + return; + } + } + // No thread will remove entry from dbMapVacuumTask_, so here is concurrency safe. + LOGI("[Vacuum][Executor] Execute vacuum task for database=%s.", nextDatabase.c_str()); + ExecuteSpecificVacuumTask(dbMapVacuumTask_[nextDatabase]); + // Awake foreground thread at this task switch point + vacuumTaskCv_.notify_all(); + } +} + +void MultiVerVacuum::ExecuteSpecificVacuumTask(VacuumTaskContext &inTask) +{ + // No other thread will access handle, node and record field of a RUN_NING, PAUSE_WAIT, ABORT_WAIT status task + // So it is concurrency safe to access or change these field without protection of lockguard + if (inTask.leftBranchCommits.empty() && inTask.rightBranchCommits.empty()) { + // Newly launched task + int errCode = inTask.databaseHandle->GetVacuumAbleCommits(inTask.leftBranchCommits, inTask.rightBranchCommits); + if (errCode != E_OK) { + LOGE("[Vacuum][Execute] GetVacuumAbleCommits fail, errCode=%d.", errCode); + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + FinishVaccumTask(inTask); + return; + } + } + + // Vacuum left branch first, since record of left branch will be synced out, more urgently + while (!inTask.leftBranchCommits.empty()) { + int errCode = DealWithLeftBranchCommit(inTask); + if (errCode != E_OK) { + return; + } + } + LOGD("[Vacuum][Execute] All vacuum able commits of left branch have been dealt with for this database!"); + + // Vacuum right branch later, since record of right branch will not be synced out, not so urgent + while (!inTask.rightBranchCommits.empty()) { + int errCode = DealWithRightBranchCommit(inTask); + if (errCode != E_OK) { + return; + } + } + LOGD("[Vacuum][Execute] All vacuum able commits of right branch have been dealt with for this database!"); + + // Commit changes before finish this task, if fail, just finish it(commit fail auto rollback) + int errCode = CommitTransactionIfNeed(inTask); + if (errCode != E_OK) { + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + FinishVaccumTask(inTask); + return; + } + + // Every commit of this task has been treated, consider finish or relaunch the task + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + if (inTask.status == VacuumTaskStatus::RUN_NING && inTask.autoRelaunchOnce) { + RelaunchVacuumTask(inTask); + } else { + // If in PAUSE_WAIT or ABORT_WAIT status, shall not relaunch it, just finish it to make sure it be unactive + // The autoRelaunchOnce will be set false, if need relaunch, the continue operation will set it true again + FinishVaccumTask(inTask); + } +} + +int MultiVerVacuum::DealWithLeftBranchCommit(VacuumTaskContext &inTask) +{ + return DoDealCommitOfLeftOrRight(inTask, inTask.leftBranchCommits, true); +} + +int MultiVerVacuum::DealWithLeftBranchVacuumNeedRecord(VacuumTaskContext &inTask) +{ + int errCode = DoCommitAndQuitIfWaitStatusObserved(inTask); + if (errCode != E_OK) { + return errCode; + } + // No other thread will access handle, node and record field of a RUN_NING, PAUSE_WAIT, ABORT_WAIT status task + // So it is concurrency safe to access or change these field without protection of lockguard + const MultiVerRecordInfo &record = inTask.vacuumNeedRecords.front(); + LOGD("[Vacuum][DealLeftRecord] Type=%" PRIu32 ", Version=%" PRIu64 ", HashKey=%s.", + static_cast(record.type), record.version, VEC_TO_STR(record.hashKey)); + if (inTask.shadowRecords.empty()) { + if (record.type == RecordType::CLEAR) { + errCode = inTask.databaseHandle->GetShadowRecordsOfClearTypeRecord(record.version, record.hashKey, + inTask.shadowRecords); + } else { + errCode = inTask.databaseHandle->GetShadowRecordsOfNonClearTypeRecord(record.version, record.hashKey, + inTask.shadowRecords); + } + if (errCode != E_OK) { + LOGE("[Vacuum][DealLeftRecord] GetShadowRecords fail, Type=%d, Version=%llu, HashKey=%s, errCode=%d.", + static_cast(record.type), ULL(record.version), VEC_TO_STR(record.hashKey), errCode); + DoRollBackAndFinish(inTask); + return errCode; + } + } + + while (!inTask.shadowRecords.empty()) { + errCode = DealWithLeftBranchShadowRecord(inTask); + if (errCode != E_OK) { + return errCode; + } + } + + // Every shadowRecords of this vacuumNeedRecord has been treated, mark this vacuumNeedRecord as vacuum done + errCode = StartTransactionIfNotYet(inTask); + if (errCode != E_OK) { + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + FinishVaccumTask(inTask); + return errCode; + } + errCode = inTask.databaseHandle->MarkRecordAsVacuumDone(record.version, record.hashKey); + if (errCode != E_OK) { + LOGE("[Vacuum][DealLeftRecord] MarkRecordAsVacuumDone fail, Type=%d, Version=%llu, HashKey=%s, errCode=%d.", + static_cast(record.type), ULL(record.version), VEC_TO_STR(record.hashKey), errCode); + DoRollBackAndFinish(inTask); + return errCode; + } + // Pop out this vacuumNeedRecord + inTask.vacuumNeedRecords.pop_front(); + return E_OK; +} + +int MultiVerVacuum::DealWithLeftBranchShadowRecord(VacuumTaskContext &inTask) +{ + return DoDeleteRecordOfLeftShadowOrRightVacuumNeed(inTask, inTask.shadowRecords); +} + +int MultiVerVacuum::DealWithRightBranchCommit(VacuumTaskContext &inTask) +{ + return DoDealCommitOfLeftOrRight(inTask, inTask.rightBranchCommits, false); +} + +int MultiVerVacuum::DealWithRightBranchVacuumNeedRecord(VacuumTaskContext &inTask) +{ + return DoDeleteRecordOfLeftShadowOrRightVacuumNeed(inTask, inTask.vacuumNeedRecords); +} + +int MultiVerVacuum::DoDealCommitOfLeftOrRight(VacuumTaskContext &inTask, std::list &commitList, + bool isLeft) +{ + int errCode = DoCommitAndQuitIfWaitStatusObserved(inTask); + if (errCode != E_OK) { + return errCode; + } + // No other thread will access handle, node and record field of a RUN_NING, PAUSE_WAIT, ABORT_WAIT status task + // So it is concurrency safe to access or change these field without protection of lockguard + const MultiVerCommitInfo &commit = commitList.front(); + LOGD("[Vacuum][DoDealCommit] Version=%llu, CommitId=%s, isLeft=%d.", ULL(commit.version), + VEC_TO_STR(commit.commitId), isLeft); + if (inTask.vacuumNeedRecords.empty()) { + errCode = inTask.databaseHandle->GetVacuumNeedRecordsByVersion(commit.version, inTask.vacuumNeedRecords); + if (errCode != E_OK) { + LOGE("[Vacuum][DoDealCommit] GetVacuumNeedRecordsByVersion fail, Version=%llu, CommitId=%s, isLeft=%d, " + "errCode=%d.", ULL(commit.version), VEC_TO_STR(commit.commitId), isLeft, errCode); + DoRollBackAndFinish(inTask); + return errCode; + } + } + + while (!inTask.vacuumNeedRecords.empty()) { + if (isLeft) { + errCode = DealWithLeftBranchVacuumNeedRecord(inTask); + } else { + errCode = DealWithRightBranchVacuumNeedRecord(inTask); + } + if (errCode != E_OK) { + return errCode; + } + } + + // Every vacuumNeedRecords of this commit has been treated, mark this commit as vacuum done + errCode = StartTransactionIfNotYet(inTask); + if (errCode != E_OK) { + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + FinishVaccumTask(inTask); + return errCode; + } + errCode = inTask.databaseHandle->MarkCommitAsVacuumDone(commit.commitId); + if (errCode != E_OK) { + LOGE("[Vacuum][DoDealCommit] MarkCommitAsVacuumDone fail, Version=%llu, CommitId=%s, isLeft=%d, errCode=%d.", + ULL(commit.version), VEC_TO_STR(commit.commitId), isLeft, errCode); + DoRollBackAndFinish(inTask); + return errCode; + } + // Pop out this commit + commitList.pop_front(); + return E_OK; +} + +int MultiVerVacuum::DoDeleteRecordOfLeftShadowOrRightVacuumNeed(VacuumTaskContext &inTask, + std::list &recordList) +{ + int errCode = DoCommitAndQuitIfWaitStatusObserved(inTask); + if (errCode != E_OK) { + return errCode; + } + // No other thread will access handle, node and record field of a RUN_NING, PAUSE_WAIT, ABORT_WAIT status task + // So it is concurrency safe to access or change these field without protection of lockguard + const MultiVerRecordInfo &record = recordList.front(); + LOGD("[Vacuum][DoDeleteRecord] Type=%u, Version=%llu, HashKey=%s.", static_cast(record.type), + ULL(record.version), VEC_TO_STR(record.hashKey)); + errCode = StartTransactionIfNotYet(inTask); + if (errCode != E_OK) { + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + FinishVaccumTask(inTask); + return errCode; + } + errCode = inTask.databaseHandle->DeleteRecordTotally(record.version, record.hashKey); + if (errCode != E_OK) { + LOGE("[Vacuum][DoDeleteRecord] DeleteRecordTotally fail, Type=%u, Version=%llu, HashKey=%s, errCode=%d.", + static_cast(record.type), ULL(record.version), VEC_TO_STR(record.hashKey), errCode); + DoRollBackAndFinish(inTask); + return errCode; + } + // Pop out this shadowRecord or vacuumNeedRecord + recordList.pop_front(); + return E_OK; +} + +void MultiVerVacuum::DoRollBackAndFinish(VacuumTaskContext &inTask) +{ + RollBackTransactionIfNeed(inTask); + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + FinishVaccumTask(inTask); +} + +int MultiVerVacuum::DoCommitAndQuitIfWaitStatusObserved(VacuumTaskContext &inTask) +{ + bool waitStatusObserved = false; + { + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + if (inTask.status == VacuumTaskStatus::PAUSE_WAIT || inTask.status == VacuumTaskStatus::ABORT_WAIT) { + waitStatusObserved = true; + } + } + // Only this TaskThread will change a PAUSE_WAIT or ABORT_WAIT status to other status + // So here during the gap of miss-lockguard-protection, the status of this inTask will not change + if (waitStatusObserved) { + // CommitTransactionIfNeed may be an time cost operation, should not be called within the range of lockguard + int errCode = CommitTransactionIfNeed(inTask); + // Change status operation should be protected within the lockguard + std::lock_guard vacuumTaskLockGuard(vacuumTaskMutex_); + if (errCode != E_OK) { + // If commit fail, just finish this task(commit fail auto rollback) + FinishVaccumTask(inTask); + return errCode; + } + if (inTask.status == VacuumTaskStatus::ABORT_WAIT) { + AbortVacuumTask(inTask); + return -E_TASK_BREAK_OFF; + } + // Nor commit fail, nor Abort_wait case, here is Pause_wait Case, just set status to Pause_done + inTask.status = VacuumTaskStatus::PAUSE_DONE; + return -E_TASK_BREAK_OFF; + } + return E_OK; +} + +int MultiVerVacuum::StartTransactionIfNotYet(VacuumTaskContext &inTask) +{ + if (!inTask.isTransactionStarted) { + int errCode = inTask.databaseHandle->StartTransactionForVacuum(); + if (errCode != E_OK) { + LOGE("[Vacuum][StartTransact] StartTransactionForVacuum fail, errCode=%d.", errCode); + return errCode; + } + inTask.isTransactionStarted = true; + } + return E_OK; +} + +int MultiVerVacuum::CommitTransactionIfNeed(VacuumTaskContext &inTask) +{ + if (inTask.isTransactionStarted) { + // Whether CommitTransactionForVacuum fail or not, the transaction is ended. + inTask.isTransactionStarted = false; + int errCode = inTask.databaseHandle->CommitTransactionForVacuum(); + if (errCode != E_OK) { + LOGE("[Vacuum][CommitTransact] CommitTransactionForVacuum fail, errCode=%d.", errCode); + return errCode; + } + } + return E_OK; +} + +void MultiVerVacuum::RollBackTransactionIfNeed(VacuumTaskContext &inTask) +{ + if (inTask.isTransactionStarted) { + // Whether RollBackTransactionForVacuum fail or not, the transaction is ended. + inTask.isTransactionStarted = false; + int errCode = inTask.databaseHandle->RollBackTransactionForVacuum(); + if (errCode != E_OK) { + LOGE("[Vacuum][RollBackTransact] RollBackTransactionForVacuum fail, errCode=%d.", errCode); + } + } +} + +void MultiVerVacuum::FinishVaccumTask(VacuumTaskContext &inTask) +{ + inTask.status = VacuumTaskStatus::FINISH; + // It is OK to reset the autoRelaunchOnce. Since this is called when this task is RUN_NING status, all pause to + // this task will block and wait, and all continue to this task happens after we reset the autoRelaunchOnce + inTask.autoRelaunchOnce = false; + // Do not reset the databaseHandle while finish a task, because it will be reused after autoRelaunch + ResetNodeAndRecordContextInfo(inTask); +} + +void MultiVerVacuum::RelaunchVacuumTask(VacuumTaskContext &inTask) +{ + inTask.status = VacuumTaskStatus::RUN_WAIT; + inTask.runWaitOrder = incRunWaitOrder_++; // Queue at the back + inTask.autoRelaunchOnce = false; + // Obviously can not reset the databaseHandle while relaunch a task + ResetNodeAndRecordContextInfo(inTask); +} + +void MultiVerVacuum::AbortVacuumTask(VacuumTaskContext &inTask) +{ + inTask.status = VacuumTaskStatus::ABORT_DONE; + inTask.autoRelaunchOnce = false; + inTask.databaseHandle = nullptr; // reset handle in abort case + ResetNodeAndRecordContextInfo(inTask); +} + +void MultiVerVacuum::ResetNodeAndRecordContextInfo(VacuumTaskContext &inTask) +{ + inTask.leftBranchCommits.clear(); + inTask.rightBranchCommits.clear(); + inTask.vacuumNeedRecords.clear(); + inTask.shadowRecords.clear(); + inTask.isTransactionStarted = false; +} + +int MultiVerVacuum::SearchVacuumTaskToExecute(std::string &outDbIdentifier) +{ + // Find a vacuum task with the smallest runWaitOrder among tasks that is in RUN_WAIT Status(Except In Error). + uint64_t minRunWaitOrder = UINT64_MAX; + for (auto &eachTask : dbMapVacuumTask_) { + LOGD("[Vacuum][Search] db=%s, status=%d, error=%d, relaunch=%d, immediate=%d, runWait=%llu, pauseCount=%llu.", + eachTask.first.c_str(), static_cast(eachTask.second.status), eachTask.second.launchErrorHappen, + eachTask.second.autoRelaunchOnce, eachTask.second.immediatelyRelaunchable, + ULL(eachTask.second.runWaitOrder), ULL(eachTask.second.pauseNeedCount)); + if (eachTask.second.status == VacuumTaskStatus::RUN_WAIT && !eachTask.second.launchErrorHappen) { + if (eachTask.second.runWaitOrder < minRunWaitOrder) { + minRunWaitOrder = eachTask.second.runWaitOrder; + outDbIdentifier = eachTask.first; + } + } + } + if (!outDbIdentifier.empty()) { + dbMapVacuumTask_[outDbIdentifier].status = VacuumTaskStatus::RUN_NING; + return E_OK; + } else { + return -E_NOT_FOUND; + } +} + +void MultiVerVacuum::ActivateBackgroundVacuumTaskExecution() +{ + if (!isBackgroundVacuumTaskInExecution_) { + TaskAction backgroundTask = [this]() { + LOGI("[Vacuum][Activate] Begin Background Execution."); + VacuumTaskExecutor(); + LOGI("[Vacuum][Activate] End Background Execution."); + }; + int errCode = RuntimeContext::GetInstance()->ScheduleTask(backgroundTask); + if (errCode != E_OK) { + LOGE("[Vacuum][Activate] ScheduleTask failed, errCode = %d.", errCode); + return; + } + isBackgroundVacuumTaskInExecution_ = true; + } +} + +void MultiVerVacuum::IncPauseNeedCount(VacuumTaskContext &inTask) +{ + inTask.pauseNeedCount++; +} + +void MultiVerVacuum::DecPauseNeedCount(VacuumTaskContext &inTask) +{ + if (inTask.pauseNeedCount == 0) { + LOGE("[Vacuum][DecPause] PauseNeedCount Zero Before Decrease."); + return; + } + inTask.pauseNeedCount--; +} + +bool MultiVerVacuum::IsPauseNotNeed(VacuumTaskContext &inTask) +{ + return inTask.pauseNeedCount == 0; +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c2047fe52eb444e4625df7839e9b8a7c691c3dc0 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_vacuum_executor_impl.h" +#include "db_errno.h" +#include "log_print.h" +#include "multi_ver_storage_executor.h" + +namespace DistributedDB { +namespace { + const uint64_t DEL_FLAG = 0x02; // Del type record flag in OperFlag + const uint64_t CLEAR_FLAG = 0x03; // Clear type record flag in OperFlag + const uint64_t MASK_FLAG = 0x07; // mask. +} + +MultiVerVacuumExecutorImpl::MultiVerVacuumExecutorImpl(MultiVerNaturalStore *multiKvDB) + : multiKvDB_(multiKvDB), writeHandle_(nullptr) +{ +} + +MultiVerVacuumExecutorImpl::~MultiVerVacuumExecutorImpl() +{ + // In abnormal case that transaction not commit or rollback + if (multiKvDB_ != nullptr && writeHandle_ != nullptr) { + multiKvDB_->ReleaseHandle(writeHandle_, true); + } +} + +// Call this always beyond transaction +int MultiVerVacuumExecutorImpl::GetVacuumAbleCommits(std::list &leftBranchCommits, + std::list &rightBranchCommits) const +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (writeHandle_ != nullptr) { + LOGE("[VacuumExec][GetCommit] Mis-Called Within Transaction"); + return -E_NOT_PERMIT; + } + + // It will return at least zero, it's ok. return at most UINT64_MAX to means that all left commit are vacuumable. + uint64_t maxVersionOfVacuumAbleLeftCommit = multiKvDB_->GetMaxTrimmableVersion(); + + int errCode = E_OK; + MultiVerStorageExecutor *readHandle = multiKvDB_->GetHandle(false, errCode, true); + if (errCode != E_OK || readHandle == nullptr) { + LOGE("[VacuumExec][GetCommit] GetHandle fail, errCode=%d", errCode); + return errCode; + } + + std::list commitsInTree; + errCode = readHandle->GetAllCommitsInTree(commitsInTree); + if (errCode != E_OK) { + LOGE("[VacuumExec][GetCommit] GetAllCommitsInTree fail, errCode=%d", errCode); + multiKvDB_->ReleaseHandle(readHandle, true); + return errCode; + } + + // As discussed and agreed, the commit in commitsInTree had already be sorted in descending order by version + for (auto &eachCommit : commitsInTree) { + if (eachCommit.isLocal) { + if (eachCommit.version > maxVersionOfVacuumAbleLeftCommit) { + continue; + } + leftBranchCommits.emplace_back(MultiVerCommitInfo{eachCommit.version, eachCommit.commitId}); + } else { + rightBranchCommits.emplace_back(MultiVerCommitInfo{eachCommit.version, eachCommit.commitId}); + } + } + + multiKvDB_->ReleaseHandle(readHandle, true); + return E_OK; +} + +// Call this within or beyond transaction +int MultiVerVacuumExecutorImpl::GetVacuumNeedRecordsByVersion(uint64_t version, + std::list &vacuumNeedRecords) +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + MultiVerStorageExecutor *handle = GetCorrectHandleForUse(); + if (handle == nullptr) { + return -E_NO_RESOURCE_FOR_USE; + } + + std::list recordsInCommit; + int errCode = handle->GetEntriesByVersion(version, recordsInCommit); + if (errCode != E_OK) { + LOGE("[VacuumExec][GetVacuumNeed] GetEntriesByVersion fail, errCode=%d", errCode); + ReleaseHandleIfNeed(handle); + return errCode; + } + + for (auto &eachRecord : recordsInCommit) { + vacuumNeedRecords.emplace_back(MultiVerRecordInfo{GetRecordType(eachRecord), eachRecord.version, + eachRecord.key}); + } + + ReleaseHandleIfNeed(handle); + return E_OK; +} + +// Call this within or beyond transaction +int MultiVerVacuumExecutorImpl::GetShadowRecordsOfClearTypeRecord(uint64_t version, + const std::vector &hashKey, std::list &shadowRecords) +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + MultiVerStorageExecutor *handle = GetCorrectHandleForUse(); + if (handle == nullptr) { + return -E_NO_RESOURCE_FOR_USE; + } + + std::list clearShadowRecords; + int errCode = handle->GetOverwrittenClearTypeEntries(version, clearShadowRecords); + if (errCode != E_OK) { + LOGE("[VacuumExec][GetShadowClear] GetOverwrittenClearTypeEntries:%zu fail, err=%d", hashKey.size(), errCode); + ReleaseHandleIfNeed(handle); + return errCode; + } + + for (auto &eachRecord : clearShadowRecords) { + shadowRecords.emplace_back(MultiVerRecordInfo{GetRecordType(eachRecord), eachRecord.version, eachRecord.key}); + } + + ReleaseHandleIfNeed(handle); + return E_OK; +} + +// Call this within or beyond transaction +int MultiVerVacuumExecutorImpl::GetShadowRecordsOfNonClearTypeRecord(uint64_t version, + const std::vector &hashKey, std::list &shadowRecords) +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + MultiVerStorageExecutor *handle = GetCorrectHandleForUse(); + if (handle == nullptr) { + return -E_NO_RESOURCE_FOR_USE; + } + + std::list nonClearShadowRecords; + int errCode = handle->GetOverwrittenNonClearTypeEntries(version, hashKey, nonClearShadowRecords); + if (errCode != E_OK) { + LOGE("[VacuumExec][GetShadowNonClear] GetOverwrittenNonClearTypeEntries fail, errCode=%d", errCode); + ReleaseHandleIfNeed(handle); + return errCode; + } + + for (auto &eachRecord : nonClearShadowRecords) { + shadowRecords.emplace_back(MultiVerRecordInfo{GetRecordType(eachRecord), eachRecord.version, eachRecord.key}); + } + + ReleaseHandleIfNeed(handle); + return E_OK; +} + +// Call this before change the database +int MultiVerVacuumExecutorImpl::StartTransactionForVacuum() +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (writeHandle_ != nullptr) { + LOGE("[VacuumExec][Start] Transaction Already Started."); + return -E_NOT_PERMIT; + } + + int errCode = E_OK; + writeHandle_ = multiKvDB_->GetHandle(true, errCode, true); + if (errCode != E_OK || writeHandle_ == nullptr) { + LOGE("[VacuumExec][Start] GetHandle fail, errCode=%d", errCode); + return errCode; + } + + errCode = writeHandle_->StartTransaction(MultiTransactionType::ALL_DATA); + if (errCode != E_OK) { + LOGE("[VacuumExec][Start] StartTransaction fail, errCode=%d", errCode); + multiKvDB_->ReleaseHandle(writeHandle_, true); + writeHandle_ = nullptr; + return errCode; + } + return E_OK; +} + +// Call this if nothing error happened, if this itself failed, do not need to call rollback +int MultiVerVacuumExecutorImpl::CommitTransactionForVacuum() +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (writeHandle_ == nullptr) { + LOGE("[VacuumExec][Commit] Transaction Had Not Been Started."); + return -E_NOT_PERMIT; + } + + int errCode = writeHandle_->CommitTransaction(MultiTransactionType::ALL_DATA); + if (errCode != E_OK) { + // Commit fail do not need to call rollback which is automatically + LOGE("[VacuumExec][Commit] CommitTransaction fail, errCode=%d", errCode); + } + multiKvDB_->ReleaseHandle(writeHandle_, true); + writeHandle_ = nullptr; + return errCode; +} + +// Call this if anything wrong happened after start transaction except commit fail +int MultiVerVacuumExecutorImpl::RollBackTransactionForVacuum() +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (writeHandle_ == nullptr) { + LOGE("[VacuumExec][RollBack] Transaction Had Not Been Started."); + return -E_NOT_PERMIT; + } + + int errCode = writeHandle_->RollBackTransaction(MultiTransactionType::ALL_DATA); + if (errCode != E_OK) { + LOGE("[VacuumExec][RollBack] RollBackTransaction fail, errCode=%d", errCode); + } + multiKvDB_->ReleaseHandle(writeHandle_, true); + writeHandle_ = nullptr; + return errCode; +} + +// Call this always within transaction +int MultiVerVacuumExecutorImpl::DeleteRecordTotally(uint64_t version, const std::vector &hashKey) +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (writeHandle_ == nullptr) { + LOGE("[VacuumExec][Delete] Transaction Had Not Been Started."); + return -E_NOT_PERMIT; + } + + int errCode = writeHandle_->DeleteEntriesByHashKey(version, hashKey); + if (errCode != E_OK) { + LOGE("[VacuumExec][Delete] DeleteEntriesByHashKey fail, errCode=%d", errCode); + } + return errCode; +} + +// Call this always within transaction +int MultiVerVacuumExecutorImpl::MarkRecordAsVacuumDone(uint64_t version, const std::vector &hashKey) +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (writeHandle_ == nullptr) { + LOGE("[VacuumExec][MarkRecord] Transaction Had Not Been Started."); + return -E_NOT_PERMIT; + } + + int errCode = writeHandle_->UpdateTrimedFlag(version, hashKey); + if (errCode != E_OK) { + LOGE("[VacuumExec][MarkRecord] UpdateTrimedFlag fail, errCode=%d", errCode); + } + return errCode; +} + +// Call this always within transaction +int MultiVerVacuumExecutorImpl::MarkCommitAsVacuumDone(const std::vector &commitId) +{ + if (multiKvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (writeHandle_ == nullptr) { + LOGE("[VacuumExec][MarkCommit] Transaction Had Not Been Started."); + return -E_NOT_PERMIT; + } + + int errCode = writeHandle_->UpdateTrimedFlag(commitId); + if (errCode != E_OK) { + LOGE("[VacuumExec][MarkCommit] UpdateTrimedFlag fail, errCode=%d", errCode); + } + return errCode; +} + +MultiVerStorageExecutor *MultiVerVacuumExecutorImpl::GetCorrectHandleForUse() const +{ + if (writeHandle_ != nullptr) { + return writeHandle_; + } + int errCode = E_OK; + MultiVerStorageExecutor *handle = multiKvDB_->GetHandle(false, errCode, true); + if (errCode != E_OK || handle == nullptr) { + LOGE("[VacuumExec][GetHandle] GetHandle fail, errCode=%d", errCode); + return nullptr; + } + return handle; +} + +void MultiVerVacuumExecutorImpl::ReleaseHandleIfNeed(MultiVerStorageExecutor *inHandle) +{ + if (inHandle != writeHandle_) { + multiKvDB_->ReleaseHandle(inHandle, true); + } +} + +RecordType MultiVerVacuumExecutorImpl::GetRecordType(const MultiVerTrimedVersionData &inRecord) const +{ + if ((inRecord.operFlag & MASK_FLAG) == CLEAR_FLAG) { + return RecordType::CLEAR; + } else if ((inRecord.operFlag & MASK_FLAG) == DEL_FLAG) { + return RecordType::DELETE; + } else { + return RecordType::VALID; + } +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.h b/mock/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..ae955ec84b3361ae659a81f75324a524146cda88 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_VACUUM_EXECUTOR_IMPL_H +#define MULTI_VER_VACUUM_EXECUTOR_IMPL_H + +#ifndef OMIT_MULTI_VER +#include "multi_ver_vacuum_executor.h" +#include "multi_ver_natural_store.h" + +namespace DistributedDB { +// All functions will not be concurrently called +class MultiVerVacuumExecutorImpl final : public MultiVerVacuumExecutor { +public: + explicit MultiVerVacuumExecutorImpl(MultiVerNaturalStore *multiKvDB); + ~MultiVerVacuumExecutorImpl() override; + + // Call this always beyond transaction + int GetVacuumAbleCommits(std::list &leftBranchCommits, + std::list &rightBranchCommits) const override; + + // Call this within or beyond transaction + int GetVacuumNeedRecordsByVersion(uint64_t version, std::list &vacuumNeedRecords) override; + + // Call this within or beyond transaction + int GetShadowRecordsOfClearTypeRecord(uint64_t version, const std::vector &hashKey, + std::list &shadowRecords) override; + + // Call this within or beyond transaction + int GetShadowRecordsOfNonClearTypeRecord(uint64_t version, const std::vector &hashKey, + std::list &shadowRecords) override; + + // Call this before change the database + int StartTransactionForVacuum() override; + + // Call this if nothing error happened, if this itself failed, do not need to call rollback + int CommitTransactionForVacuum() override; + + // Call this if anything wrong happened after start transaction except commit fail + int RollBackTransactionForVacuum() override; + + // Call this always within transaction + int DeleteRecordTotally(uint64_t version, const std::vector &hashKey) override; + + // Call this always within transaction + int MarkRecordAsVacuumDone(uint64_t version, const std::vector &hashKey) override; + + // Call this always within transaction + int MarkCommitAsVacuumDone(const std::vector &commitId) override; +private: + MultiVerStorageExecutor *GetCorrectHandleForUse() const; + void ReleaseHandleIfNeed(MultiVerStorageExecutor *inHandle); + + RecordType GetRecordType(const MultiVerTrimedVersionData &inRecord) const; + + MultiVerNaturalStore *multiKvDB_; + MultiVerStorageExecutor *writeHandle_; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_VACUUM_EXECUTOR_IMPL_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_value_object.cpp b/mock/distributeddb/storage/src/multiver/multi_ver_value_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..796f4166136e3b956c46b1842448b9d54c2c98e3 --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_value_object.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_value_object.h" +#include "parcel.h" + +namespace DistributedDB { +namespace { + const size_t SLICE_HASH_VALUE_SIZE = 32; +} + +int MultiVerValueObject::GetValueHash(std::vector &valueHashes) const +{ + if (!IsHash()) { + return E_OK; + } + + for (size_t i = 0; i < valueHashVector_.size() / SLICE_HASH_VALUE_SIZE; i++) { + ValueSliceHash sliceHash; + sliceHash.assign(valueHashVector_.begin() + i * SLICE_HASH_VALUE_SIZE, + valueHashVector_.begin() + (i + 1) * SLICE_HASH_VALUE_SIZE); + valueHashes.push_back(std::move(sliceHash)); + } + return E_OK; +} + +int MultiVerValueObject::SetValueHash(const std::vector &valueHashes) +{ + valueHashVector_.clear(); + valueHashVector_.shrink_to_fit(); + for (const auto &item : valueHashes) { + valueHashVector_.insert(valueHashVector_.end(), item.begin(), item.end()); + } + head_.flag = HASH_FLAG; + return E_OK; +} + +int MultiVerValueObject::GetSerialData(std::vector &data) const +{ + const uint32_t serialIntNum = 4; // 4 int + size_t totalLength = Parcel::GetIntLen() * serialIntNum + Parcel::GetVectorCharLen(valueHashVector_); + data.resize(totalLength); + Parcel parcel(data.data(), data.size()); + + int errCode = parcel.WriteInt(head_.flag); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteInt(head_.reserved); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteInt(head_.hashNum); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteInt(head_.length); + if (errCode != E_OK) { + return errCode; + } + + errCode = parcel.WriteVectorChar(valueHashVector_); + if (errCode != E_OK) { + return errCode; + } + + return E_OK; +} + +int MultiVerValueObject::DeSerialData(const std::vector &data) +{ + Parcel parcel(const_cast(data.data()), data.size()); + int32_t readValue = 0; + parcel.ReadInt(readValue); + head_.flag = static_cast(readValue); + parcel.ReadInt(readValue); + head_.reserved = static_cast(readValue); + parcel.ReadInt(readValue); + head_.hashNum = static_cast(readValue); + parcel.ReadInt(readValue); + head_.length = static_cast(readValue); + parcel.ReadVectorChar(valueHashVector_); + if (parcel.IsError()) { + LOGE("Deserial the multi ver value object error"); + return -E_PARSE_FAIL; + } + if (((head_.flag & HASH_FLAG) == HASH_FLAG) && ((valueHashVector_.size() % SLICE_HASH_VALUE_SIZE) != 0)) { + LOGE("Value hash list total size is unexpected:%zu", valueHashVector_.size()); + return -E_PARSE_FAIL; + } + return E_OK; +} + +uint32_t MultiVerValueObject::GetDataLength() const +{ + return head_.length; +} + +void MultiVerValueObject::SetDataLength(uint32_t length) +{ + head_.length = length; +} + +int MultiVerValueObject::GetValue(Value &value) const +{ + if ((head_.flag & HASH_FLAG) == HASH_FLAG) { + return -E_NOT_SUPPORT; + } + value.assign(valueHashVector_.begin(), valueHashVector_.end()); + return E_OK; +} + +int MultiVerValueObject::SetValue(const Value &value) +{ + head_.flag = 0; + valueHashVector_.clear(); + valueHashVector_.shrink_to_fit(); + valueHashVector_.assign(value.begin(), value.end()); + return E_OK; +} + +bool MultiVerValueObject::IsHash() const +{ + return (head_.flag & HASH_FLAG) == HASH_FLAG; +} + +void MultiVerValueObject::SetFlag(uint8_t flag) +{ + head_.flag = flag; +} +} +#endif diff --git a/mock/distributeddb/storage/src/multiver/multi_ver_value_object.h b/mock/distributeddb/storage/src/multiver/multi_ver_value_object.h new file mode 100644 index 0000000000000000000000000000000000000000..ca62af3e9cb28ac3988abed60f05e57aa400a68f --- /dev/null +++ b/mock/distributeddb/storage/src/multiver/multi_ver_value_object.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_VALUE_OBJECT_H +#define MULTI_VER_VALUE_OBJECT_H + +#ifndef OMIT_MULTI_VER +#include + +#include "db_types.h" + +namespace DistributedDB { +using ValueSliceHash = std::vector; +using ValueSlice = std::vector; + +struct ValueObjectHead { + uint8_t flag = 0; + uint8_t reserved = 0; + uint16_t hashNum = 1; + uint32_t length = 0; +}; + +class MultiVerValueObject { +public: + static const uint8_t HASH_FLAG = 0x01; + + MultiVerValueObject() {} + ~MultiVerValueObject() {} + + int GetValueHash(std::vector &valueHashes) const; + int SetValueHash(const std::vector &valueHashes); + + int GetSerialData(std::vector &data) const; + int DeSerialData(const std::vector &data); + + uint32_t GetDataLength() const; + void SetDataLength(uint32_t); + + int GetValue(Value &value) const; + int SetValue(const Value &value); + + bool IsHash() const; + + // 1:value has been Hash. 0:Origin value + void SetFlag(uint8_t flag); + +private: + ValueObjectHead head_; + std::vector valueHashVector_; +}; +} + +#endif // MULTI_VER_VALUE_OBJECT_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/operation/database_oper.cpp b/mock/distributeddb/storage/src/operation/database_oper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a12f0a134041af7f61024993da522abad1d65ad --- /dev/null +++ b/mock/distributeddb/storage/src/operation/database_oper.cpp @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "database_oper.h" + +#include "db_errno.h" +#include "db_constant.h" +#include "db_common.h" +#include "log_print.h" +#include "platform_specific.h" +#include "package_file.h" +#include "res_finalizer.h" +#include "runtime_context.h" + +namespace DistributedDB { +void DatabaseOper::SetLocalDevId(const std::string &deviceId) +{ + deviceId_ = deviceId; +} + +int DatabaseOper::ExecuteRekey(const CipherPassword &passwd, const KvDBProperties &property) +{ + int errCode = E_OK; + if (!RekeyPreHandle(passwd, errCode)) { + LOGI("Finish rekey when RekeyPre Handle, errCode = [%d]", errCode); + return errCode; + } + + std::string ctrlFileName; + std::string newFileName; + errCode = CreateStatusCtrlFile(property, ctrlFileName, newFileName); + if (errCode != E_OK) { + return errCode; + } + + LOGI("Backup the current file while rekey."); + errCode = BackupDb(passwd); + if (errCode != E_OK) { + LOGE("ExecuteRekey backup db failed! errCode = [%d]", errCode); + (void)RekeyRecover(property); + return errCode; + } + + errCode = RenameStatusCtrlFile(ctrlFileName, newFileName); + if (errCode != E_OK) { + (void)RekeyRecover(property); + LOGE("ExecuteRekey rename status ctrl failed! errCode = [%d]", errCode); + return errCode; + } + + errCode = CloseStorages(); + if (errCode != E_OK) { + return errCode; + } + + errCode = RekeyPostHandle(passwd); + if (errCode == -E_EKEYREVOKED) { + errCode = -E_FORBID_CACHEDB; + LOGI("Can not reopen database after rekey for the access controlled. errCode = [%d]", errCode); + } + return errCode; +} + +int DatabaseOper::GetCtrlFilePrefix(const KvDBProperties &property, std::string &filePrefix) const +{ + std::string baseDir; + int errCode = GetWorkDir(property, baseDir); + if (errCode != E_OK) { + return errCode; + } + + int dbType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + std::string dbSubDir = KvDBProperties::GetStoreSubDirectory(dbType); + filePrefix = baseDir + "/" + dbSubDir; + return E_OK; +} + +int DatabaseOper::RekeyRecover(const KvDBProperties &property) +{ + std::string workDir; + int errCode = GetWorkDir(property, workDir); + if (errCode != E_OK) { + return errCode; + } + + int dbType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + std::string dbSubDir = KvDBProperties::GetStoreSubDirectory(dbType); + + std::string preCtrlFileName = workDir + "/" + dbSubDir + DBConstant::REKEY_FILENAME_POSTFIX_PRE; + bool isPreCtrlFileExist = OS::CheckPathExistence(preCtrlFileName); + + std::string endCtrlFileName = workDir + "/" + dbSubDir + DBConstant::REKEY_FILENAME_POSTFIX_OK; + bool isEndCtrlFileExist = OS::CheckPathExistence(endCtrlFileName); + + std::string currentDir = workDir + "/" + dbSubDir; + bool isPrimeDbDirExist = OS::CheckPathExistence(currentDir); + + std::string backupDir = workDir + "/" + dbSubDir + DBConstant::PATH_BACKUP_POSTFIX; + bool isBackupDbDirExist = OS::CheckPathExistence(backupDir); + + // remove the backup directory and ctrl file if Rekey not finish + // name of ctrl file is pre + if (isPreCtrlFileExist) { + LOGI("Rekey recovery:Remove the backup files"); + return RecoverPrehandle(dbType, backupDir, preCtrlFileName); + } + // no ctrl file means nothing need to do + if (!isEndCtrlFileExist) { + return E_OK; + } + + // name of ctrl file is ok + if (isBackupDbDirExist) { + if (isPrimeDbDirExist) { + // scenario 1: both prime and bak dir exist + // rm prime dir -> rename backup dir to prime dir -> rm ctrl file + LOGI("Rekey recovery:Remove the current files"); + if (DBCommon::RemoveAllFilesOfDirectory(currentDir, true) != E_OK) { + LOGE("Remove the prime dir failed: %d", errno); + return -E_REMOVE_FILE; + } + } + + // scenario 2: only bak dir exist + // rename backup dir to prime dir -> rm ctrl file + if (rename(backupDir.c_str(), currentDir.c_str()) != E_OK) { + LOGE("Rename the bak dir to prime dir failed:%d.", errno); + return -E_SYSTEM_API_FAIL; + } + } + // scenario 3: only prime dir exist + // scenario 4: both prime and bak dir not exist + // remove ctrl file + if (RemoveFile(endCtrlFileName) != E_OK) { + LOGE("Remove the end ctrl file failed: %d", errno); + return -E_REMOVE_FILE; + } + return E_OK; +} + +int DatabaseOper::CheckSecurityOption(const std::string &filePath, const KvDBProperties &property) const +{ + SecurityOption secOption; + int errCode = RuntimeContext::GetInstance()->GetSecurityOption(filePath, secOption); + if (errCode != E_OK && errCode != -E_NOT_SUPPORT) { + LOGE("Get import package security option fail! errCode = [%d]", errCode); + return errCode; + } + + SecurityOption dbSecOpt; + dbSecOpt.securityFlag = property.GetSecFlag(); + dbSecOpt.securityLabel = property.GetSecLabel(); + + if (dbSecOpt == secOption || secOption.securityLabel == SecurityLabel::NOT_SET) { + return E_OK; + } + LOGE("Import package secOpt %d %d vs database %d %d", + secOption.securityFlag, secOption.securityLabel, dbSecOpt.securityFlag, dbSecOpt.securityLabel); + return -E_SECURITY_OPTION_CHECK_ERROR; +} + +int DatabaseOper::ExecuteImport(const std::string &filePath, const CipherPassword &passwd, + const KvDBProperties &property) const +{ + ImportFileInfo importInfo; + InitImportFileInfo(importInfo, property); + + int errCode = CheckSecurityOption(filePath, property); + if (errCode != E_OK) { + return errCode; + } + + // 1. unpack and check the file. + LOGI("Unpack the imported file"); + errCode = UnpackAndCheckImportedFile(filePath, importInfo, property); + if (errCode != E_OK) { + return errCode; + } + + // Using RAII define tempState clean when object finalize execute + ResFinalizer tempStateClean([&errCode, &property, this]() { + int innerCode = this->ClearImportTempFile(property); + if (innerCode != E_OK) { + LOGE("Failed to clean the intermediate import files, errCode = [%d]", innerCode); + } + // Finish. reinitialize the database. + if (errCode != E_OK) { + innerCode = this->ImportPostHandle(); + LOGE("Reinit the database after import, errCode = [%d]", innerCode); + } + }); + + // 2. backup the current database. + LOGI("Backup the current database while import."); + errCode = BackupCurrentDatabase(importInfo); + if (errCode != E_OK) { + LOGE("Failed to backup current databases, errCode = [%d]", errCode); + return errCode; + } + + // 3. export the unpacked file to the current database. + LOGI("Import the unpacked database."); + errCode = ImportUnpackedDatabase(importInfo, passwd); + if (errCode != E_OK) { + LOGE("Failed to import from the unpacked databases, errCode = [%d]", errCode); + } + DBCommon::RemoveAllFilesOfDirectory(importInfo.unpackedDir); + return errCode; +} + +int DatabaseOper::CreateBackupDirForExport(const KvDBProperties &property, std::string ¤tDir, + std::string &backupDir) const +{ + std::string baseDir; + int errCode = GetWorkDir(property, baseDir); + if (errCode != E_OK) { + LOGE("Get work dir failed:%d.", errCode); + return errCode; + } + + int databaseType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + std::string subDir = KvDBProperties::GetStoreSubDirectory(databaseType); + currentDir = baseDir + "/" + subDir; + + backupDir = baseDir + "/" + subDir + DBConstant::PATH_POSTFIX_EXPORT_BACKUP + "/"; + errCode = DBCommon::CreateDirectory(backupDir); + if (errCode != E_OK) { + return errCode; + } + std::vector dbDir {DBConstant::MAINDB_DIR, DBConstant::METADB_DIR, DBConstant::CACHEDB_DIR}; + for (const auto &item : dbDir) { + if (DBCommon::CreateDirectory(backupDir + "/" + item) != E_OK) { + return -E_SYSTEM_API_FAIL; + } + } + return errCode; +} + +int DatabaseOper::ExecuteExport(const std::string &filePath, const CipherPassword &passwd, + const KvDBProperties &property) const +{ + if (deviceId_.empty()) { + return -E_NOT_INIT; + } + + std::string currentDir; + std::string backupDir; + int errCode = CreateBackupDirForExport(property, currentDir, backupDir); + if (errCode != E_OK) { + return errCode; + } + + errCode = ExportAllDatabases(currentDir, passwd, backupDir); + if (errCode != E_OK) { + LOGE("Export databases fail!:%d.", errCode); + (void)ClearExportedTempFiles(property); + return errCode; + } + + errCode = PackExportedDatabase(backupDir, filePath, property); + if (errCode != E_OK) { + OS::RemoveFile(filePath); // Pack file failed, need rollback delete Intermediate state package file + LOGE("[DatabaseOper][ExecuteExport] Pack files fail! errCode = [%d], errno = [%d].", errCode, errno); + (void)ClearExportedTempFiles(property); + return errCode; + } + + SecurityOption secOption {property.GetSecLabel(), property.GetSecFlag()}; + // RuntimeContext can make sure GetInstance not nullptr + errCode = RuntimeContext::GetInstance()->SetSecurityOption(filePath, secOption); + if (errCode != E_OK) { + if (errCode == -E_NOT_SUPPORT) { + (void)ClearExportedTempFiles(property); + return E_OK; + } + OS::RemoveFile(filePath); + LOGE("[DatabaseOper][ExecuteExport] Set security option fail! errCode = [%d].", errCode); + } + + (void)ClearExportedTempFiles(property); + return errCode; +} + +// private begin +int DatabaseOper::CreateStatusCtrlFile(const KvDBProperties &property, std::string &orgCtrlFile, + std::string &newCtrlFile) +{ + std::string filePrefix; + int errCode = GetCtrlFilePrefix(property, filePrefix); + if (errCode != E_OK) { + return errCode; + } + + // create control file + newCtrlFile = filePrefix + DBConstant::REKEY_FILENAME_POSTFIX_OK; + orgCtrlFile = filePrefix + DBConstant::REKEY_FILENAME_POSTFIX_PRE; + return OS::CreateFileByFileName(orgCtrlFile); +} + +int DatabaseOper::RenameStatusCtrlFile(const std::string &orgCtrlFile, const std::string &newCtrlFile) +{ + int errCode = rename(orgCtrlFile.c_str(), newCtrlFile.c_str()); + if (errCode != E_OK) { + LOGE("change ctrl file name to ok failed: %d.", errCode); + return -E_SYSTEM_API_FAIL; + } + return E_OK; +} + +int DatabaseOper::RecoverPrehandle(int dbType, const std::string &dir, const std::string &fileName) +{ + if (DBCommon::RemoveAllFilesOfDirectory(dir, true) != E_OK) { + LOGE("Remove the backup dir failed:%d", errno); + return -E_REMOVE_FILE; + } + if (RemoveFile(fileName) != E_OK) { + LOGE("Remove the pre ctrl file failed:%d", errno); + return -E_REMOVE_FILE; + } + return E_OK; +} + +int DatabaseOper::RemoveDbDir(const std::string &dir, int dbType, bool isNeedDelDir) +{ + if (!OS::CheckPathExistence(dir)) { + return E_OK; + } + + if (dbType == DBConstant::DB_TYPE_LOCAL) { + std::vector dbNameList = { + DBConstant::LOCAL_DATABASE_NAME + }; + return RemoveDbFiles(dir, dbNameList, isNeedDelDir); + } + if (dbType == DBConstant::DB_TYPE_SINGLE_VER) { + std::vector dbNameList = { + DBConstant::SINGLE_VER_DATA_STORE + }; + return RemoveDbFiles(dir, dbNameList, isNeedDelDir); + } + if (dbType == DBConstant::DB_TYPE_MULTI_VER) { + std::vector dbNameList = { + DBConstant::MULTI_VER_DATA_STORE, DBConstant::MULTI_VER_COMMIT_STORE, + DBConstant::MULTI_VER_VALUE_STORE, DBConstant::MULTI_VER_META_STORE + }; + return RemoveDbFiles(dir, dbNameList, isNeedDelDir); + } + return -E_NOT_SUPPORT; +} + +int DatabaseOper::RemoveFile(const std::string &fileName) +{ + if (!OS::CheckPathExistence(fileName)) { + return E_OK; + } + + if (OS::RemoveFile(fileName.c_str()) != E_OK) { + LOGE("Remove file failed:%d", errno); + return -E_REMOVE_FILE; + } + return E_OK; +} + +int DatabaseOper::GetWorkDir(const KvDBProperties &property, std::string &workDir) +{ + std::string dataDir = property.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifierDir = property.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + if (dataDir.empty()) { + return -E_INVALID_ARGS; + } + + workDir = dataDir + "/" + identifierDir; + return E_OK; +} + +// Only for remove the backup directory while rekey. +int DatabaseOper::RemoveDbFiles(const std::string &dir, const std::vector &dbNameList, bool isNeedDelDir) +{ + for (const auto &iter : dbNameList) { + // remove + std::string dbFile = dir + "/" + iter + ".db"; + if (RemoveFile(dbFile) != E_OK) { + LOGE("Remove the db file failed:%d", errno); + return -E_REMOVE_FILE; + } + + dbFile = dir + "/" + iter + ".db-wal"; + if (RemoveFile(dbFile) != E_OK) { + LOGE("Remove the wal file failed:%d", errno); + return -E_REMOVE_FILE; + } + + dbFile = dir + "/" + iter + ".db-shm"; + if (RemoveFile(dbFile) != E_OK) { + LOGE("Remove the shm file failed:%d", errno); + return -E_REMOVE_FILE; + } + } + if (isNeedDelDir && OS::RemoveDBDirectory(dir) != E_OK) { + LOGE("Remove directory:%d", errno); + return -E_REMOVE_FILE; + } + return E_OK; +} + +void DatabaseOper::InitImportFileInfo(ImportFileInfo &info, const KvDBProperties &property) +{ + std::string dataDir = property.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifierDir = property.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + int databaseType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + std::string subDir = KvDBProperties::GetStoreSubDirectory(databaseType); + + std::string baseDir = dataDir + "/" + identifierDir + "/" + subDir; + info.backupDir = baseDir + DBConstant::PATH_POSTFIX_IMPORT_BACKUP + "/"; + info.unpackedDir = baseDir + DBConstant::PATH_POSTFIX_UNPACKED + "/"; + info.currentDir = baseDir + "/"; + info.curValidFile = baseDir + DBConstant::PATH_POSTFIX_IMPORT_ORIGIN; // origin directory is valid. + info.backValidFile = baseDir + DBConstant::PATH_POSTFIX_IMPORT_DUP; // the back directory is valid. +} + +int DatabaseOper::UnpackAndCheckImportedFile(const std::string &srcFile, const ImportFileInfo &info, + const KvDBProperties &property) const +{ + int errCode = DBCommon::CreateDirectory(info.unpackedDir); + if (errCode != E_OK) { + return errCode; + } + + FileInfo fileInfo; + errCode = PackageFile::UnpackFile(srcFile, info.unpackedDir, fileInfo); + if (errCode != E_OK) { + DBCommon::RemoveAllFilesOfDirectory(info.unpackedDir); + LOGE("Failed to unpack the imported file:%d", errCode); + return errCode; + } + int dbType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + if (fileInfo.dbType != static_cast(dbType) || fileInfo.deviceID != deviceId_) { + DBCommon::RemoveAllFilesOfDirectory(info.unpackedDir); + LOGE("Check db type [%u] vs [%u] or devicesId fail!", fileInfo.dbType, static_cast(dbType)); + return -E_INVALID_FILE; + } + return E_OK; +} + +int DatabaseOper::RecoverImportedBackFiles(const std::string &dir, const std::string &fileName, int dbType) const +{ + std::string backupDir = dir + DBConstant::PATH_POSTFIX_IMPORT_BACKUP; + // if backup directory is not existed + if (!OS::CheckPathExistence(backupDir)) { + goto END; + } + + if (DBCommon::RemoveAllFilesOfDirectory(dir, true) != E_OK) { + LOGE("Remove the current db dir failed"); + return -E_REMOVE_FILE; + } + + if (rename(backupDir.c_str(), dir.c_str()) != E_OK) { + LOGE("Rename the backfile error:%d", errno); + return -E_SYSTEM_API_FAIL; + } + +END: + if (RemoveFile(fileName) != E_OK) { + LOGE("Remove the pre ctrl file failed:%d", errno); + return -E_REMOVE_FILE; + } + return E_OK; +} + +int DatabaseOper::RemoveImportedBackFiles(const std::string &backupDir, const std::string &ctrlFileName, int dbType) + const +{ + if (DBCommon::RemoveAllFilesOfDirectory(backupDir, true) != E_OK) { + LOGE("Remove the backup dir failed"); + return -E_REMOVE_FILE; + } + + if (RemoveFile(ctrlFileName) != E_OK) { + LOGE("Remove the pre ctrl file failed"); + return -E_REMOVE_FILE; + } + return E_OK; +} + +int DatabaseOper::ClearImportTempFile(const KvDBProperties &property) const +{ + // get work directory + std::string workDir; + int errCode = GetWorkDir(property, workDir); + if (errCode != E_OK) { + return errCode; + } + + int dbType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + std::string dbSubDir = KvDBProperties::GetStoreSubDirectory(dbType); + + std::string oriKeepFile = workDir + "/" + dbSubDir + DBConstant::PATH_POSTFIX_IMPORT_ORIGIN; + bool isOriKeepFileExist = OS::CheckPathExistence(oriKeepFile); + + std::string backKeepFile = workDir + "/" + dbSubDir + DBConstant::PATH_POSTFIX_IMPORT_DUP; + bool isBakKeepFileExist = OS::CheckPathExistence(backKeepFile); + + std::string currentDir = workDir + "/" + dbSubDir; + std::string backupDir = workDir + "/" + dbSubDir + DBConstant::PATH_POSTFIX_IMPORT_BACKUP; + bool isBackupDbDirExist = OS::CheckPathExistence(backupDir); + std::string exportBackupDir = workDir + "/" + dbSubDir + DBConstant::PATH_POSTFIX_UNPACKED; + DBCommon::RemoveAllFilesOfDirectory(exportBackupDir); + + if (isOriKeepFileExist && isBakKeepFileExist) { + LOGE("Origin and backup file shouldn't exist concurrently"); + } + + // Clear the backup dir and the ctrl file + if (isOriKeepFileExist) { + return RemoveImportedBackFiles(backupDir, oriKeepFile, dbType); + } + + // remove the main directory and restore the backup files. + if (isBakKeepFileExist) { + return RecoverImportedBackFiles(currentDir, backKeepFile, dbType); + } + + if (isBackupDbDirExist) { + // Import success, clean backupdir + if (DBCommon::RemoveAllFilesOfDirectory(backupDir, true) != E_OK) { + LOGE("Remove the backup dir failed"); + return -E_REMOVE_FILE; + } + } + + return E_OK; +} + +int DatabaseOper::ClearExportedTempFiles(const KvDBProperties &property) const +{ + std::string workDir; + int errCode = GetWorkDir(property, workDir); + if (errCode != E_OK) { + return errCode; + } + + int dbType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + std::string dbSubDir = KvDBProperties::GetStoreSubDirectory(dbType); + std::string backupDir = workDir + "/" + dbSubDir + DBConstant::PATH_POSTFIX_EXPORT_BACKUP; + errCode = DBCommon::RemoveAllFilesOfDirectory(backupDir); + if (errCode != E_OK) { + LOGE("Remove the exported backup dir failed"); + return -E_REMOVE_FILE; + } + + return errCode; +} + +int DatabaseOper::PackExportedDatabase(const std::string &fileDir, const std::string &packedFile, + const KvDBProperties &property) const +{ + LOGI("Pack the exported database."); + int databaseType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + FileInfo fileInfo = {static_cast(databaseType), deviceId_}; + int errCode = PackageFile::PackageFiles(fileDir, packedFile, fileInfo); + if (errCode != E_OK) { + LOGE("Pack the database error:%d", errCode); + } + + return errCode; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/operation/database_oper.h b/mock/distributeddb/storage/src/operation/database_oper.h new file mode 100644 index 0000000000000000000000000000000000000000..5e933b0aa61edf80bcbfe65ff4faa451a391fbc0 --- /dev/null +++ b/mock/distributeddb/storage/src/operation/database_oper.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DATABASE_OPER_H +#define DATABASE_OPER_H + +#include + +#include "kvdb_properties.h" +#include "generic_kvdb.h" + +namespace DistributedDB { +class DatabaseOper { +public: + virtual ~DatabaseOper() {}; + + virtual int Rekey(const CipherPassword &passwd) = 0; + + virtual int Import(const std::string &filePath, const CipherPassword &passwd) = 0; + + virtual int Export(const std::string &filePath, const CipherPassword &passwd) const = 0; + + void SetLocalDevId(const std::string &deviceId); + + int RekeyRecover(const KvDBProperties &property); + + int ClearImportTempFile(const KvDBProperties &property) const; + + int ClearExportedTempFiles(const KvDBProperties &property) const; + +protected: + int ExecuteRekey(const CipherPassword &passwd, const KvDBProperties &property); + + virtual bool RekeyPreHandle(const CipherPassword &passwd, int &errCode) = 0; + + virtual int BackupDb(const CipherPassword &passwd) const = 0; + + virtual int CloseStorages() = 0; + + virtual int RekeyPostHandle(const CipherPassword &passwd) = 0; + + int GetCtrlFilePrefix(const KvDBProperties &property, std::string &filePrefix) const; + + virtual int ExportAllDatabases(const std::string ¤tDir, const CipherPassword &passwd, + const std::string &dbDir) const = 0; + + static int RemoveFile(const std::string &fileName); + + // import begin + int ExecuteImport(const std::string &filePath, const CipherPassword &passwd, const KvDBProperties &property) const; + + virtual int BackupCurrentDatabase(const ImportFileInfo &info) const = 0; + + virtual int ImportUnpackedDatabase(const ImportFileInfo &info, const CipherPassword &srcPasswd) const = 0; + + virtual int ImportPostHandle() const = 0; + + // export begin + int ExecuteExport(const std::string &filePath, const CipherPassword &passwd, const KvDBProperties &property) const; + +private: + int CreateStatusCtrlFile(const KvDBProperties &property, std::string &orgCtrlFile, std::string &newCtrlFile); + + static int RenameStatusCtrlFile(const std::string &orgCtrlFile, const std::string &newCtrlFile); + + int RecoverPrehandle(int dbType, const std::string &dir, const std::string &fileName); + + int RemoveDbDir(const std::string &dir, int dbType, bool isNeedDelDir = true); + + static int GetWorkDir(const KvDBProperties &property, std::string &workDir); + + int RemoveDbFiles(const std::string &dir, const std::vector &dbNameList, bool isNeedDelDir = true); + + static void InitImportFileInfo(ImportFileInfo &info, const KvDBProperties &property); + + int UnpackAndCheckImportedFile(const std::string &srcFile, const ImportFileInfo &info, + const KvDBProperties &property) const; + + int RecoverImportedBackFiles(const std::string &dir, const std::string &fileName, int dbType) const; + + int RemoveImportedBackFiles(const std::string &backupDir, const std::string &ctrlFileName, int dbType) const; + + int PackExportedDatabase(const std::string &fileDir, const std::string &packedFile, + const KvDBProperties &property) const; + + int CheckSecurityOption(const std::string &filePath, const KvDBProperties &property) const; + + int CreateBackupDirForExport(const KvDBProperties &property, std::string ¤tDir, std::string &backupDir) const; + + std::string deviceId_; +}; +} // namespace DistributedDB + +#endif // DATABASE_OPER_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/operation/local_database_oper.cpp b/mock/distributeddb/storage/src/operation/local_database_oper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ceede762cb9508a3996634e3b401e2d308bbf827 --- /dev/null +++ b/mock/distributeddb/storage/src/operation/local_database_oper.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "local_database_oper.h" + +#include "log_print.h" +#include "platform_specific.h" +#include "db_errno.h" +#include "db_constant.h" +#include "db_common.h" + +namespace DistributedDB { +LocalDatabaseOper::LocalDatabaseOper(SQLiteLocalKvDB *localKvDb, SQLiteStorageEngine *storageEngine) + : localKvDb_(localKvDb), + storageEngine_(storageEngine) +{} + +int LocalDatabaseOper::Rekey(const CipherPassword &passwd) +{ + if (localKvDb_ == nullptr || storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + + return ExecuteRekey(passwd, localKvDb_->GetDbProperties()); +} + +int LocalDatabaseOper::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (localKvDb_ == nullptr || storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + + return ExecuteImport(filePath, passwd, localKvDb_->GetDbProperties()); +} + +int LocalDatabaseOper::Export(const std::string &filePath, const CipherPassword &passwd) const +{ + return ExecuteExport(filePath, passwd, localKvDb_->GetDbProperties()); +} + +bool LocalDatabaseOper::RekeyPreHandle(const CipherPassword &passwd, int &errCode) +{ + CipherType cipherType; + CipherPassword cachePasswd; + localKvDb_->GetDbProperties().GetPassword(cipherType, cachePasswd); + + if (cachePasswd.GetSize() == 0 && passwd.GetSize() == 0) { + errCode = E_OK; + return false; + } + + // need invoke sqlite3 rekey + if (cachePasswd.GetSize() > 0 && passwd.GetSize() > 0) { + errCode = localKvDb_->RunRekeyLogic(cipherType, passwd); + return false; + } + + return true; +} + +int LocalDatabaseOper::BackupDb(const CipherPassword &passwd) const +{ + std::string filePrefix; + int errCode = GetCtrlFilePrefix(localKvDb_->GetDbProperties(), filePrefix); + if (errCode != E_OK) { + return errCode; + } + + // create backup dir + std::string backupDir = filePrefix + DBConstant::PATH_BACKUP_POSTFIX; + errCode = DBCommon::CreateDirectory(backupDir); + if (errCode != E_OK) { + LOGE("create backup dir failed:%d.", errCode); + return errCode; + } + + // export db to backup + CipherType cipherType; + CipherPassword oldPasswd; + localKvDb_->GetDbProperties().GetPassword(cipherType, oldPasswd); + std::string backupDbName = backupDir + "/" + DBConstant::LOCAL_DATABASE_NAME + DBConstant::SQLITE_DB_EXTENSION; + return localKvDb_->RunExportLogic(cipherType, passwd, backupDbName); +} + +int LocalDatabaseOper::CloseStorages() +{ + // close old db + storageEngine_->Release(); + int errCode = RekeyRecover(localKvDb_->GetDbProperties()); + if (errCode != E_OK) { + LOGE("Recover failed after rekey ok:%d.", errCode); + int innerCode = localKvDb_->InitDatabaseContext(localKvDb_->GetDbProperties()); + if (innerCode != E_OK) { + LOGE("ReInit the handlePool failed:%d", innerCode); + } + } + return errCode; +} + +int LocalDatabaseOper::RekeyPostHandle(const CipherPassword &passwd) +{ + CipherType cipherType; + CipherPassword oldPasswd; + localKvDb_->GetDbPropertyForUpdate().GetPassword(cipherType, oldPasswd); + localKvDb_->GetDbPropertyForUpdate().SetPassword(cipherType, passwd); + return localKvDb_->InitDatabaseContext(localKvDb_->GetDbProperties()); +} + +int LocalDatabaseOper::ExportAllDatabases(const std::string ¤tDir, const CipherPassword &passwd, + const std::string &dbDir) const +{ + std::string backupDbName = dbDir + DBConstant::LOCAL_DATABASE_NAME + DBConstant::SQLITE_DB_EXTENSION; + std::string currentDb = currentDir + "/" + DBConstant::LOCAL_DATABASE_NAME + DBConstant::SQLITE_DB_EXTENSION; + + CipherType cipherType; + CipherPassword currPasswd; + localKvDb_->GetDbProperties().GetPassword(cipherType, currPasswd); + int errCode = SQLiteUtils::ExportDatabase(currentDb, cipherType, currPasswd, backupDbName, passwd); + if (errCode != E_OK) { + LOGE("Export the database failed:%d", errCode); + } + return errCode; +} + +int LocalDatabaseOper::BackupCurrentDatabase(const ImportFileInfo &info) const +{ + storageEngine_->Release(); + // create the pre flag file. + int errCode = OS::CreateFileByFileName(info.curValidFile); + if (errCode != E_OK) { + LOGE("create ctrl file failed:%d.", errCode); + return errCode; + } + + // create backup dir + errCode = DBCommon::CreateDirectory(info.backupDir); + if (errCode != E_OK) { + LOGE("Create backup dir failed:%d.", errCode); + (void)RemoveFile(info.curValidFile); + return errCode; + } + + std::string currentFile = info.currentDir + DBConstant::LOCAL_DATABASE_NAME + + DBConstant::SQLITE_DB_EXTENSION; + std::string backupFile = info.backupDir + DBConstant::LOCAL_DATABASE_NAME + + DBConstant::SQLITE_DB_EXTENSION; + errCode = DBCommon::CopyFile(currentFile, backupFile); + if (errCode != E_OK) { + LOGE("Backup the current database error:%d", errCode); + return errCode; + } + int innerCode = rename(info.curValidFile.c_str(), info.backValidFile.c_str()); + if (innerCode != 0) { + LOGE("Failed to rename the file after the backup:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + } + return errCode; +} + +int LocalDatabaseOper::ImportUnpackedDatabase(const ImportFileInfo &info, const CipherPassword &srcPasswd) const +{ + // create backup dir + int errCode = DBCommon::RemoveAllFilesOfDirectory(info.currentDir, false); + if (errCode != E_OK) { + return errCode; + } + + std::string unpackedFile = info.unpackedDir + DBConstant::LOCAL_DATABASE_NAME + DBConstant::SQLITE_DB_EXTENSION; + std::string currentFile = info.currentDir + DBConstant::LOCAL_DATABASE_NAME + DBConstant::SQLITE_DB_EXTENSION; + CipherType cipherType; + CipherPassword passwd; + localKvDb_->GetDbProperties().GetPassword(cipherType, passwd); + errCode = SQLiteUtils::ExportDatabase(unpackedFile, cipherType, srcPasswd, currentFile, passwd); + DBCommon::RemoveAllFilesOfDirectory(info.unpackedDir); + if (errCode != E_OK) { + LOGE("export the unpacked database to current error:%d", errCode); + errCode = -E_INVALID_FILE; + return errCode; + } + + // reinitialize the database, and delete the backup database. + errCode = localKvDb_->InitDatabaseContext(localKvDb_->GetDbProperties()); + if (errCode != E_OK) { + LOGE("InitDatabaseContext error:%d", errCode); + return errCode; + } + + // rename the flag file. + int innerCode = rename(info.backValidFile.c_str(), info.curValidFile.c_str()); + if (innerCode != E_OK) { + LOGE("Failed to rename after the import operation:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + } + + return errCode; +} + +int LocalDatabaseOper::ImportPostHandle() const +{ + return localKvDb_->InitDatabaseContext(localKvDb_->GetDbProperties()); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/operation/local_database_oper.h b/mock/distributeddb/storage/src/operation/local_database_oper.h new file mode 100644 index 0000000000000000000000000000000000000000..dc0359ed7ec011144336c4ea55ff75d9cf77e4fd --- /dev/null +++ b/mock/distributeddb/storage/src/operation/local_database_oper.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOCAL_DATABASE_OPER_H +#define LOCAL_DATABASE_OPER_H + +#include "database_oper.h" +#include "sqlite_local_kvdb.h" + +namespace DistributedDB { +class LocalDatabaseOper : public DatabaseOper { +public: + LocalDatabaseOper(SQLiteLocalKvDB *localKvDb, SQLiteStorageEngine *storageEngine); + ~LocalDatabaseOper() override {}; + + int Rekey(const CipherPassword &passwd) override; + + int Import(const std::string &filePath, const CipherPassword &passwd) override; + + int Export(const std::string &filePath, const CipherPassword &passwd) const override; + +protected: + bool RekeyPreHandle(const CipherPassword &passwd, int &errCode) override; + + int BackupDb(const CipherPassword &passwd) const override; + + int CloseStorages() override; + + int RekeyPostHandle(const CipherPassword &passwd) override; + + int ExportAllDatabases(const std::string ¤tDir, const CipherPassword &passwd, + const std::string &dbDir) const override; + + int BackupCurrentDatabase(const ImportFileInfo &info) const override; + + int ImportUnpackedDatabase(const ImportFileInfo &info, const CipherPassword &srcPasswd) const override; + + int ImportPostHandle() const override; + +private: + SQLiteLocalKvDB *localKvDb_; + SQLiteStorageEngine *storageEngine_; +}; +} // namespace DistributedDB +#endif // LOCAL_DATABASE_OPER_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/operation/multi_ver_database_oper.cpp b/mock/distributeddb/storage/src/operation/multi_ver_database_oper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..76a33727aa05eee305e5c88f8884281f7e613d60 --- /dev/null +++ b/mock/distributeddb/storage/src/operation/multi_ver_database_oper.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_database_oper.h" + +#include "db_errno.h" +#include "log_print.h" +#include "db_constant.h" +#include "db_common.h" +#include "platform_specific.h" +#include "sqlite_multi_ver_data_storage.h" +#include "multi_ver_natural_store_commit_storage.h" + +namespace DistributedDB { +MultiVerDatabaseOper::MultiVerDatabaseOper(MultiVerNaturalStore *multiVerNaturalStore, + IKvDBMultiVerDataStorage *multiVerData, IKvDBCommitStorage *commitHistory, MultiVerKvDataStorage *multiVerKvStorage) + : multiVerNaturalStore_(multiVerNaturalStore), + multiVerData_(multiVerData), + commitHistory_(commitHistory), + multiVerKvStorage_(multiVerKvStorage) +{} + +int MultiVerDatabaseOper::Rekey(const CipherPassword &passwd) +{ + if (multiVerNaturalStore_ == nullptr || multiVerData_ == nullptr || commitHistory_ == nullptr || + multiVerKvStorage_ == nullptr) { + return -E_INVALID_DB; + } + + return ExecuteRekey(passwd, multiVerNaturalStore_->GetDbProperties()); +} + +int MultiVerDatabaseOper::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (multiVerNaturalStore_ == nullptr || multiVerData_ == nullptr || commitHistory_ == nullptr || + multiVerKvStorage_ == nullptr) { + return -E_INVALID_DB; + } + + return ExecuteImport(filePath, passwd, multiVerNaturalStore_->GetDbProperties()); +} + +int MultiVerDatabaseOper::Export(const std::string &filePath, const CipherPassword &passwd) const +{ + if (multiVerNaturalStore_ == nullptr || multiVerData_ == nullptr || commitHistory_ == nullptr || + multiVerKvStorage_ == nullptr) { + return -E_INVALID_DB; + } + return ExecuteExport(filePath, passwd, multiVerNaturalStore_->GetDbProperties()); +} + +bool MultiVerDatabaseOper::RekeyPreHandle(const CipherPassword &passwd, int &errCode) +{ + CipherType cipherType; + CipherPassword cachePasswd; + multiVerNaturalStore_->GetDbProperties().GetPassword(cipherType, cachePasswd); + + if (cachePasswd.GetSize() == 0 && passwd.GetSize() == 0) { + errCode = E_OK; + return false; + } + + return true; +} + +int MultiVerDatabaseOper::BackupDb(const CipherPassword &passwd) const +{ + std::string filePrefix; + int errCode = GetCtrlFilePrefix(multiVerNaturalStore_->GetDbProperties(), filePrefix); + if (errCode != E_OK) { + return errCode; + } + + // create backup dir + std::string currentDir = filePrefix; + std::string backupDir = filePrefix + DBConstant::PATH_BACKUP_POSTFIX; + + // export db to backup + return ExportAllDatabases(currentDir, passwd, backupDir); +} + +int MultiVerDatabaseOper::CloseStorages() +{ + if (commitHistory_ != nullptr) { + commitHistory_->Close(); + } + if (multiVerData_ != nullptr) { + multiVerData_->Close(); + } + if (multiVerKvStorage_ != nullptr) { + multiVerKvStorage_->Close(); + } + + // rm old dir -> rename backup dir to prime dir -> rm ctrl file + int errCode = RekeyRecover(multiVerNaturalStore_->GetDbProperties()); + if (errCode != E_OK) { + LOGE("Recover failed after run all export ok: %d.", errCode); + } + return errCode; +} + +int MultiVerDatabaseOper::RekeyPostHandle(const CipherPassword &passwd) +{ + CipherType cipherType; + CipherPassword oldPasswd; + multiVerNaturalStore_->GetDbPropertyForUpdate().GetPassword(cipherType, oldPasswd); + multiVerNaturalStore_->GetDbPropertyForUpdate().SetPassword(cipherType, passwd); + + int errCode = multiVerNaturalStore_->InitStorages(multiVerNaturalStore_->GetDbProperties()); + if (errCode != E_OK) { + return errCode; + } + return RekeyRecover(multiVerNaturalStore_->GetDbProperties()); +} + +int MultiVerDatabaseOper::ExportAllDatabases(const std::string ¤tDir, const CipherPassword &passwd, + const std::string &dbDir) const +{ + int errCode = DBCommon::CreateDirectory(dbDir); + if (errCode != E_OK) { + return errCode; + } + CipherType cipherType; + CipherPassword oldPasswd; + multiVerNaturalStore_->GetDbPropertyForUpdate().GetPassword(cipherType, oldPasswd); + errCode = static_cast(multiVerData_)->RunExportLogic(cipherType, passwd, dbDir); + if (errCode != E_OK) { + return errCode; + } + errCode = static_cast(commitHistory_)->RunExportLogic(cipherType, + passwd, dbDir); + if (errCode != E_OK) { + return errCode; + } + errCode = multiVerKvStorage_->RunExportLogic(cipherType, passwd, dbDir); + if (errCode != E_OK) { + return errCode; + } + + std::string versionFile = currentDir + "/version"; + if (OS::CheckPathExistence(versionFile)) { + std::string targetVerFile = dbDir + "/version"; + DBCommon::CopyFile(versionFile, targetVerFile); + } + + return E_OK; +} + +int MultiVerDatabaseOper::BackupCurrentDatabase(const ImportFileInfo &info) const +{ + if (multiVerKvStorage_ == nullptr || commitHistory_ == nullptr || multiVerData_ == nullptr) { + return -E_INVALID_DB; + } + commitHistory_->Close(); + multiVerData_->Close(); + multiVerKvStorage_->Close(); + + // Create the file which imply that the current database files is valid. + int errCode = OS::CreateFileByFileName(info.curValidFile); + if (errCode != E_OK) { + LOGE("Create current valid file failed:%d.", errCode); + return errCode; + } + + std::string dataDir = multiVerNaturalStore_->GetDbProperties().GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string id = multiVerNaturalStore_->GetDbProperties().GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + bool isNeedCreate = multiVerNaturalStore_->GetDbProperties().GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + + CipherType cipherType; + CipherPassword passwd; + multiVerNaturalStore_->GetDbProperties().GetPassword(cipherType, passwd); + + IKvDBMultiVerDataStorage::Property multiVerProp = {dataDir, id, isNeedCreate, cipherType, passwd}; + IKvDBCommitStorage::Property commitProp = {dataDir, id, isNeedCreate, cipherType, passwd}; + MultiVerKvDataStorage::Property multiVerKvProp = {dataDir, id, isNeedCreate, cipherType, passwd}; + + errCode = DBCommon::CreateDirectory(info.backupDir); + if (errCode != E_OK) { + LOGE("Create backup dir failed"); + RemoveFile(info.curValidFile); + return errCode; + } + + errCode = multiVerData_->BackupCurrentDatabase(multiVerProp, info.backupDir); + if (errCode != E_OK) { + return errCode; + } + + errCode = commitHistory_->BackupCurrentDatabase(commitProp, info.backupDir); + if (errCode != E_OK) { + return errCode; + } + + errCode = multiVerKvStorage_->BackupCurrentDatabase(multiVerKvProp, info.backupDir); + if (errCode != E_OK) { + return errCode; + } + + (void)DBCommon::CopyFile(info.currentDir + "/version", info.backupDir + "/version"); + int innerCode = rename(info.curValidFile.c_str(), info.backValidFile.c_str()); + if (innerCode != 0) { + LOGE("Failed to rename the file after the backup:%d", errno); + return -E_SYSTEM_API_FAIL; + } + return E_OK; +} + +int MultiVerDatabaseOper::ImportUnpackedDatabase(const ImportFileInfo &info, const CipherPassword &srcPasswd) const +{ + // create backup dir + int errCode = DBCommon::RemoveAllFilesOfDirectory(info.currentDir, false); + if (errCode != E_OK) { + return errCode; + } + + errCode = ImportDatabase(info.unpackedDir, srcPasswd); + DBCommon::CopyFile(info.unpackedDir + "/version", info.currentDir + "/version"); + (void)DBCommon::RemoveAllFilesOfDirectory(info.unpackedDir); + if (errCode != E_OK) { + LOGE("export the unpacked database to current error:%d", errCode); + errCode = -E_INVALID_FILE; + return errCode; + } + + // reinitialize the database, and delete the backup database. + errCode = multiVerNaturalStore_->InitStorages(multiVerNaturalStore_->GetDbProperties(), true); + if (errCode != E_OK) { + LOGE("InitStorages error:%d", errCode); + return errCode; + } + + // rename the flag file. + int innerCode = rename(info.backValidFile.c_str(), info.curValidFile.c_str()); + if (innerCode != E_OK) { + LOGE("Failed to rename after the import operation:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + } + + return errCode; +} + +int MultiVerDatabaseOper::ImportPostHandle() const +{ + return multiVerNaturalStore_->InitStorages(multiVerNaturalStore_->GetDbProperties()); +} + +// private +int MultiVerDatabaseOper::ImportDatabase(const std::string &dir, const CipherPassword &passwd) const +{ + if (multiVerKvStorage_ == nullptr || commitHistory_ == nullptr || multiVerData_ == nullptr) { + return -E_INVALID_DB; + } + + std::string dataDir = multiVerNaturalStore_->GetDbProperties().GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string id = multiVerNaturalStore_->GetDbProperties().GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + + CipherType cipherType; + CipherPassword currPasswd; + multiVerNaturalStore_->GetDbProperties().GetPassword(cipherType, currPasswd); + + IKvDBMultiVerDataStorage::Property multiVerProp = {dataDir, id, true, cipherType, currPasswd}; + IKvDBCommitStorage::Property commitProp = {dataDir, id, true, cipherType, currPasswd}; + MultiVerKvDataStorage::Property multiVerKvProp = {dataDir, id, true, cipherType, currPasswd}; + int errCode = multiVerData_->ImportDatabase(multiVerProp, dir, passwd); + if (errCode != E_OK) { + return errCode; + } + + errCode = commitHistory_->ImportDatabase(commitProp, dir, passwd); + if (errCode != E_OK) { + return errCode; + } + return multiVerKvStorage_->ImportDatabase(multiVerKvProp, dir, passwd); +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/operation/multi_ver_database_oper.h b/mock/distributeddb/storage/src/operation/multi_ver_database_oper.h new file mode 100644 index 0000000000000000000000000000000000000000..eb2fa31001409b53697060fc5453ba799e353fcd --- /dev/null +++ b/mock/distributeddb/storage/src/operation/multi_ver_database_oper.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_DATABASE_OPER_H +#define MULTI_VER_DATABASE_OPER_H + +#ifndef OMIT_MULTI_VER +#include "database_oper.h" +#include "multi_ver_natural_store.h" + +namespace DistributedDB { +class MultiVerDatabaseOper : public DatabaseOper { +public: + MultiVerDatabaseOper(MultiVerNaturalStore *multiVerNaturalStore, IKvDBMultiVerDataStorage *multiVerData, + IKvDBCommitStorage *commitHistory, MultiVerKvDataStorage *multiVerKvStorage); + ~MultiVerDatabaseOper() override {}; + + int Rekey(const CipherPassword &passwd) override; + + int Import(const std::string &filePath, const CipherPassword &passwd) override; + + int Export(const std::string &filePath, const CipherPassword &passwd) const override; + +protected: + bool RekeyPreHandle(const CipherPassword &passwd, int &errCode) override; + + int BackupDb(const CipherPassword &passwd) const override; + + int CloseStorages() override; + + int RekeyPostHandle(const CipherPassword &passwd) override; + + int ExportAllDatabases(const std::string ¤tDir, const CipherPassword &passwd, + const std::string &dbDir) const override; + + int BackupCurrentDatabase(const ImportFileInfo &info) const override; + + int ImportUnpackedDatabase(const ImportFileInfo &info, const CipherPassword &srcPasswd) const override; + + int ImportPostHandle() const override; + +private: + int ImportDatabase(const std::string &dir, const CipherPassword &passwd) const; + + MultiVerNaturalStore *multiVerNaturalStore_; + IKvDBMultiVerDataStorage *multiVerData_; + IKvDBCommitStorage *commitHistory_; + MultiVerKvDataStorage *multiVerKvStorage_; +}; +} // namespace DistributedDB +#endif // MULTI_VER_DATABASE_OPER_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/operation/single_ver_database_oper.cpp b/mock/distributeddb/storage/src/operation/single_ver_database_oper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6863a736ce625195dcffde98c90d1e4fb607d769 --- /dev/null +++ b/mock/distributeddb/storage/src/operation/single_ver_database_oper.cpp @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_database_oper.h" + +#include "db_errno.h" +#include "log_print.h" +#include "db_constant.h" +#include "db_common.h" +#include "platform_specific.h" + +namespace DistributedDB { +SingleVerDatabaseOper::SingleVerDatabaseOper(SQLiteSingleVerNaturalStore *naturalStore, + SQLiteStorageEngine *storageEngine) + : singleVerNaturalStore_(naturalStore), + storageEngine_(storageEngine) +{} + +int SingleVerDatabaseOper::SetSecOpt(const std::string &path, bool isDir) const +{ + std::string currentMetaPath = path + "/" + DBConstant::METADB_DIR; + std::string currentMainPath = path + "/" + DBConstant::MAINDB_DIR; + if (!isDir) { + currentMetaPath = currentMetaPath + "/" + DBConstant::SINGLE_VER_META_STORE + DBConstant::SQLITE_DB_EXTENSION; + currentMainPath = currentMainPath + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + } + SecurityOption option; + int mainSecLabel = singleVerNaturalStore_->GetDbProperties().GetSecLabel(); + option.securityLabel = ((mainSecLabel >= SecurityLabel::S2) ? SecurityLabel::S2 : mainSecLabel); + int errCode = RuntimeContext::GetInstance()->SetSecurityOption(currentMetaPath, option); + if (errCode != E_OK && errCode != -E_NOT_SUPPORT) { + return errCode; + } + + option.securityLabel = singleVerNaturalStore_->GetDbProperties().GetSecLabel(); + option.securityFlag = singleVerNaturalStore_->GetDbProperties().GetSecFlag(); + errCode = RuntimeContext::GetInstance()->SetSecurityOption(currentMainPath, option); + if (errCode != E_OK && errCode != -E_NOT_SUPPORT) { + return errCode; + } + return E_OK; +} + +int SingleVerDatabaseOper::Rekey(const CipherPassword &passwd) +{ + if (singleVerNaturalStore_ == nullptr || storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + + return ExecuteRekey(passwd, singleVerNaturalStore_->GetDbProperties()); +} + +int SingleVerDatabaseOper::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (singleVerNaturalStore_ == nullptr || storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + + return ExecuteImport(filePath, passwd, singleVerNaturalStore_->GetDbProperties()); +} + +int SingleVerDatabaseOper::Export(const std::string &filePath, const CipherPassword &passwd) const +{ + if (singleVerNaturalStore_ == nullptr || storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + + return ExecuteExport(filePath, passwd, singleVerNaturalStore_->GetDbProperties()); +} + +bool SingleVerDatabaseOper::RekeyPreHandle(const CipherPassword &passwd, int &errCode) +{ + if (singleVerNaturalStore_->GetDbProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false)) { + errCode = -E_NOT_SUPPORT; + return false; + } + + CipherType cipherType; + CipherPassword cachePasswd; + singleVerNaturalStore_->GetDbProperties().GetPassword(cipherType, cachePasswd); + + if (cachePasswd.GetSize() == 0 && passwd.GetSize() == 0) { + errCode = E_OK; + return false; + } + + // need invoke sqlite3 rekey + if (cachePasswd.GetSize() > 0 && passwd.GetSize() > 0) { + errCode = RunRekeyLogic(cipherType, passwd); + return false; + } + + return true; +} + +int SingleVerDatabaseOper::BackupDb(const CipherPassword &passwd) const +{ + std::string filePrefix; + int errCode = GetCtrlFilePrefix(singleVerNaturalStore_->GetDbProperties(), filePrefix); + if (errCode != E_OK) { + return errCode; + } + + // create backup dir + std::string backupDir = filePrefix + DBConstant::PATH_BACKUP_POSTFIX; + errCode = DBCommon::CreateDirectory(backupDir); + if (errCode != E_OK) { + LOGE("create backup dir failed:%d.", errCode); + return errCode; + } + + std::vector dbDir {DBConstant::MAINDB_DIR, DBConstant::METADB_DIR, DBConstant::CACHEDB_DIR}; + for (const auto &item : dbDir) { + if (DBCommon::CreateDirectory(backupDir + "/" + item) != E_OK) { + return -E_SYSTEM_API_FAIL; + } + } + + errCode = SetSecOpt(backupDir, true); + if (errCode != E_OK) { + LOGE("Set backup dir secOption failed, errCode = [%d]", errCode); + return errCode; + } + + // export db to backup + errCode = RunExportLogic(passwd, filePrefix); + if (errCode != E_OK) { + return errCode; + } + + return SetSecOpt(backupDir, false); // set file SecOpt +} + +int SingleVerDatabaseOper::CloseStorages() +{ + // close old db + storageEngine_->Release(); + int errCode = RekeyRecover(singleVerNaturalStore_->GetDbProperties()); + if (errCode != E_OK) { + LOGE("Recover failed after rekey ok:%d.", errCode); + int innerCode = InitStorageEngine(); + if (innerCode != E_OK) { + LOGE("ReInit the handlePool failed:%d", innerCode); + } + } + return errCode; +} + +int SingleVerDatabaseOper::RekeyPostHandle(const CipherPassword &passwd) +{ + CipherType cipherType; + CipherPassword oldPasswd; + singleVerNaturalStore_->GetDbPropertyForUpdate().GetPassword(cipherType, oldPasswd); + singleVerNaturalStore_->GetDbPropertyForUpdate().SetPassword(cipherType, passwd); + singleVerNaturalStore_->GetDbPropertyForUpdate().SetBoolProp( + KvDBProperties::ENCRYPTED_MODE, (passwd.GetSize() == 0) ? false : true); + + return InitStorageEngine(); +} + +int SingleVerDatabaseOper::ExportMainDB(const std::string ¤tDir, const CipherPassword &passwd, + const std::string &dbDir) const +{ + std::string backupDbName = dbDir + DBConstant::MAINDB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + std::string currentDb = currentDir + "/" + DBConstant::MAINDB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + + CipherType cipherType; + CipherPassword currPasswd; + singleVerNaturalStore_->GetDbProperties().GetPassword(cipherType, currPasswd); + LOGI("Begin the sqlite main database export!"); + int errCode = SQLiteUtils::ExportDatabase(currentDb, cipherType, currPasswd, backupDbName, passwd); + if (errCode != E_OK) { + LOGE("Export the database failed:%d", errCode); + } + + return errCode; +} + +int SingleVerDatabaseOper::ExportMetaDB(const std::string ¤tDir, const CipherPassword &passwd, + const std::string &dbDir) const +{ + std::string backupDbName = dbDir + DBConstant::METADB_DIR + "/" + DBConstant::SINGLE_VER_META_STORE + + DBConstant::SQLITE_DB_EXTENSION; + std::string currentDb = currentDir + "/" + DBConstant::METADB_DIR + "/" + DBConstant::SINGLE_VER_META_STORE + + DBConstant::SQLITE_DB_EXTENSION; + if (!OS::CheckPathExistence(currentDb)) { // Is S2 label, can access + LOGD("No metaDB, no need Export metaDB."); + return E_OK; + } + + // Set metaDB db passwd same as mainDB temp, may be not need + LOGI("Begin the sqlite meta database export."); + int errCode = SQLiteUtils::ExportDatabase(currentDb, CipherType::DEFAULT, CipherPassword(), + backupDbName, CipherPassword()); + if (errCode != E_OK) { + LOGE("Export the database failed:%d", errCode); + } + + return errCode; +} + +int SingleVerDatabaseOper::ExportAllDatabases(const std::string ¤tDir, const CipherPassword &passwd, + const std::string &dbDir) const +{ + int errCode = ExportMainDB(currentDir, passwd, dbDir); + if (errCode != E_OK) { + LOGE("Export MainDB fail, errCode = [%d]", errCode); + return errCode; + } + + errCode = ExportMetaDB(currentDir, passwd, dbDir); + if (errCode != E_OK) { + LOGE("Export MetaDB fail, errCode = [%d]", errCode); + return errCode; + } + return errCode; +} + +int SingleVerDatabaseOper::BackupDatabase(const ImportFileInfo &info) const +{ + std::string currentMainFile = info.currentDir + DBConstant::MAINDB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + std::string backupMainFile = info.backupDir + DBConstant::MAINDB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + int errCode = DBCommon::CopyFile(currentMainFile, backupMainFile); + if (errCode != E_OK) { + LOGE("Backup the current database error:%d", errCode); + return errCode; + } + + std::string currentMetaFile = info.currentDir + DBConstant::METADB_DIR + "/" + DBConstant::SINGLE_VER_META_STORE + + DBConstant::SQLITE_DB_EXTENSION; + if (OS::CheckPathExistence(currentMetaFile)) { + std::string backupMetaFile = info.backupDir + DBConstant::METADB_DIR + "/" + DBConstant::SINGLE_VER_META_STORE + + DBConstant::SQLITE_DB_EXTENSION; + errCode = DBCommon::CopyFile(currentMetaFile, backupMetaFile); + if (errCode != E_OK) { + LOGE("Backup the current database error:%d", errCode); + return errCode; + } + } + return E_OK; +} + +int SingleVerDatabaseOper::BackupCurrentDatabase(const ImportFileInfo &info) const +{ + storageEngine_->Release(); + // create the pre flag file. + int errCode = OS::CreateFileByFileName(info.curValidFile); + if (errCode != E_OK) { + LOGE("create ctrl file failed:%d.", errCode); + return errCode; + } + + // create backup dir + errCode = DBCommon::CreateDirectory(info.backupDir); + if (errCode != E_OK) { + LOGE("Create backup dir failed:%d.", errCode); + return errCode; + } + + std::vector dbDir {DBConstant::MAINDB_DIR, DBConstant::METADB_DIR, DBConstant::CACHEDB_DIR}; + for (const auto &item : dbDir) { + if (DBCommon::CreateDirectory(info.backupDir + "/" + item) != E_OK) { + return -E_SYSTEM_API_FAIL; + } + } + + errCode = SetSecOpt(info.backupDir, true); + if (errCode != E_OK) { + LOGE("[singleVer][BackupCurrentDatabase]Set secOpt to dir fail, errCode = [%d]", errCode); + return errCode; + } + + errCode = BackupDatabase(info); + if (errCode != E_OK) { + LOGE("[SingleVerDatabaseOper][BackupCurrentDatabase] backup current database fail, errCode = [%d]", errCode); + return errCode; + } + + // Protect the loss of label information when the abnormal scene is restored + errCode = SetSecOpt(info.backupDir, false); + if (errCode != E_OK) { + LOGE("[singleVer][BackupCurrentDatabase]Set secOpt to file fail, errCode = [%d]", errCode); + return errCode; + } + + // rename + int innerCode = rename(info.curValidFile.c_str(), info.backValidFile.c_str()); + if (innerCode != 0) { + LOGE("Failed to rename the file after the backup:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + } + return errCode; +} + +int SingleVerDatabaseOper::ClearCurrentDatabase(const ImportFileInfo &info) const +{ + int errCode = DBCommon::RemoveAllFilesOfDirectory(info.currentDir, false); + if (errCode != E_OK) { + return errCode; + } + + std::vector dbExtensionVec { DBConstant::MAINDB_DIR, DBConstant::METADB_DIR, DBConstant::CACHEDB_DIR }; + for (const auto &item : dbExtensionVec) { + if (DBCommon::CreateDirectory(info.currentDir + "/" + item) != E_OK) { + return -E_SYSTEM_API_FAIL; + } + } + return errCode; +} + +int SingleVerDatabaseOper::ImportUnpackedMainDatabase(const ImportFileInfo &info, + const CipherPassword &srcPasswd) const +{ + std::string unpackedMainFile = info.unpackedDir + DBConstant::MAINDB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + std::string currentMainFile = info.currentDir + DBConstant::MAINDB_DIR + "/" + + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + CipherType cipherType; + CipherPassword passwd; + singleVerNaturalStore_->GetDbProperties().GetPassword(cipherType, passwd); + + std::string unpackedOldMainFile = info.unpackedDir + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + bool isMainDbExisted = OS::CheckPathExistence(unpackedMainFile); + bool isOldMainDbExisted = OS::CheckPathExistence(unpackedOldMainFile); // version < 3, mainDb in singer_ver/ + if (isMainDbExisted && isOldMainDbExisted) { + LOGE("Unpacked dir existed two diff version mainDb!"); + return -E_INVALID_FILE; + } + + int errCode = E_OK; + if (isMainDbExisted) { + errCode = SQLiteUtils::ExportDatabase(unpackedMainFile, cipherType, srcPasswd, currentMainFile, passwd); + if (errCode != E_OK) { + LOGE("Export the unpacked main database to current error:%d", errCode); + return -E_INVALID_FILE; + } + } + + if (isOldMainDbExisted) { + errCode = SQLiteUtils::ExportDatabase(unpackedOldMainFile, cipherType, srcPasswd, currentMainFile, passwd); + if (errCode != E_OK) { + LOGE("Export the unpacked old version(<3) main database to current error:%d", errCode); + return -E_INVALID_FILE; + } + } + return errCode; +} + +int SingleVerDatabaseOper::ImportUnpackedMetaDatabase(const ImportFileInfo &info) const +{ + LOGI("MetaDB existed, need import, no need upgrade!"); + std::string unpackedMetaFile = info.unpackedDir + DBConstant::METADB_DIR + "/" + + DBConstant::SINGLE_VER_META_STORE + DBConstant::SQLITE_DB_EXTENSION; + std::string currentMetaFile = info.currentDir + DBConstant::METADB_DIR + "/" + + DBConstant::SINGLE_VER_META_STORE + DBConstant::SQLITE_DB_EXTENSION; + int errCode = SQLiteUtils::ExportDatabase(unpackedMetaFile, CipherType::DEFAULT, CipherPassword(), + currentMetaFile, CipherPassword()); + if (errCode != E_OK) { + LOGE("export the unpacked meta database to current error:%d", errCode); + errCode = -E_INVALID_FILE; + } + return errCode; +} + +int SingleVerDatabaseOper::ImportUnpackedDatabase(const ImportFileInfo &info, const CipherPassword &srcPasswd) const +{ + std::string unpackedMetaFile = info.unpackedDir + DBConstant::METADB_DIR + "/" + + DBConstant::SINGLE_VER_META_STORE + DBConstant::SQLITE_DB_EXTENSION; + bool metaDbExisted = OS::CheckPathExistence(unpackedMetaFile); + int errCode = ClearCurrentDatabase(info); + if (errCode != E_OK) { + return errCode; + } + + errCode = ImportUnpackedMainDatabase(info, srcPasswd); + if (errCode != E_OK) { + LOGE("import unpacked mainDb fail, errCode = [%d]", errCode); + return errCode; + } + + if (metaDbExisted) { // Is S2 label, no need deal + errCode = ImportUnpackedMetaDatabase(info); + if (errCode != E_OK) { + LOGE("import unpacked metaDb fail, errCode = [%d]", errCode); + return errCode; + } + } + + (void)SetSecOpt(info.currentDir, false); // not care err, Make sure to set the label + + // reinitialize the database, and delete the backup database. + errCode = singleVerNaturalStore_->InitDatabaseContext(singleVerNaturalStore_->GetDbProperties(), true); + if (errCode != E_OK) { + LOGE("InitDatabaseContext error:%d", errCode); + return errCode; + } + + // rename the flag file. + int innerCode = rename(info.backValidFile.c_str(), info.curValidFile.c_str()); + if (innerCode != E_OK) { + LOGE("Failed to rename after the import operation:%d", errno); + errCode = -E_SYSTEM_API_FAIL; + } + return errCode; +} + +int SingleVerDatabaseOper::ImportPostHandle() const +{ + return singleVerNaturalStore_->InitDatabaseContext(singleVerNaturalStore_->GetDbProperties(), true); +} + +// private begin +int SingleVerDatabaseOper::RunExportLogic(const CipherPassword &passwd, const std::string &filePrefix) const +{ + std::string currentMainDb = filePrefix + "/" + DBConstant::MAINDB_DIR + "/" + + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + CipherType cipherType; + CipherPassword currPasswd; + singleVerNaturalStore_->GetDbProperties().GetPassword(cipherType, currPasswd); + + // get backup db name + std::string backupMainDbName = filePrefix + DBConstant::PATH_BACKUP_POSTFIX + "/" + DBConstant::MAINDB_DIR + "/" + + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + + int errCode = SQLiteUtils::ExportDatabase(currentMainDb, cipherType, currPasswd, backupMainDbName, passwd); + if (errCode != E_OK) { + LOGE("single ver database export mainDb fail, errCode = [%d]", errCode); + return errCode; + } + + std::string currentMetaDb = filePrefix + "/" + DBConstant::METADB_DIR + "/" + + DBConstant::SINGLE_VER_META_STORE + DBConstant::SQLITE_DB_EXTENSION; + if (!OS::CheckPathExistence(currentMetaDb)) { + LOGD("No metaDB, no need Export metaDB."); + return E_OK; + } + + LOGI("Begin export metaDB to back up!"); + std::string backupMetaDbName = filePrefix + DBConstant::PATH_BACKUP_POSTFIX + "/" + DBConstant::METADB_DIR + "/" + + DBConstant::SINGLE_VER_META_STORE + DBConstant::SQLITE_DB_EXTENSION; + // Set metaDB db passwd same as mainDB temp, may be not need + errCode = SQLiteUtils::ExportDatabase(currentMetaDb, CipherType::DEFAULT, CipherPassword(), + backupMetaDbName, CipherPassword()); + if (errCode != E_OK) { + LOGE("single ver database export metaDb fail, errCode = [%d]", errCode); + return errCode; + } + return errCode; +} + +int SingleVerDatabaseOper::InitStorageEngine() +{ + OpenDbProperties option; + InitDataBaseOption(option); + bool isMemoryMode = singleVerNaturalStore_->GetDbProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false); + // Use 1 read handle to check passwd + StorageEngineAttr poolSize = {0, 1, 1, 16}; // at most 1 write 16 read. + if (isMemoryMode) { + poolSize.minWriteNum = 1; // keep at least one connection. + } + + std::string identify = singleVerNaturalStore_->GetDbProperties().GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + int errCode = storageEngine_->InitSQLiteStorageEngine(poolSize, option, identify); + if (errCode != E_OK) { + LOGE("[SingleVerOper]Init the sqlite storage engine failed:%d", errCode); + } + return errCode; +} + +void SingleVerDatabaseOper::InitDataBaseOption(OpenDbProperties &option) const +{ + const KvDBProperties properties = singleVerNaturalStore_->GetDbProperties(); + const std::string dataDir = properties.GetStringProp(KvDBProperties::DATA_DIR, ""); + const std::string identifierDir = properties.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + std::string uri = dataDir + "/" + identifierDir + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::MAINDB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + bool isMemoryDb = properties.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + if (isMemoryDb) { + uri = identifierDir + DBConstant::SQLITE_MEMDB_IDENTIFY; + LOGD("Begin create memory natural store database"); + } + + std::vector createTableSqls; + CipherType cipherType; + CipherPassword passwd; + properties.GetPassword(cipherType, passwd); + bool isCreate = properties.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + + SecurityOption securityOpt; + securityOpt.securityLabel = properties.GetSecLabel(); + securityOpt.securityFlag = properties.GetSecFlag(); + + option = {uri, isCreate, isMemoryDb, createTableSqls, cipherType, passwd}; + std::string dirPath = dataDir + "/" + identifierDir + "/" + DBConstant::SINGLE_SUB_DIR; + option.subdir = dirPath; + option.securityOpt = securityOpt; + option.conflictReslovePolicy = properties.GetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, 0); +} + +int SingleVerDatabaseOper::RunRekeyLogic(CipherType type, const CipherPassword &passwd) +{ + OpenDbProperties option; + InitDataBaseOption(option); + option.createIfNecessary = true; + option.cipherType = type; + sqlite3 *db = nullptr; + + // open one temporary connection. + int errCode = SQLiteUtils::OpenDatabase(option, db); + if (errCode != E_OK) { + LOGE("[RunRekeyLogic] Open database new connect fail!, errCode = [%d]", errCode); + goto END; + } + + errCode = SQLiteUtils::Rekey(db, passwd); + if (errCode != E_OK) { + LOGE("[RunRekeyLogic] Rekey fail!, errCode = [%d]", errCode); + goto END; + } + + // Release all the connections, update the passwd and re-initialize the storage engine. + storageEngine_->Release(); + singleVerNaturalStore_->GetDbPropertyForUpdate().SetPassword(type, passwd); + errCode = InitStorageEngine(); + if (errCode != E_OK) { + LOGE("Init storage engine while rekey open failed:%d", errCode); + } + + // Rekey while locked before init storage engine, it can not open file, but rekey successfully + if (storageEngine_->GetEngineState() != EngineState::MAINDB && errCode == -E_EKEYREVOKED) { + LOGI("Rekey successfully, locked state init state successfully, need ignore open file failed!"); + errCode = -E_FORBID_CACHEDB; + } + +END: + if (db != nullptr) { + (void)sqlite3_close_v2(db); + db = nullptr; + } + return errCode; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/operation/single_ver_database_oper.h b/mock/distributeddb/storage/src/operation/single_ver_database_oper.h new file mode 100644 index 0000000000000000000000000000000000000000..b0ea78704d1198f4ff9853c1d6212f80c1856c5f --- /dev/null +++ b/mock/distributeddb/storage/src/operation/single_ver_database_oper.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_DATABASE_OPER_H +#define SINGLE_VER_DATABASE_OPER_H + +#include "database_oper.h" +#include "sqlite_single_ver_natural_store.h" + +namespace DistributedDB { +class SingleVerDatabaseOper : public DatabaseOper { +public: + SingleVerDatabaseOper(SQLiteSingleVerNaturalStore *naturalStore, SQLiteStorageEngine *storageEngine); + ~SingleVerDatabaseOper() override {}; + + int Rekey(const CipherPassword &passwd) override; + + int Import(const std::string &filePath, const CipherPassword &passwd) override; + + int Export(const std::string &filePath, const CipherPassword &passwd) const override; + +protected: + bool RekeyPreHandle(const CipherPassword &passwd, int &errCode) override; + + int BackupDb(const CipherPassword &passwd) const override; + + int CloseStorages() override; + + int RekeyPostHandle(const CipherPassword &passwd) override; + + int ExportAllDatabases(const std::string ¤tDir, const CipherPassword &passwd, + const std::string &dbDir) const override; + + int BackupCurrentDatabase(const ImportFileInfo &info) const override; + + int ImportUnpackedDatabase(const ImportFileInfo &info, const CipherPassword &srcPasswd) const override; + + int ImportPostHandle() const override; + +private: + int InitStorageEngine(); + + void InitDataBaseOption(OpenDbProperties &option) const; + + int RunExportLogic(const CipherPassword &passwd, const std::string &filePrefix) const; + + int RunRekeyLogic(CipherType type, const CipherPassword &passwd); + + int ExportMainDB(const std::string ¤tDir, const CipherPassword &passwd, const std::string &dbDir) const; + + int ExportMetaDB(const std::string ¤tDir, const CipherPassword &passwd, const std::string &dbDir) const; + + int ClearCurrentDatabase(const ImportFileInfo &info) const; + + int ImportUnpackedMainDatabase(const ImportFileInfo &info, const CipherPassword &srcPasswd) const; + + int ImportUnpackedMetaDatabase(const ImportFileInfo &info) const; + + int SetSecOpt(const std::string &path, bool isDir = true) const; + + int BackupDatabase(const ImportFileInfo &info) const; + + SQLiteSingleVerNaturalStore *singleVerNaturalStore_; + SQLiteStorageEngine *storageEngine_; +}; +} // namespace DistributedDB +#endif // SINGLE_VER_DATABASE_OPER_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/package_file.cpp b/mock/distributeddb/storage/src/package_file.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85eee2744f5fecc4ac2df84866dc1b962bf7ed9d --- /dev/null +++ b/mock/distributeddb/storage/src/package_file.cpp @@ -0,0 +1,571 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "package_file.h" + +#include + +#include "db_errno.h" +#include "value_hash_calc.h" +#include "parcel.h" +#include "platform_specific.h" + +namespace DistributedDB { +using std::string; +using std::vector; +using std::list; +using std::ifstream; +using std::ofstream; +using std::ios; +using std::ios_base; + +namespace { + constexpr uint32_t MAX_FILE_NAME_LEN = 256; + constexpr uint32_t CHECKSUM_LEN = SHA256_DIGEST_LENGTH; + constexpr uint32_t CHECKSUM_BLOCK_SIZE = 64; + constexpr uint32_t DEVICE_ID_LEN = SHA256_DIGEST_LENGTH; + constexpr uint32_t MAGIC_LEN = 16; + constexpr uint32_t CURRENT_VERSION = 0; + constexpr uint64_t BUFFER_LEN = 4096; + const string MAGIC = "HW package file"; + const string FILE_SEPARATOR = "/"; + const string INVALID_FILE_WORDS = ".."; + + const uint32_t FILE_HEADER_LEN = MAGIC_LEN + CHECKSUM_LEN + DEVICE_ID_LEN + Parcel::GetUInt32Len() * 3; + const uint32_t FILE_CONTEXT_LEN = MAX_FILE_NAME_LEN + Parcel::GetUInt32Len() * 2 + Parcel::GetUInt64Len() * 2; +} + +struct FileContext { + char fileName[MAX_FILE_NAME_LEN] = {0}; + uint32_t fileType = 0; + uint32_t parentID = 0; + uint64_t fileLen = 0; + uint64_t offset = 0; +}; + +static void Clear(ofstream &target, string targetFile) +{ + if (target.is_open()) { + target.close(); + } + if (OS::RemoveFile(targetFile.c_str()) != E_OK) { + LOGE("Remove file failed."); + } + return; +} + +static int GetChecksum(const string &file, vector &result) +{ + ifstream fileHandle(file, ios::in | ios::binary); + if (!fileHandle.good()) { + LOGE("[GetChecksum]Error fileHandle!"); + return -E_INVALID_PATH; + } + ValueHashCalc calc; + int errCode = calc.Initialize(); + if (errCode != E_OK) { + LOGE("[GetChecksum]Calc Initialize fail!"); + return errCode; + } + fileHandle.seekg(static_cast(MAGIC_LEN + Parcel::GetUInt32Len() + CHECKSUM_LEN), ios_base::beg); + vector buffer(CHECKSUM_BLOCK_SIZE, 0); + bool readEnd = false; + while (!readEnd) { + fileHandle.read(reinterpret_cast(buffer.data()), buffer.size()); + if (fileHandle.eof()) { + readEnd = true; + } else if (!fileHandle.good()) { + LOGE("[GetChecksum]fileHandle error!"); + return -E_INVALID_PATH; + } + errCode = calc.Update(buffer); + if (errCode != E_OK) { + LOGE("[GetChecksum]Calc Update fail!"); + return errCode; + } + buffer.assign(CHECKSUM_BLOCK_SIZE, 0); + } + vector resultBuf; + errCode = calc.GetResult(resultBuf); + if (errCode != E_OK) { + LOGE("[GetChecksum]Calc GetResult fail!"); + return errCode; + } + result.assign(resultBuf.begin(), resultBuf.end()); + return E_OK; +} + +static int GetFileContexts(const string &sourcePath, list &fileContexts) +{ + list files; + int errCode = OS::GetFileAttrFromPath(sourcePath, files, false); + if (errCode != E_OK) { + LOGE("[GetFileContexts] get file attr from path fail, errCode = [%d]", errCode); + return errCode; + } + FileContext fileContext; + int countLimit = 0; + for (auto file = files.begin(); file != files.end(); file++, countLimit++) { + if (countLimit > 20) { // Limit number of files 20 for security + LOGE("Too deep access for get file context!"); + return -E_INVALID_PATH; + } + + if (file->fileType != OS::FILE && file->fileType != OS::PATH) { + continue; + } + + errCode = memset_s(fileContext.fileName, MAX_FILE_NAME_LEN, 0, MAX_FILE_NAME_LEN); + if (errCode != EOK) { + return -E_SECUREC_ERROR; + } + + if (file->fileName.size() >= MAX_FILE_NAME_LEN) { + LOGE("file name is too long!"); + return -E_INVALID_FILE; + } + + errCode = memcpy_s(fileContext.fileName, MAX_FILE_NAME_LEN, file->fileName.c_str(), file->fileName.size()); + if (errCode != EOK) { + return -E_SECUREC_ERROR; + } + + fileContext.fileLen = file->fileLen; + fileContext.fileType = file->fileType; + fileContexts.push_back(fileContext); + } + LOGD("Get file contexts, fileContexts size is [%zu]", fileContexts.size()); + return E_OK; +} + +static int FileContentCopy(ifstream &sourceFile, ofstream &targetFile, uint64_t fileLen) +{ + uint64_t leftLen = fileLen; + vector buffer(BUFFER_LEN, 0); + while (leftLen > 0) { + uint64_t readLen = (leftLen > BUFFER_LEN) ? BUFFER_LEN : leftLen; + sourceFile.read(buffer.data(), readLen); + if (!sourceFile.good()) { + LOGE("[FileContentCopy] SourceFile error! sys[%d]", errno); + return -E_INVALID_PATH; + } + targetFile.write(buffer.data(), readLen); + if (!targetFile.good()) { + LOGE("[FileContentCopy] TargetFile error! sys[%d]", errno); + return -E_INVALID_PATH; + } + leftLen -= readLen; + } + return E_OK; +} + +static int PackFileHeader(ofstream &targetFile, const FileInfo &fileInfo, uint32_t fileNum) +{ + if (fileInfo.deviceID.size() != DEVICE_ID_LEN) { + return -E_INVALID_ARGS; + } + vector buffer(FILE_HEADER_LEN, 0); + vector checksum(CHECKSUM_LEN, 0); + Parcel parcel(buffer.data(), FILE_HEADER_LEN); + + int errCode = parcel.WriteBlob(MAGIC.c_str(), MAGIC_LEN); + if (errCode != E_OK) { + return errCode; + } + // before current version package version is always 0 + errCode = parcel.WriteUInt32(CURRENT_VERSION); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteBlob(checksum.data(), CHECKSUM_LEN); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteBlob(fileInfo.deviceID.c_str(), DEVICE_ID_LEN); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt32(fileInfo.dbType); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt32(fileNum); + if (errCode != E_OK) { + return errCode; + } + targetFile.write(reinterpret_cast(buffer.data()), buffer.size()); + if (!targetFile.good()) { + return -E_INVALID_PATH; + } + return E_OK; +} + +static int CheckMagicHeader(Parcel &fileHeaderParcel) +{ + vector buffer(MAGIC_LEN, 0); + (void)fileHeaderParcel.ReadBlob(buffer.data(), MAGIC_LEN); + if (fileHeaderParcel.IsError()) { + LOGE("[CheckMagicHeader]fileHeaderParcel error!"); + return -E_PARSE_FAIL; + } + if (memcmp(MAGIC.c_str(), buffer.data(), MAGIC_LEN) != 0) { + return -E_INVALID_FILE; + } + return E_OK; +} + +static int UnpackFileHeader(ifstream &sourceFile, const string &sourceFileName, FileInfo &fileInfo, uint32_t &fileNum) +{ + vector fileHeader(FILE_HEADER_LEN, 0); + sourceFile.read(reinterpret_cast(fileHeader.data()), FILE_HEADER_LEN); + if (!sourceFile.good()) { + LOGE("UnpackFileHeader sourceFile error!"); + return -E_INVALID_FILE; + } + Parcel parcel(fileHeader.data(), FILE_HEADER_LEN); + int errCode = CheckMagicHeader(parcel); + if (errCode != E_OK) { + return errCode; + } + uint32_t version; + vector buffer(CHECKSUM_LEN, 0); + (void)parcel.ReadUInt32(version); + (void)parcel.ReadBlob(buffer.data(), CHECKSUM_LEN); + if (parcel.IsError()) { + LOGE("UnpackFileHeader parcel version error!"); + return -E_PARSE_FAIL; + } + vector checksum(CHECKSUM_LEN, 0); + errCode = GetChecksum(sourceFileName, checksum); + if (errCode != E_OK) { + LOGE("Get checksum failed."); + return errCode; + } + if (buffer != checksum) { + LOGE("Checksum check failed."); + return -E_INVALID_FILE; + } + buffer.resize(DEVICE_ID_LEN); + (void)parcel.ReadBlob(buffer.data(), DEVICE_ID_LEN); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + fileInfo.deviceID.resize(DEVICE_ID_LEN); + fileInfo.deviceID.assign(buffer.begin(), buffer.end()); + (void)parcel.ReadUInt32(fileInfo.dbType); + (void)parcel.ReadUInt32(fileNum); + if (parcel.IsError()) { + LOGE("UnpackFileHeader parcel dbType error!"); + return -E_PARSE_FAIL; + } + return E_OK; +} + +static int PackFileContext(ofstream &targetFile, const FileContext &fileContext) +{ + vector buffer(FILE_CONTEXT_LEN, 0); + Parcel parcel(buffer.data(), FILE_CONTEXT_LEN); + int errCode = parcel.WriteBlob(fileContext.fileName, MAX_FILE_NAME_LEN); + if (errCode != E_OK) { + LOGE("PackFileContext fileContext fileName error!"); + return errCode; + } + errCode = parcel.WriteUInt32(fileContext.fileType); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt32(0); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt64(fileContext.fileLen); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt64(fileContext.offset); + if (errCode != E_OK) { + return errCode; + } + targetFile.write(reinterpret_cast(buffer.data()), buffer.size()); + if (!targetFile.good()) { + return -E_INVALID_PATH; + } + return E_OK; +} + +static int UnpackFileContext(ifstream &sourceFile, FileContext &fileContext) +{ + vector buffer(FILE_CONTEXT_LEN, 0); + sourceFile.read(reinterpret_cast(buffer.data()), buffer.size()); + if (!sourceFile.good()) { + return -E_INVALID_PATH; + } + Parcel parcel(buffer.data(), FILE_CONTEXT_LEN); + (void)parcel.ReadBlob(fileContext.fileName, MAX_FILE_NAME_LEN); + (void)parcel.ReadUInt32(fileContext.fileType); + (void)parcel.ReadUInt32(fileContext.parentID); + (void)parcel.ReadUInt64(fileContext.fileLen); + (void)parcel.ReadUInt64(fileContext.offset); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + return E_OK; +} + +static int PackFileContent(ofstream &targetFile, const string &sourcePath, const FileContext &fileContext) +{ + if (fileContext.fileType != OS::FILE) { + return E_OK; + } + string fileName = sourcePath + fileContext.fileName; + ifstream file(fileName, ios::in | ios::binary); + if (!file.good()) { + LOGE("[PackFileContent] File error! sys[%d]", errno); + return -E_INVALID_PATH; + } + file.seekg(0, ios_base::end); + if (!file.good()) { + LOGE("[PackFileContent]file error after seekg! sys[%d]", errno); + return -E_INVALID_PATH; + } + if (file.tellg() < 0) { + LOGE("[PackFileContent]file error after tellg! sys[%d]", errno); + return -E_INVALID_PATH; + } + uint64_t fileLen = static_cast(file.tellg()); + file.seekg(0, ios_base::beg); + if (!file.good()) { + LOGE("[PackFileContent]file error after seekg fileLen! sys[%d]", errno); + return -E_INVALID_PATH; + } + + return FileContentCopy(file, targetFile, fileLen); +} + +static int UnpackFileContent(ifstream &sourceFile, const string &targetPath, const FileContext &fileContext) +{ + if (fileContext.fileType != OS::FILE) { + return E_OK; + } + + string fileName = fileContext.fileName; + fileName = targetPath + FILE_SEPARATOR + fileName; + + // check if fileName contains the words ".." + std::string::size_type pos = fileName.find(INVALID_FILE_WORDS); + if (pos != std::string::npos) { + LOGE("[UnpackFileContent]fileName contains the words double dot!!!"); + return -E_INVALID_PATH; + } + + ofstream file(fileName, ios::out | ios::binary); + if (!file.good()) { + file.close(); + LOGE("[UnpackFileContent]Get checksum failed."); + return -E_INVALID_PATH; + } + int errCode = FileContentCopy(sourceFile, file, fileContext.fileLen); + file.close(); + return errCode; +} + +static int WriteChecksum(const string &targetFile) +{ + vector checksum(CHECKSUM_LEN, 0); + int errCode = GetChecksum(targetFile, checksum); + if (errCode != E_OK) { + LOGE("Get checksum failed."); + return errCode; + } + ofstream targetHandle(targetFile, ios::in | ios::out | ios::binary); + if (!targetHandle.good()) { + Clear(targetHandle, targetFile); + LOGE("[WriteChecksum]targetHandle error, sys err [%d]", errno); + return -E_INVALID_PATH; + } + targetHandle.seekp(static_cast(MAGIC_LEN + Parcel::GetUInt32Len()), ios_base::beg); + if (!targetHandle.good()) { + Clear(targetHandle, targetFile); + LOGE("[WriteChecksum]targetHandle error after seekp, sys err [%d]", errno); + return -E_INVALID_PATH; + } + targetHandle.write(checksum.data(), checksum.size()); + if (!targetHandle.good()) { + Clear(targetHandle, targetFile); + LOGE("[WriteChecksum]targetHandle error after write, sys err [%d]", errno); + return -E_INVALID_PATH; + } + targetHandle.close(); + return E_OK; +} + +static int CopyFilePermissions(const string &sourceFile, const string &targetFile) +{ + uint32_t permissions; + int errCode = OS::GetFilePermissions(sourceFile, permissions); + if (errCode != E_OK) { + LOGE("Get file permissions failed."); + return errCode; + } + errCode = OS::SetFilePermissions(targetFile, permissions); + if (errCode != E_OK) { + LOGE("Set file permissions failed."); + } + return errCode; +} + +int PackageFile::PackageFiles(const string &sourcePath, const string &targetFile, + const FileInfo &fileInfo) +{ + int errCode = ExePackage(sourcePath, targetFile, fileInfo); + if (errno == EKEYREVOKED) { + errCode = -E_EKEYREVOKED; + LOGE("[PackageFile][PackageFiles] Forbid access files errCode [%d].", errCode); + } + return errCode; +} + +int PackageFile::GetPackageVersion(const std::string &sourceFile, uint32_t &version) +{ + int errCode = E_OK; + vector fileHeader(FILE_HEADER_LEN, 0); + Parcel parcel(fileHeader.data(), FILE_HEADER_LEN); + + ifstream sourceHandle(sourceFile, ios::in | ios::binary); + if (!sourceHandle.good()) { + LOGE("sourceHandle error, sys err [%d]", errno); + errCode = -E_INVALID_PATH; + goto END; + } + + sourceHandle.read(reinterpret_cast(fileHeader.data()), FILE_HEADER_LEN); + if (!sourceHandle.good()) { + LOGE("GetPackageVersion read sourceFile handle error!"); + errCode = -E_INVALID_PATH; + goto END; + } + + errCode = CheckMagicHeader(parcel); + if (errCode != E_OK) { + errCode = -E_INVALID_PATH; + goto END; + } + + (void)parcel.ReadUInt32(version); +END: + if (errno == EKEYREVOKED) { + errCode = -E_EKEYREVOKED; + LOGE("[PackageFile][PackageFiles] Forbid access files by secLabel, errCode [%d].", errCode); + } + return errCode; +} + +int PackageFile::ExePackage(const string &sourcePath, const string &targetFile, + const FileInfo &fileInfo) +{ + list fileContexts; + int errCode = GetFileContexts(sourcePath, fileContexts); + if (errCode != E_OK) { + return errCode; + } + if (fileContexts.empty()) { + return -E_EMPTY_PATH; + } + ofstream targetHandle(targetFile, ios::out | ios::binary); + if (!targetHandle.good()) { + Clear(targetHandle, targetFile); + LOGE("[PackageFiles]targetHandle error, sys err [%d], [%zu]", errno, fileContexts.size()); + return -E_INVALID_PATH; + } + + errCode = CopyFilePermissions(sourcePath + FILE_SEPARATOR + string(fileContexts.front().fileName), targetFile); + if (errCode != E_OK) { + LOGE("Copy file fail when execute pack files! errCode = [%d]", errCode); + Clear(targetHandle, targetFile); + return errCode; + } + + errCode = PackFileHeader(targetHandle, fileInfo, static_cast(fileContexts.size())); + if (errCode != E_OK) { + Clear(targetHandle, targetFile); + LOGE("[PackageFiles]Pack file header err[%d]!!!", errCode); + return errCode; + } + // FILE_HEADER_LEN is 92, FILE_CONTEXT_LEN is 280, fileContexts.size() < UINT_MAX, the offset will never overflow. + uint64_t offset = FILE_HEADER_LEN + FILE_CONTEXT_LEN * static_cast(fileContexts.size()); + for (auto &file : fileContexts) { + file.offset = offset; + errCode = PackFileContext(targetHandle, file); + if (errCode != E_OK) { + Clear(targetHandle, targetFile); + LOGE("[PackageFiles]Pack file context err[%d]!!!", errCode); + return errCode; + } + offset += file.fileLen; + } + for (const auto &file : fileContexts) { + // If file type is path no need pack content in PackFileContent + errCode = PackFileContent(targetHandle, sourcePath, file); + if (errCode != E_OK) { + Clear(targetHandle, targetFile); + return errCode; + } + } + targetHandle.close(); + return WriteChecksum(targetFile); +} + +int PackageFile::UnpackFile(const string &sourceFile, const string &targetPath, FileInfo &fileInfo) +{ + ifstream sourceHandle(sourceFile, ios::in | ios::binary); + if (!sourceHandle.good()) { + LOGE("sourceHandle error, sys err [%d]", errno); + return -E_INVALID_PATH; + } + uint32_t fileNum; + int errCode = UnpackFileHeader(sourceHandle, sourceFile, fileInfo, fileNum); + if (errCode != E_OK) { + return errCode; + } + FileContext fileContext; + list fileContexts; + sourceHandle.seekg(static_cast(FILE_HEADER_LEN), ios_base::beg); + if (!sourceHandle.good()) { + return -E_INVALID_PATH; + } + for (uint32_t fileCount = 0; fileCount < fileNum; fileCount++) { + errCode = UnpackFileContext(sourceHandle, fileContext); + if (errCode != E_OK) { + return errCode; + } + fileContexts.push_back(fileContext); + } + + for (const auto &file : fileContexts) { + if (file.fileType == OS::PATH) { + std::string dirPath = targetPath + "/" + std::string(file.fileName); + if (!OS::CheckPathExistence(dirPath) && OS::MakeDBDirectory(dirPath) != E_OK) { + return -E_SYSTEM_API_FAIL; + } + continue; + } + errCode = UnpackFileContent(sourceHandle, targetPath, file); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} +} diff --git a/mock/distributeddb/storage/src/package_file.h b/mock/distributeddb/storage/src/package_file.h new file mode 100644 index 0000000000000000000000000000000000000000..c404091e453b70257d8d05229571f001d13ef0e3 --- /dev/null +++ b/mock/distributeddb/storage/src/package_file.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PACKAGE_FILE_H +#define PACKAGE_FILE_H + +#include +namespace DistributedDB { +struct FileInfo { + uint32_t dbType; + std::string deviceID; +}; + +class PackageFile { +public: + PackageFile() {} + ~PackageFile() {} + static int PackageFiles(const std::string &sourcePath, const std::string &targetFile, const FileInfo &fileInfo); + static int UnpackFile(const std::string &sourceFile, const std::string &targetPath, FileInfo &fileInfo); + static int GetPackageVersion(const std::string &sourceFile, uint32_t &version); +private: + static int ExePackage(const std::string &sourcePath, const std::string &targetFile, const FileInfo &fileInfo); +}; +} + +#endif // PACKAGE_FILE_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/relational_store_connection.cpp b/mock/distributeddb/storage/src/relational_store_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..76b7fffcddf6912759d7ae771bff2534af830f1f --- /dev/null +++ b/mock/distributeddb/storage/src/relational_store_connection.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "relational_store_connection.h" +#include "db_errno.h" +#include "sqlite_single_ver_relational_storage_executor.h" + +namespace DistributedDB { +RelationalStoreConnection::RelationalStoreConnection() : isExclusive_(false) +{} + +RelationalStoreConnection::RelationalStoreConnection(IRelationalStore *store) + : store_(store), isExclusive_(false) +{} + +int RelationalStoreConnection::Pragma(int cmd, void *parameter) +{ + (void) cmd; + (void) parameter; + return E_OK; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/relational_store_instance.cpp b/mock/distributeddb/storage/src/relational_store_instance.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca1c40b18090beb36a470650184eaa333b3310c7 --- /dev/null +++ b/mock/distributeddb/storage/src/relational_store_instance.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "relational_store_instance.h" + +#include +#include + +#include "db_common.h" +#include "db_errno.h" +#include "sqlite_relational_store.h" +#include "log_print.h" + +namespace DistributedDB { +RelationalStoreInstance *RelationalStoreInstance::instance_ = nullptr; +std::mutex RelationalStoreInstance::instanceLock_; + +static std::mutex storeLock_; +static std::map dbs_; + +RelationalStoreInstance::RelationalStoreInstance() +{} + +RelationalStoreInstance *RelationalStoreInstance::GetInstance() +{ + std::lock_guard lockGuard(instanceLock_); + if (instance_ == nullptr) { + instance_ = new (std::nothrow) RelationalStoreInstance(); + if (instance_ == nullptr) { + LOGE("failed to new RelationalStoreManager!"); + return nullptr; + } + } + return instance_; +} + +int RelationalStoreInstance::ReleaseDataBaseConnection(RelationalStoreConnection *connection) +{ + if (connection == nullptr) { + return -E_INVALID_DB; + } + auto manager = RelationalStoreInstance::GetInstance(); + if (manager == nullptr) { + return -E_OUT_OF_MEMORY; + } + std::string identifier = connection->GetIdentifier(); + manager->EnterDBOpenCloseProcess(identifier); + int errCode = connection->Close(); + manager->ExitDBOpenCloseProcess(identifier); + + if (errCode != E_OK) { + LOGE("Release db connection failed. %d", errCode); + } + return errCode; +} + +int RelationalStoreInstance::CheckDatabaseFileStatus(const std::string &id) +{ + std::lock_guard lockGuard(storeLock_); + if (dbs_.count(id) != 0 && dbs_[id] != nullptr) { + return -E_BUSY; + } + return E_OK; +} + +static IRelationalStore *GetFromCache(const RelationalDBProperties &properties, int &errCode) +{ + errCode = E_OK; + std::string identifier = properties.GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, ""); + std::lock_guard lockGuard(storeLock_); + auto iter = dbs_.find(identifier); + if (iter == dbs_.end()) { + errCode = -E_NOT_FOUND; + return nullptr; + } + + auto *db = iter->second; + if (db == nullptr) { + LOGE("Store cache is nullptr, there may be a logic error"); + errCode = -E_INTERNAL_ERROR; + return nullptr; + } + db->IncObjRef(db); + return db; +} + +// Save to IKvDB to the global map +void RelationalStoreInstance::RemoveKvDBFromCache(const RelationalDBProperties &properties) +{ + std::string identifier = properties.GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, ""); + std::lock_guard lockGuard(storeLock_); + dbs_.erase(identifier); +} + +void RelationalStoreInstance::SaveRelationalDBToCache(IRelationalStore *store, const RelationalDBProperties &properties) +{ + std::string identifier = properties.GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, ""); + std::lock_guard lockGuard(storeLock_); + if (dbs_.count(identifier) == 0) { + dbs_.insert(std::pair(identifier, store)); + } +} + +IRelationalStore *RelationalStoreInstance::OpenDatabase(const RelationalDBProperties &properties, int &errCode) +{ + auto db = new (std::nothrow) SQLiteRelationalStore(); + if (db == nullptr) { + errCode = -E_OUT_OF_MEMORY; + LOGE("Failed to get relational store! err:%d", errCode); + return nullptr; + } + + db->OnClose([this, properties]() { + LOGI("Remove from the cache"); + this->RemoveKvDBFromCache(properties); + }); + + errCode = db->Open(properties); + if (errCode != E_OK) { + LOGE("Failed to open db! err:%d", errCode); + RefObject::KillAndDecObjRef(db); + return nullptr; + } + db->WakeUpSyncer(); + + SaveRelationalDBToCache(db, properties); + return db; +} + +IRelationalStore *RelationalStoreInstance::GetDataBase(const RelationalDBProperties &properties, int &errCode) +{ + auto *db = GetFromCache(properties, errCode); + if (db != nullptr) { + LOGD("Get db from cache."); + return db; + } + + // file lock + RelationalStoreInstance *manager = RelationalStoreInstance::GetInstance(); + if (manager == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + + db = manager->OpenDatabase(properties, errCode); + if (errCode != E_OK) { + LOGE("Create data base failed, errCode = [%d]", errCode); + } + return db; +} + +RelationalStoreConnection *RelationalStoreInstance::GetDatabaseConnection(const RelationalDBProperties &properties, + int &errCode) +{ + std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + LOGD("Begin to get [%s] database connection.", STR_MASK(DBCommon::TransferStringToHex(identifier))); + RelationalStoreInstance *manager = RelationalStoreInstance::GetInstance(); + if (manager == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + manager->EnterDBOpenCloseProcess(properties.GetStringProp(DBProperties::IDENTIFIER_DATA, "")); + RelationalStoreConnection *connection = nullptr; + std::string canonicalDir; + IRelationalStore *db = GetDataBase(properties, errCode); + if (db == nullptr) { + LOGE("Failed to open the db:%d", errCode); + goto END; + } + + canonicalDir = properties.GetStringProp(KvDBProperties::DATA_DIR, ""); + if (canonicalDir.empty() || canonicalDir != db->GetStorePath()) { + LOGE("Failed to check store path, the input path does not match with cached store."); + errCode = -E_INVALID_ARGS; + goto END; + } + + connection = db->GetDBConnection(errCode); + if (connection == nullptr) { // not kill db, Other operations like import may be used concurrently + LOGE("Failed to get the db connect for delegate:%d", errCode); + } + +END: + RefObject::DecObjRef(db); // restore the reference increased by the cache. + manager->ExitDBOpenCloseProcess(properties.GetStringProp(DBProperties::IDENTIFIER_DATA, "")); + return connection; +} + +void RelationalStoreInstance::EnterDBOpenCloseProcess(const std::string &identifier) +{ + std::unique_lock lock(relationalDBOpenMutex_); + relationalDBOpenCondition_.wait(lock, [this, &identifier]() { + return this->relationalDBOpenSet_.count(identifier) == 0; + }); + (void)relationalDBOpenSet_.insert(identifier); +} + +void RelationalStoreInstance::ExitDBOpenCloseProcess(const std::string &identifier) +{ + std::unique_lock lock(relationalDBOpenMutex_); + (void)relationalDBOpenSet_.erase(identifier); + relationalDBOpenCondition_.notify_all(); +} + +void RelationalStoreInstance::Dump(int fd) +{ + std::lock_guard autoLock(storeLock_); + for (const auto &entry : dbs_) { + RefObject::IncObjRef(entry.second); + entry.second->Dump(fd); + RefObject::DecObjRef(entry.second); + } +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/relational_sync_able_storage.cpp b/mock/distributeddb/storage/src/relational_sync_able_storage.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dbaf8f9bb2b7aad24564ea585492348cbf99b6fc --- /dev/null +++ b/mock/distributeddb/storage/src/relational_sync_able_storage.cpp @@ -0,0 +1,618 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "relational_sync_able_storage.h" + +#include "data_compression.h" +#include "db_common.h" +#include "db_dfx_adapter.h" +#include "generic_single_ver_kv_entry.h" +#include "platform_specific.h" +#include "runtime_context.h" + +namespace DistributedDB { +#define CHECK_STORAGE_ENGINE do { \ + if (storageEngine_ == nullptr) { \ + return -E_INVALID_DB; \ + } \ +} while (0) + +RelationalSyncAbleStorage::RelationalSyncAbleStorage(StorageEngine *engine) + : storageEngine_(static_cast(engine)) +{} + +RelationalSyncAbleStorage::~RelationalSyncAbleStorage() +{} + +// Get interface type of this relational db. +int RelationalSyncAbleStorage::GetInterfaceType() const +{ + return SYNC_RELATION; +} + +// Get the interface ref-count, in order to access asynchronously. +void RelationalSyncAbleStorage::IncRefCount() +{ + LOGD("RelationalSyncAbleStorage ref +1"); + IncObjRef(this); +} + +// Drop the interface ref-count. +void RelationalSyncAbleStorage::DecRefCount() +{ + LOGD("RelationalSyncAbleStorage ref -1"); + DecObjRef(this); +} + +// Get the identifier of this rdb. +std::vector RelationalSyncAbleStorage::GetIdentifier() const +{ + std::string identifier = storageEngine_->GetIdentifier(); + return std::vector(identifier.begin(), identifier.end()); +} + +// Get the max timestamp of all entries in database. +void RelationalSyncAbleStorage::GetMaxTimestamp(Timestamp ×tamp) const +{ + int errCode = E_OK; + auto handle = GetHandle(false, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return; + } + timestamp = 0; + errCode = handle->GetMaxTimestamp(storageEngine_->GetSchemaRef().GetTableNames(), timestamp); + if (errCode != E_OK) { + LOGE("GetMaxTimestamp failed, errCode:%d", errCode); + } + ReleaseHandle(handle); + return; +} + +int RelationalSyncAbleStorage::GetMaxTimestamp(const std::string &tableName, Timestamp ×tamp) const +{ + int errCode = E_OK; + auto handle = GetHandle(false, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return errCode; + } + timestamp = 0; + errCode = handle->GetMaxTimestamp({ tableName }, timestamp); + if (errCode != E_OK) { + LOGE("GetMaxTimestamp failed, errCode:%d", errCode); + } + ReleaseHandle(handle); + return errCode; +} + +SQLiteSingleVerRelationalStorageExecutor *RelationalSyncAbleStorage::GetHandle(bool isWrite, int &errCode, + OperatePerm perm) const +{ + if (storageEngine_ == nullptr) { + errCode = -E_INVALID_DB; + return nullptr; + } + return static_cast(storageEngine_->FindExecutor(isWrite, perm, + errCode)); +} + +void RelationalSyncAbleStorage::ReleaseHandle(SQLiteSingleVerRelationalStorageExecutor *&handle) const +{ + if (storageEngine_ == nullptr) { + return; + } + StorageExecutor *databaseHandle = handle; + storageEngine_->Recycle(databaseHandle); + std::function listener = nullptr; + { + std::lock_guard autoLock(heartBeatMutex_); + listener = heartBeatListener_; + } + if (listener) { + listener(); + } +} + +// Get meta data associated with the given key. +int RelationalSyncAbleStorage::GetMetaData(const Key &key, Value &value) const +{ + CHECK_STORAGE_ENGINE; + if (key.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + auto handle = GetHandle(true, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return errCode; + } + errCode = handle->GetKvData(key, value); + ReleaseHandle(handle); + return errCode; +} + +// Put meta data as a key-value entry. +int RelationalSyncAbleStorage::PutMetaData(const Key &key, const Value &value) +{ + CHECK_STORAGE_ENGINE; + int errCode = E_OK; + auto *handle = GetHandle(true, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->PutKvData(key, value); // meta doesn't need time. + if (errCode != E_OK) { + LOGE("Put kv data err:%d", errCode); + } + ReleaseHandle(handle); + return errCode; +} + +// Delete multiple meta data records in a transaction. +int RelationalSyncAbleStorage::DeleteMetaData(const std::vector &keys) +{ + for (const auto &key : keys) { + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + } + int errCode = E_OK; + auto handle = GetHandle(true, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return errCode; + } + + handle->StartTransaction(TransactType::IMMEDIATE); + errCode = handle->DeleteMetaData(keys); + if (errCode != E_OK) { + handle->Rollback(); + LOGE("[SinStore] DeleteMetaData failed, errCode = %d", errCode); + } else { + handle->Commit(); + } + ReleaseHandle(handle); + return errCode; +} + +// Delete multiple meta data records with key prefix in a transaction. +int RelationalSyncAbleStorage::DeleteMetaDataByPrefixKey(const Key &keyPrefix) const +{ + if (keyPrefix.empty() || keyPrefix.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + auto handle = GetHandle(true, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->DeleteMetaDataByPrefixKey(keyPrefix); + if (errCode != E_OK) { + LOGE("[SinStore] DeleteMetaData by prefix key failed, errCode = %d", errCode); + } + ReleaseHandle(handle); + return errCode; +} + +// Get all meta data keys. +int RelationalSyncAbleStorage::GetAllMetaKeys(std::vector &keys) const +{ + CHECK_STORAGE_ENGINE; + int errCode = E_OK; + auto *handle = GetHandle(true, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->GetAllMetaKeys(keys); + ReleaseHandle(handle); + return errCode; +} + +const KvDBProperties &RelationalSyncAbleStorage::GetDbProperties() const +{ + return properties_; +} + +static int GetKvEntriesByDataItems(std::vector &entries, std::vector &dataItems) +{ + int errCode = E_OK; + for (auto &item : dataItems) { + auto entry = new (std::nothrow) GenericSingleVerKvEntry(); + if (entry == nullptr) { + errCode = -E_OUT_OF_MEMORY; + LOGE("GetKvEntries failed, errCode:%d", errCode); + SingleVerKvEntry::Release(entries); + break; + } + entry->SetEntryData(std::move(item)); + entries.push_back(entry); + } + return errCode; +} + +static size_t GetDataItemSerialSize(const DataItem &item, size_t appendLen) +{ + // timestamp and local flag: 3 * uint64_t, version(uint32_t), key, value, origin dev and the padding size. + // the size would not be very large. + static const size_t maxOrigDevLength = 40; + size_t devLength = std::max(maxOrigDevLength, item.origDev.size()); + size_t dataSize = (Parcel::GetUInt64Len() * 3 + Parcel::GetUInt32Len() + Parcel::GetVectorCharLen(item.key) + + Parcel::GetVectorCharLen(item.value) + devLength + appendLen); + return dataSize; +} + +static bool CanHoldDeletedData(const std::vector &dataItems, const DataSizeSpecInfo &dataSizeInfo, + size_t appendLen) +{ + bool reachThreshold = (dataItems.size() >= dataSizeInfo.packetSize); + for (size_t i = 0, blockSize = 0; !reachThreshold && i < dataItems.size(); i++) { + blockSize += GetDataItemSerialSize(dataItems[i], appendLen); + reachThreshold = (blockSize >= dataSizeInfo.blockSize * DBConstant::QUERY_SYNC_THRESHOLD); + } + return !reachThreshold; +} + +static void ProcessContinueTokenForQuerySync(const std::vector &dataItems, int &errCode, + SQLiteSingleVerRelationalContinueToken *&token) +{ + if (errCode != -E_UNFINISHED) { // Error happened or get data finished. Token should be cleared. + delete token; + token = nullptr; + return; + } + + if (dataItems.empty()) { + errCode = -E_INTERNAL_ERROR; + LOGE("Get data unfinished but data items is empty."); + delete token; + token = nullptr; + return; + } + token->SetNextBeginTime(dataItems.back()); +} + +/** + * Caller must ensure that parameter token is valid. + * If error happened, token will be deleted here. + */ +int RelationalSyncAbleStorage::GetSyncDataForQuerySync(std::vector &dataItems, + SQLiteSingleVerRelationalContinueToken *&token, const DataSizeSpecInfo &dataSizeInfo) const +{ + if (storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + auto handle = static_cast(storageEngine_->FindExecutor(false, + OperatePerm::NORMAL_PERM, errCode)); + if (handle == nullptr) { + goto ERROR; + } + + do { + errCode = handle->GetSyncDataByQuery(dataItems, + Parcel::GetAppendedLen(), + dataSizeInfo, + std::bind(&SQLiteSingleVerRelationalContinueToken::GetStatement, *token, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + storageEngine_->GetSchemaRef().GetTable(token->GetQuery().GetTableName())); + if (errCode == -E_FINISHED) { + token->FinishGetData(); + errCode = token->IsGetAllDataFinished() ? E_OK : -E_UNFINISHED; + } + } while (errCode == -E_UNFINISHED && CanHoldDeletedData(dataItems, dataSizeInfo, Parcel::GetAppendedLen())); + +ERROR: + if (errCode != -E_UNFINISHED && errCode != E_OK) { // Error happened. + dataItems.clear(); + } + ProcessContinueTokenForQuerySync(dataItems, errCode, token); + ReleaseHandle(handle); + return errCode; +} + +// use kv struct data to sync +// Get the data which would be synced with query condition +int RelationalSyncAbleStorage::GetSyncData(QueryObject &query, const SyncTimeRange &timeRange, + const DataSizeSpecInfo &dataSizeInfo, ContinueToken &continueStmtToken, + std::vector &entries) const +{ + if (!timeRange.IsValid()) { + return -E_INVALID_ARGS; + } + query.SetSchema(storageEngine_->GetSchemaRef()); + auto token = new (std::nothrow) SQLiteSingleVerRelationalContinueToken(timeRange, query); + if (token == nullptr) { + LOGE("[SingleVerNStore] Allocate continue token failed."); + return -E_OUT_OF_MEMORY; + } + + continueStmtToken = static_cast(token); + return GetSyncDataNext(entries, continueStmtToken, dataSizeInfo); +} + +int RelationalSyncAbleStorage::GetSyncDataNext(std::vector &entries, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const +{ + auto token = static_cast(continueStmtToken); + if (!token->CheckValid()) { + return -E_INVALID_ARGS; + } + const auto fieldInfos = storageEngine_->GetSchemaRef().GetTable(token->GetQuery().GetTableName()).GetFieldInfos(); + std::vector fieldNames; + for (const auto &fieldInfo : fieldInfos) { + fieldNames.push_back(fieldInfo.GetFieldName()); + } + token->SetFieldNames(fieldNames); + + std::vector dataItems; + int errCode = GetSyncDataForQuerySync(dataItems, token, dataSizeInfo); + if (errCode != E_OK && errCode != -E_UNFINISHED) { // The code need be sent to outside except new error happened. + continueStmtToken = static_cast(token); + return errCode; + } + + int innerCode = GetKvEntriesByDataItems(entries, dataItems); + if (innerCode != E_OK) { + errCode = innerCode; + delete token; + token = nullptr; + } + continueStmtToken = static_cast(token); + return errCode; +} + +int RelationalSyncAbleStorage::PutSyncDataWithQuery(const QueryObject &object, + const std::vector &entries, const DeviceID &deviceName) +{ + std::vector dataItems; + for (auto itemEntry : entries) { + GenericSingleVerKvEntry *entry = static_cast(itemEntry); + if (entry != nullptr) { + DataItem item; + item.origDev = entry->GetOrigDevice(); + item.flag = entry->GetFlag(); + item.timestamp = entry->GetTimestamp(); + item.writeTimestamp = entry->GetWriteTimestamp(); + entry->GetKey(item.key); + entry->GetValue(item.value); + entry->GetHashKey(item.hashKey); + dataItems.push_back(item); + } + } + + return PutSyncData(object, dataItems, deviceName); +} + +int RelationalSyncAbleStorage::SaveSyncDataItems(const QueryObject &object, std::vector &dataItems, + const std::string &deviceName) +{ + int errCode = E_OK; + LOGD("[RelationalSyncAbleStorage::SaveSyncDataItems] Get write handle."); + auto *handle = GetHandle(true, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return errCode; + } + QueryObject query = object; + query.SetSchema(storageEngine_->GetSchemaRef()); + DBDfxAdapter::StartTraceSQL(); + errCode = handle->SaveSyncItems(query, dataItems, deviceName, + storageEngine_->GetSchemaRef().GetTable(object.GetTableName())); + DBDfxAdapter::FinishTraceSQL(); + if (errCode == E_OK) { + // dataItems size > 0 now because already check before + // all dataItems will write into db now, so need to observer notify here + // if some dataItems will not write into db in the future, observer notify here need change + TriggerObserverAction(deviceName); + } + + ReleaseHandle(handle); + return errCode; +} + +int RelationalSyncAbleStorage::PutSyncData(const QueryObject &query, std::vector &dataItems, + const std::string &deviceName) +{ + if (deviceName.length() > DBConstant::MAX_DEV_LENGTH) { + LOGW("Device length is invalid for sync put"); + return -E_INVALID_ARGS; + } + + int errCode = SaveSyncDataItems(query, dataItems, deviceName); // Currently true to check value content + if (errCode != E_OK) { + LOGE("[Relational] PutSyncData errCode:%d", errCode); + } + return errCode; +} + +int RelationalSyncAbleStorage::RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) +{ + (void) deviceName; + (void) isNeedNotify; + return -E_NOT_SUPPORT; +} + +RelationalSchemaObject RelationalSyncAbleStorage::GetSchemaInfo() const +{ + return storageEngine_->GetSchemaRef(); +} + +int RelationalSyncAbleStorage::GetSecurityOption(SecurityOption &option) const +{ + return -E_NOT_SUPPORT; +} + +void RelationalSyncAbleStorage::NotifyRemotePushFinished(const std::string &deviceId) const +{ + return; +} + +// Get the timestamp when database created or imported +int RelationalSyncAbleStorage::GetDatabaseCreateTimestamp(Timestamp &outTime) const +{ + return OS::GetCurrentSysTimeInMicrosecond(outTime); +} + +// Get batch meta data associated with the given key. +int RelationalSyncAbleStorage::GetBatchMetaData(const std::vector &keys, std::vector &entries) const +{ + return -E_NOT_SUPPORT; +} + +// Put batch meta data as a key-value entry vector +int RelationalSyncAbleStorage::PutBatchMetaData(std::vector &entries) +{ + return -E_NOT_SUPPORT; +} + +std::vector RelationalSyncAbleStorage::GetTablesQuery() +{ + return {}; +} + +int RelationalSyncAbleStorage::LocalDataChanged(int notifyEvent, std::vector &queryObj) +{ + (void) queryObj; + return -E_NOT_SUPPORT; +} + +int RelationalSyncAbleStorage::CreateDistributedDeviceTable(const std::string &device, + const RelationalSyncStrategy &syncStrategy) +{ + int errCode = E_OK; + auto *handle = GetHandle(true, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + LOGE("Start transaction failed:%d", errCode); + ReleaseHandle(handle); + return errCode; + } + + for (const auto &[table, strategy] : syncStrategy) { + if (!strategy.permitSync) { + continue; + } + + errCode = handle->CreateDistributedDeviceTable(device, storageEngine_->GetSchemaRef().GetTable(table)); + if (errCode != E_OK) { + LOGE("Create distributed device table failed. %d", errCode); + break; + } + } + + if (errCode == E_OK) { + errCode = handle->Commit(); + } else { + (void)handle->Rollback(); + } + + ReleaseHandle(handle); + return errCode; +} + +int RelationalSyncAbleStorage::RegisterSchemaChangedCallback(const std::function &callback) +{ + std::lock_guard lock(onSchemaChangedMutex_); + onSchemaChanged_ = callback; + return E_OK; +} + +void RelationalSyncAbleStorage::NotifySchemaChanged() +{ + std::lock_guard lock(onSchemaChangedMutex_); + if (onSchemaChanged_) { + LOGD("Notify relational schema was changed"); + onSchemaChanged_(); + } +} +int RelationalSyncAbleStorage::GetCompressionAlgo(std::set &algorithmSet) const +{ + algorithmSet.clear(); + DataCompression::GetCompressionAlgo(algorithmSet); + return E_OK; +} + +void RelationalSyncAbleStorage::RegisterObserverAction(const RelationalObserverAction &action) +{ + std::lock_guard lock(dataChangeDeviceMutex_); + dataChangeDeviceCallback_ = action; +} + +void RelationalSyncAbleStorage::TriggerObserverAction(const std::string &deviceName) +{ + { + std::lock_guard lock(dataChangeDeviceMutex_); + if (!dataChangeDeviceCallback_) { + return; + } + } + IncObjRef(this); + int taskErrCode = RuntimeContext::GetInstance()->ScheduleTask([this, deviceName] { + std::lock_guard lock(dataChangeDeviceMutex_); + if (dataChangeDeviceCallback_) { + dataChangeDeviceCallback_(deviceName); + } + DecObjRef(this); + }); + if (taskErrCode != E_OK) { + LOGE("TriggerObserverAction scheduletask retCode=%d", taskErrCode); + DecObjRef(this); + } +} + +void RelationalSyncAbleStorage::RegisterHeartBeatListener(const std::function &listener) +{ + std::lock_guard autoLock(heartBeatMutex_); + heartBeatListener_ = listener; +} + +int RelationalSyncAbleStorage::CheckAndInitQueryCondition(QueryObject &query) const +{ + RelationalSchemaObject schema = storageEngine_->GetSchemaRef(); + TableInfo table = schema.GetTable(query.GetTableName()); + if (table.GetTableName() != query.GetTableName()) { + LOGE("Query table is not a distributed table."); + return -E_DISTRIBUTED_SCHEMA_NOT_FOUND; + } + query.SetSchema(schema); + + int errCode = E_OK; + auto *handle = GetHandle(false, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->CheckQueryObjectLegal(table, query); + if (errCode != E_OK) { + LOGE("Check relational query condition failed. %d", errCode); + } + + ReleaseHandle(handle); + return errCode; +} + +bool RelationalSyncAbleStorage::CheckCompatible(const std::string &schema, uint8_t type) const +{ + // return true if is relational schema. + return !schema.empty() && ReadSchemaType(type) == SchemaType::RELATIVE; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/relationaldb_properties.cpp b/mock/distributeddb/storage/src/relationaldb_properties.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52d34820ac7319446ffef3295220483d93451252 --- /dev/null +++ b/mock/distributeddb/storage/src/relationaldb_properties.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "relationaldb_properties.h" + +namespace DistributedDB { +RelationalDBProperties::RelationalDBProperties() +{} + +RelationalDBProperties::~RelationalDBProperties() +{} + +bool RelationalDBProperties::IsSchemaExist() const +{ + return schema_.IsSchemaValid(); +} + +void RelationalDBProperties::SetSchema(const RelationalSchemaObject &schema) +{ + schema_ = schema; +} + +RelationalSchemaObject RelationalDBProperties::GetSchema() const +{ + return schema_; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/result_entries_window.cpp b/mock/distributeddb/storage/src/result_entries_window.cpp new file mode 100644 index 0000000000000000000000000000000000000000..775ef833a9b7738f4f5cd92b084d12ba8be796c6 --- /dev/null +++ b/mock/distributeddb/storage/src/result_entries_window.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "result_entries_window.h" + +#include "db_constant.h" +#include "db_errno.h" + +using std::vector; +using std::move; + +namespace DistributedDB { +ResultEntriesWindow::ResultEntriesWindow() + : rawCursor_(nullptr), + windowSize_(0), + totalCount_(0), + begin_(0), + currentPosition_(0) {} + +ResultEntriesWindow::~ResultEntriesWindow() +{ + if (rawCursor_ != nullptr) { + (void)(rawCursor_->Close()); + rawCursor_ = nullptr; + } +} + +int ResultEntriesWindow::Init(IKvDBRawCursor *rawCursor, int64_t windowSize, double scale) +{ + if (rawCursor == nullptr || + (windowSize <= 0 || windowSize > MAX_WINDOW_SIZE) || + (scale <= std::numeric_limits::epsilon() || scale > 1)) { + return -E_INVALID_ARGS; + } + int errCode = rawCursor->Open(); + if (errCode != E_OK) { + return errCode; + } + + rawCursor_ = rawCursor; + windowSize_ = static_cast(static_cast(windowSize) * scale); + totalCount_ = rawCursor_->GetCount(); + return E_OK; +} + +int ResultEntriesWindow::GetTotalCount() const +{ + return totalCount_; +} + +int ResultEntriesWindow::GetCurrentPosition() const +{ + return currentPosition_; +} + +bool ResultEntriesWindow::MoveToPosition(int position) +{ + if ((rawCursor_ == nullptr && buffer_.size() == 0) || (position < 0 || position >= totalCount_)) { + return false; + } + if (buffer_.size() == 0) { + if (SetCursor(0, position) != E_OK) { + return false; + } + return true; + } + int last = begin_ + buffer_.size() - 1; + if (position > last) { + buffer_.clear(); + buffer_.shrink_to_fit(); + if (SetCursor(last + 1, position) != E_OK) { + return false; + } + return true; + } else if (position < begin_) { + if (rawCursor_ == nullptr) { + return false; + } + buffer_.clear(); + buffer_.shrink_to_fit(); + if (rawCursor_->Reload() != E_OK) { + ResetWindow(); + return false; + } + if (SetCursor(0, position) != E_OK) { + return false; + } + return true; + } else { + currentPosition_ = position; + } + return true; +} + +int ResultEntriesWindow::GetEntry(Entry &entry) const +{ + if (rawCursor_ == nullptr && buffer_.size() == 0) { + return -E_NOT_INIT; + } + if (totalCount_ == 0) { + return -E_NOT_FOUND; + } + if (buffer_.size() == 0) { + int errCode = LoadData(0, currentPosition_); + if (errCode != E_OK) { + return errCode; + } + } + entry = buffer_[currentPosition_ - begin_]; + return E_OK; +} + +void ResultEntriesWindow::ResetWindow() +{ + buffer_.clear(); + buffer_.shrink_to_fit(); + if (rawCursor_ != nullptr) { + (void)(rawCursor_->Reload()); + } + begin_ = 0; + currentPosition_ = 0; + return; +} + +int ResultEntriesWindow::SetCursor(int begin, int target) +{ + int errCode = LoadData(begin, target); + if (errCode == E_OK) { + begin_ = target; + currentPosition_ = target; + } else { + ResetWindow(); + } + return errCode; +} + +int ResultEntriesWindow::LoadData(int begin, int target) const +{ + if (rawCursor_ == nullptr) { + return -E_NOT_INIT; + } + if (target < begin || target >= totalCount_) { + return -E_INVALID_ARGS; + } + int cursor = begin; + int errCode = E_OK; + for (; cursor < target; cursor++) { + Entry next; + errCode = rawCursor_->GetNext(next, false); + if (errCode != E_OK) { + return errCode; + } + } + int64_t bufferSize = 0; + for (; cursor < totalCount_ && bufferSize < windowSize_; cursor++) { + Entry next; + errCode = rawCursor_->GetNext(next, true); + if (errCode != E_OK) { + return errCode; + } + // filter the abnormal data. + if (next.key.size() > DBConstant::MAX_KEY_SIZE || next.value.size() > DBConstant::MAX_VALUE_SIZE) { + continue; + } + bufferSize += next.key.size() + next.value.size(); + buffer_.push_back(move(next)); + } + if (buffer_.size() == static_cast(totalCount_)) { + (void)(rawCursor_->Close()); + rawCursor_ = nullptr; + } + return E_OK; +} +} diff --git a/mock/distributeddb/storage/src/result_entries_window.h b/mock/distributeddb/storage/src/result_entries_window.h new file mode 100644 index 0000000000000000000000000000000000000000..7b0fe2705ae5b20a744470a6a5bf798585ddd108 --- /dev/null +++ b/mock/distributeddb/storage/src/result_entries_window.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESULT_ENTRIES_WINDOW_H +#define RESULT_ENTRIES_WINDOW_H + +#include "macro_utils.h" +#include "ikvdb_raw_cursor.h" + +namespace DistributedDB { +class ResultEntriesWindow { +public: + ResultEntriesWindow(); + ~ResultEntriesWindow(); + int Init(IKvDBRawCursor *rawCursor, int64_t windowSize, double scale); + DISABLE_COPY_ASSIGN_MOVE(ResultEntriesWindow); + int GetTotalCount() const; + int GetCurrentPosition() const; + bool MoveToPosition(int position); + int GetEntry(Entry &entry) const; + +private: + void ResetWindow(); + int SetCursor(int begin, int target); + int LoadData(int begin, int target) const; + +private: + static const int64_t MAX_WINDOW_SIZE = 0xFFFFFFFFL; // 4G - 1 + mutable IKvDBRawCursor *rawCursor_; + int64_t windowSize_; + int totalCount_; + mutable std::vector buffer_; + int begin_; + int currentPosition_; +}; +} // namespace DistributedDB + +#endif // RESULT_ENTRIES_WINDOW_H diff --git a/mock/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.cpp b/mock/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56d61937afc88f73df23bf174e67d755fc87dc1b --- /dev/null +++ b/mock/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_natural_store_commit_notify_data.h" +#include "db_errno.h" +#include "log_print.h" +#include "db_common.h" + +namespace DistributedDB { +SingleVerNaturalStoreCommitNotifyData::SingleVerNaturalStoreCommitNotifyData() : conflictedFlag_(0) {} + +const std::list SingleVerNaturalStoreCommitNotifyData::GetInsertedEntries(int &errCode) const +{ + return FilterEntriesByKey(insertedEntries_, keyFilter_, errCode); +} + +const std::list SingleVerNaturalStoreCommitNotifyData::GetUpdatedEntries(int &errCode) const +{ + return FilterEntriesByKey(updatedEntries_, keyFilter_, errCode); +} + +const std::list SingleVerNaturalStoreCommitNotifyData::GetDeletedEntries(int &errCode) const +{ + return FilterEntriesByKey(deletedEntries_, keyFilter_, errCode); +} + +const std::list SingleVerNaturalStoreCommitNotifyData::GetCommitConflicts(int &errCode) const +{ + errCode = E_OK; + return conflictedEntries_; +} + +void SingleVerNaturalStoreCommitNotifyData::SetFilterKey(const Key &key) +{ + keyFilter_ = key; + return; +} + +bool SingleVerNaturalStoreCommitNotifyData::IsChangedDataEmpty() const +{ + int errCode; + return (!IsCleared() && GetInsertedEntries(errCode).empty() && GetUpdatedEntries(errCode).empty() && + GetDeletedEntries(errCode).empty()); +} + +bool SingleVerNaturalStoreCommitNotifyData::IsConflictedDataEmpty() const +{ + return conflictedEntries_.empty(); +} + +int SingleVerNaturalStoreCommitNotifyData::InsertCommittedData(const Entry &entry, DataType dataType, bool needMerge) +{ + if (!needMerge) { + return InsertEntry(dataType, entry); + } + + Key hashKey; + DBCommon::CalcValueHash(entry.key, hashKey); + // conclude the operation type + if (!IsKeyPropSet(hashKey)) { + return E_OK; + } + DataType type = DataType::NONE; + if (keyPropRecord_[hashKey].existStatus == ExistStatus::EXIST) { + if (dataType == DataType::INSERT || dataType == DataType::UPDATE) { + type = DataType::UPDATE; + } else if (dataType == DataType::DELETE) { + type = DataType::DELETE; + } + } else { + if (dataType == DataType::INSERT || dataType == DataType::UPDATE) { + type = DataType::INSERT; + } else if (dataType == DataType::DELETE) { + type = DataType::NONE; + } + } + + // clear the old data + DeleteEntryByKey(entry.key, keyPropRecord_[hashKey].latestType); + + // update the latest operation type value + keyPropRecord_[hashKey].latestType = type; + + return InsertEntry(type, entry); +} + +int SingleVerNaturalStoreCommitNotifyData::InsertEntry(DataType dataType, const Entry &entry) +{ + if (dataType == DataType::INSERT) { + insertedEntries_.push_back(entry); + } else if (dataType == DataType::UPDATE) { + updatedEntries_.push_back(entry); + } else if (dataType == DataType::DELETE) { + deletedEntries_.push_back(entry); + } + return E_OK; +} + +int SingleVerNaturalStoreCommitNotifyData::InsertConflictedItem(const DataItemInfo &itemInfo, bool isOriginal) +{ + Key hashKey; + DBCommon::CalcValueHash(itemInfo.dataItem.key, hashKey); + if (!IsKeyPropSet(hashKey)) { + LOGE("key property not set."); + return E_OK; + } + // key not exist in db + if (keyPropRecord_[hashKey].existStatus == ExistStatus::NONE) { + return E_OK; + } + + auto iter = orgDataItem_.find(itemInfo.dataItem.key); + if (iter == orgDataItem_.end()) { + if (isOriginal) { + orgDataItem_[itemInfo.dataItem.key] = itemInfo; + } + return E_OK; + } + if (!isOriginal) { + PutIntoConflictData(iter->second, itemInfo); + } + + return E_OK; +} + +const std::list SingleVerNaturalStoreCommitNotifyData::FilterEntriesByKey( + const std::list &entries, const Key &filterKey, int &errCode) +{ + errCode = E_OK; + if (filterKey.size() == 0) { + return entries; + } + std::list filterEntries; + for (const auto &entry : entries) { + if (entry.key == filterKey) { + filterEntries.push_back(entry); + } + } + return filterEntries; +} + +void SingleVerNaturalStoreCommitNotifyData::DeleteEntry(const Key &key, std::list &entries) const +{ + if (entries.empty()) { + return; + } + entries.remove_if([&key](const Entry &entry) { + return entry.key == key; + }); +} + +void SingleVerNaturalStoreCommitNotifyData::DeleteEntryByKey(const Key &key, DataType type) +{ + if (type == DataType::INSERT) { + DeleteEntry(key, insertedEntries_); + } + + if (type == DataType::UPDATE) { + DeleteEntry(key, updatedEntries_); + } + + if (type == DataType::DELETE) { + DeleteEntry(key, deletedEntries_); + } +} + +void SingleVerNaturalStoreCommitNotifyData::InitKeyPropRecord(const Key &key, ExistStatus status) +{ + // check if key status set before, we can only set key status at the first time + if (IsKeyPropSet(key)) { + return; + } + + keyPropRecord_[key].existStatus = status; +} + +void SingleVerNaturalStoreCommitNotifyData::SetConflictedNotifiedFlag(int conflictedFlag) +{ + conflictedFlag_ = conflictedFlag; +} + +int SingleVerNaturalStoreCommitNotifyData::GetConflictedNotifiedFlag() const +{ + return conflictedFlag_; +} + +bool SingleVerNaturalStoreCommitNotifyData::IsConflictedNotifyMatched(const DataItem &itemPut, + const DataItem &itemGet) const +{ + int dataConflictedType = 0; + // Local put + if ((itemPut.flag & DataItem::LOCAL_FLAG) != 0) { + dataConflictedType = SINGLE_VER_CONFLICT_NATIVE_ALL; + } else { + // Compare the origin device of the get and put item. + if (itemPut.origDev != itemGet.origDev) { + dataConflictedType = SINGLE_VER_CONFLICT_FOREIGN_KEY_ORIG; + } else { + dataConflictedType = SINGLE_VER_CONFLICT_FOREIGN_KEY_ONLY; + } + } + + int conflictedFlag = GetConflictedNotifiedFlag(); + LOGD("flag bind kvdb is %d, current data conflicted flag is %d", conflictedFlag, dataConflictedType); + return (static_cast(conflictedFlag) & static_cast(dataConflictedType)) != 0; +} + +void SingleVerNaturalStoreCommitNotifyData::PutIntoConflictData(const DataItemInfo &orgItemInfo, + const DataItemInfo &newItemInfo) +{ + if (orgItemInfo.dataItem.value == newItemInfo.dataItem.value && + orgItemInfo.dataItem.origDev == newItemInfo.dataItem.origDev && + orgItemInfo.dataItem.flag == newItemInfo.dataItem.flag && + orgItemInfo.deviceName == newItemInfo.deviceName) { + LOGW("same data no need to put."); + return; + } + + KvDBConflictEntry conflictData; + // Local put + if (newItemInfo.isLocal) { + conflictData.type = SingleVerNaturalStoreCommitNotifyData::SINGLE_VER_CONFLICT_NATIVE_ALL; + } else { + // Compare the origin device of the get and put item. + conflictData.type = ((newItemInfo.dataItem.origDev != orgItemInfo.dataItem.origDev) ? + SingleVerNaturalStoreCommitNotifyData::SINGLE_VER_CONFLICT_FOREIGN_KEY_ORIG : + SingleVerNaturalStoreCommitNotifyData::SINGLE_VER_CONFLICT_FOREIGN_KEY_ONLY); + } + + bool isDeleted = ((orgItemInfo.dataItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG); + conflictData.oldData = {orgItemInfo.dataItem.value, isDeleted, true}; + + isDeleted = ((newItemInfo.dataItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG); + conflictData.newData = {newItemInfo.dataItem.value, isDeleted, newItemInfo.isLocal}; + + // If the new item is deleted, just using the key of the old data item. + // If the items are all deleted, this function should not be executed. + conflictData.key = isDeleted ? orgItemInfo.dataItem.key : newItemInfo.dataItem.key; + if (newItemInfo.dataItem.writeTimestamp <= orgItemInfo.dataItem.writeTimestamp) { + std::swap(conflictData.newData, conflictData.oldData); + } + + DeleteConflictEntry(conflictData.key); + conflictedEntries_.push_back(std::move(conflictData)); +} + +void SingleVerNaturalStoreCommitNotifyData::DeleteConflictEntry(const Key &key) +{ + if (conflictedEntries_.empty()) { + return; + } + auto iter = conflictedEntries_.begin(); + for (; iter != conflictedEntries_.end(); ++iter) { + if (iter->key == key) { + conflictedEntries_.erase(iter); + return; + } + } +} + +bool SingleVerNaturalStoreCommitNotifyData::IsKeyPropSet(const Key &key) const +{ + // check if key status set before + return (keyPropRecord_.find(key) != keyPropRecord_.end()); +} + +DEFINE_OBJECT_TAG_FACILITIES(SingleVerNaturalStoreCommitNotifyData) +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.h b/mock/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.h new file mode 100644 index 0000000000000000000000000000000000000000..be54d608aaa046e422ac71812d05b5789dcd4f6f --- /dev/null +++ b/mock/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_NATURAL_STORE_COMMIT_NOTIFY_DATA_H +#define SINGLE_VER_NATURAL_STORE_COMMIT_NOTIFY_DATA_H + +#include "kvdb_commit_notify_filterable_data.h" + +namespace DistributedDB { +enum class DataType { + NONE, + INSERT, + UPDATE, + DELETE, +}; + +enum class ExistStatus { + NONE, // key never exist in db + DELETED, // key deleted but exist before + EXIST, // key exist +}; +constexpr size_t MAX_TOTAL_NOTIFY_ITEM_SIZE = 1048576; // 1MB +constexpr size_t MAX_TOTAL_NOTIFY_DATA_SIZE = 4195328; // 4MB + 1KB + +struct DataItemInfo { + DataItem dataItem; + bool isLocal = false; + std::vector deviceName; +}; + +class SingleVerNaturalStoreCommitNotifyData final : public KvDBCommitNotifyFilterAbleData { +public: + SingleVerNaturalStoreCommitNotifyData(); + ~SingleVerNaturalStoreCommitNotifyData() {} + DISABLE_COPY_ASSIGN_MOVE(SingleVerNaturalStoreCommitNotifyData); + + const std::list GetInsertedEntries(int &errCode) const override; + + const std::list GetUpdatedEntries(int &errCode) const override; + + const std::list GetDeletedEntries(int &errCode) const override; + + const std::list GetCommitConflicts(int &errCode) const override; + + void SetFilterKey(const Key &key) override; + + bool IsChangedDataEmpty() const override; + + bool IsConflictedDataEmpty() const override; + + int InsertCommittedData(const Entry &entry, DataType dataType, bool needMerge = false); + + int InsertConflictedItem(const DataItemInfo &itemInfo, bool isOriginal = false); + + void InitKeyPropRecord(const Key &key, ExistStatus status); + + void SetConflictedNotifiedFlag(int conflictedFlag); + + int GetConflictedNotifiedFlag() const; + + bool IsConflictedNotifyMatched(const DataItem &itemPut, const DataItem &itemGet) const; + +private: + + struct ItemProp { + ExistStatus existStatus = ExistStatus::NONE; // indicator if the key exist in db before this transaction + DataType latestType = DataType::NONE; // indicator the latest operation type for this key + }; + + int InsertEntry(DataType dataType, const Entry &entry); + + static const std::list FilterEntriesByKey(const std::list &entries, + const Key &filterKey, int &errCode); + + void DeleteEntry(const Key &key, std::list &entries) const; + + void DeleteEntryByKey(const Key &key, DataType type); + + void PutIntoConflictData(const DataItemInfo &orgItemInfo, const DataItemInfo &newItemInfo); + + void DeleteConflictEntry(const Key &key); + + bool IsKeyPropSet(const Key &key) const; + + DECLARE_OBJECT_TAG(SingleVerNaturalStoreCommitNotifyData); + + static const int SINGLE_VER_CONFLICT_FOREIGN_KEY_ONLY = 0x01; // sync conflict for same origin dev + static const int SINGLE_VER_CONFLICT_FOREIGN_KEY_ORIG = 0x02; // sync conflict for different origin dev + static const int SINGLE_VER_CONFLICT_NATIVE_ALL = 0x0c; // native conflict. + + std::list insertedEntries_; + std::list updatedEntries_; + std::list deletedEntries_; + std::list conflictedEntries_; + Key keyFilter_; + std::map keyPropRecord_; // hash key mapping to item property + std::map orgDataItem_; + int conflictedFlag_; // the conflict notifier type composition, 0 means no conflict notifier. +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_NATURAL_STORE_COMMIT_NOTIFY_DATA_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/query_object.cpp b/mock/distributeddb/storage/src/sqlite/query_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f474755549106318f2f247219278e8e4d95cf8f --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/query_object.cpp @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "query_object.h" +#include "db_errno.h" +#include "get_query_info.h" +#include "log_print.h" + +namespace DistributedDB { +namespace { +const int INVALID_LIMIT = INT_MAX; +const int LIMIT_FIELD_VALUE_SIZE = 2; +} + +QueryObject::QueryObject() + : isValid_(true), + initialized_(false), + limit_(INVALID_LIMIT), + offset_(0), + hasOrderBy_(false), + hasLimit_(false), + hasPrefixKey_(false), + hasInKeys_(false), + orderByCounts_(0) +{ +} + +void QueryObject::GetAttrFromQueryObjNodes() +{ + for (const auto &iter : queryObjNodes_) { + SymbolType symbolType = SqliteQueryHelper::GetSymbolType(iter.operFlag); + if (iter.operFlag == QueryObjType::LIMIT) { + hasLimit_ = true; + if (iter.fieldValue.size() == LIMIT_FIELD_VALUE_SIZE) { + limit_ = iter.fieldValue[0].integerValue; + offset_ = iter.fieldValue[1].integerValue; + } + } else if (iter.operFlag == QueryObjType::ORDERBY) { + hasOrderBy_ = true; + } else if (symbolType == PREFIXKEY_SYMBOL) { + hasPrefixKey_ = true; + } else if (symbolType == IN_KEYS_SYMBOL) { + hasInKeys_ = true; + } + } +} + +QueryObject::QueryObject(const Query &query) + : initialized_(false), + limit_(INVALID_LIMIT), + offset_(0), + hasOrderBy_(false), + hasLimit_(false), + hasPrefixKey_(false), + hasInKeys_(false), + orderByCounts_(0) +{ + QueryExpression queryExpressions = GetQueryInfo::GetQueryExpression(query); + queryObjNodes_ = queryExpressions.GetQueryExpression(); + GetAttrFromQueryObjNodes(); + isValid_ = queryExpressions.GetErrFlag(); + prefixKey_ = queryExpressions.GetPreFixKey(); + suggestIndex_ = queryExpressions.GetSuggestIndex(); + tableName_ = queryExpressions.GetTableName(); + isTableNameSpecified_ = queryExpressions.IsTableNameSpecified(); + keys_ = queryExpressions.GetKeys(); +} + +QueryObject::QueryObject(const std::list &queryObjNodes, const std::vector &prefixKey, + const std::set &keys) + : queryObjNodes_(queryObjNodes), + prefixKey_(prefixKey), + keys_(keys), + isValid_(true), + initialized_(false), + limit_(INVALID_LIMIT), + offset_(0), + hasOrderBy_(false), + hasLimit_(false), + hasPrefixKey_(false), + hasInKeys_(false), + orderByCounts_(0) +{ + GetAttrFromQueryObjNodes(); +} + +QueryObject::~QueryObject() +{} + +int QueryObject::Init() +{ + if (initialized_) { + return E_OK; + } + + int errCode = Parse(); + if (errCode != E_OK) { + LOGE("Parse query object err[%d]!", errCode); + return errCode; + } + + initialized_ = true; + return errCode; +} + +SqliteQueryHelper QueryObject::GetQueryHelper(int &errCode) +{ + errCode = Init(); + if (errCode != E_OK) { + return SqliteQueryHelper(QueryObjInfo{}); + } + QueryObjInfo info {schema_, queryObjNodes_, prefixKey_, suggestIndex_, keys_, + orderByCounts_, isValid_, hasOrderBy_, hasLimit_, hasPrefixKey_, tableName_, isTableNameSpecified_}; + return SqliteQueryHelper {info}; // compiler RVO by default, and RVO is generally required after C++17 +} + +bool QueryObject::IsValid() +{ + if (!initialized_) { + (void)Init(); + } + return isValid_; +} + +bool QueryObject::HasLimit() const +{ + return hasLimit_; +} + +void QueryObject::GetLimitVal(int &limit, int &offset) const +{ + limit = limit_; + offset = offset_; +} + +void QueryObject::SetSchema(const SchemaObject &schema) +{ + schema_ = schema; +} + +bool QueryObject::IsCountValid() const +{ + if (hasLimit_ || hasOrderBy_) { + LOGI("It is invalid for limit and orderby!"); + return false; + } + return true; +} + +const std::vector &QueryObject::GetPrefixKey() const +{ + return prefixKey_; +} + +void QueryObject::ClearNodesFlag() +{ + limit_ = INVALID_LIMIT; + offset_ = 0; + isValid_ = true; + hasOrderBy_ = false; + hasLimit_ = false; + hasPrefixKey_ = false; + hasInKeys_ = false; + orderByCounts_ = 0; +} + +int QueryObject::Parse() +{ + if (!isValid_) { + LOGE("Invalid query object!"); + return -E_INVALID_QUERY_FORMAT; + } + int errCode = ParseQueryObjNodes(); + if (errCode != E_OK) { + LOGE("Check query object illegal!"); + isValid_ = false; + } + return errCode; +} + +int QueryObject::ParseQueryObjNodes() +{ + ClearNodesFlag(); + + auto iter = queryObjNodes_.begin(); + int errCode = E_OK; + while (iter != queryObjNodes_.end()) { + errCode = ParseNode(iter); + if (errCode != E_OK) { + return errCode; + } + iter++; + } + return errCode; +} + +int QueryObject::ParseNode(const std::list::iterator &iter) +{ + // The object is newly instantiated in the connection, and there is no reentrancy problem. + if (!iter->IsValid()) { + return -E_INVALID_QUERY_FORMAT; + } + + switch (SqliteQueryHelper::GetSymbolType(iter->operFlag)) { + case COMPARE_SYMBOL: + case RELATIONAL_SYMBOL: + case RANGE_SYMBOL: + return CheckEqualFormat(iter); + case LINK_SYMBOL: + return CheckLinkerFormat(iter); + case PREFIXKEY_SYMBOL: { + if (hasPrefixKey_) { + LOGE("Only filter by prefix key once!!"); + return -E_INVALID_QUERY_FORMAT; + } + hasPrefixKey_ = true; + if (prefixKey_.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + return E_OK; + } + case SUGGEST_INDEX_SYMBOL: + return CheckSuggestIndexFormat(iter); + case IN_KEYS_SYMBOL: { + if (hasInKeys_) { + LOGE("Only filter by keys in once!!"); + return -E_INVALID_QUERY_FORMAT; + } + int errCode = CheckInKeys(); + if (errCode != E_OK) { + return errCode; + } + hasInKeys_ = true; + return E_OK; + } + default: + return ParseNodeByOperFlag(iter); + } + return E_OK; +} + +int QueryObject::ParseNodeByOperFlag(const std::list::iterator &iter) +{ + switch (iter->operFlag) { + case QueryObjType::LIMIT: + hasLimit_ = true; + if (iter->fieldValue.size() == LIMIT_FIELD_VALUE_SIZE) { + limit_ = iter->fieldValue[0].integerValue; + offset_ = iter->fieldValue[1].integerValue; + } + return CheckLimitFormat(iter); + case QueryObjType::ORDERBY: + return CheckOrderByFormat(iter); + default: + return E_OK; + } + return E_OK; +} + +int QueryObject::CheckLinkerBefore(const std::list::iterator &iter) const +{ + auto preIter = std::prev(iter, 1); + SymbolType symbolType = SqliteQueryHelper::GetSymbolType(preIter->operFlag); + if (symbolType != COMPARE_SYMBOL && symbolType != RELATIONAL_SYMBOL && symbolType != LOGIC_SYMBOL && + symbolType != RANGE_SYMBOL && symbolType != PREFIXKEY_SYMBOL && symbolType != IN_KEYS_SYMBOL) { + LOGE("Must be a comparison operation before the connective! operFlag = %s", VNAME(preIter->operFlag)); + return -E_INVALID_QUERY_FORMAT; + } + return E_OK; +} + +bool QueryObject::IsRelationalQuery() const +{ + return isTableNameSpecified_; +} + +int QueryObject::CheckEqualFormat(const std::list::iterator &iter) const +{ + if (!schema_.IsSchemaValid()) { + LOGE("Schema is invalid!"); + return -E_NOT_SUPPORT; + } + + FieldPath fieldPath; + int errCode = SchemaUtils::ParseAndCheckFieldPath(iter->fieldName, fieldPath); + if (errCode != E_OK) { + return -E_INVALID_QUERY_FIELD; + } + + FieldType schemaFieldType = FieldType::LEAF_FIELD_BOOL; + errCode = schema_.CheckQueryableAndGetFieldType(fieldPath, schemaFieldType); + if (errCode != E_OK) { + LOGE("Get field type fail when check compare format! errCode = %d, fieldType = %u", + errCode, static_cast(schemaFieldType)); + return -E_INVALID_QUERY_FIELD; + } + + if (schemaFieldType == FieldType::LEAF_FIELD_BOOL && + SqliteQueryHelper::GetSymbolType(iter->operFlag) == COMPARE_SYMBOL && + iter->operFlag != QueryObjType::EQUALTO && iter->operFlag != QueryObjType::NOT_EQUALTO) { // bool can == or != + LOGE("Bool forbid compare!!!"); + return -E_INVALID_QUERY_FORMAT; + } + auto nextIter = std::next(iter, 1); + if (nextIter != queryObjNodes_.end()) { + SymbolType symbolType = SqliteQueryHelper::GetSymbolType(nextIter->operFlag); + if (symbolType == RELATIONAL_SYMBOL || symbolType == COMPARE_SYMBOL || symbolType == RANGE_SYMBOL) { + LOGE("After Compare you need, You need the conjunction like and or for connecting!"); + return -E_INVALID_QUERY_FORMAT; + } + } + return E_OK; +} + +int QueryObject::CheckLinkerFormat(const std::list::iterator &iter) const +{ + auto itPre = iter; + for (; itPre != queryObjNodes_.begin(); itPre = std::prev(itPre, 1)) { + SymbolType symbolType = SqliteQueryHelper::GetSymbolType(std::prev(itPre, 1)->operFlag); + if (symbolType != PREFIXKEY_SYMBOL && symbolType != IN_KEYS_SYMBOL) { + break; + } + } + if (itPre == queryObjNodes_.begin()) { + LOGE("Connectives are not allowed in the first place!"); + return -E_INVALID_QUERY_FORMAT; + } + auto nextIter = std::next(iter, 1); + if (nextIter == queryObjNodes_.end()) { + LOGE("Connectives are not allowed in the last place!"); + return -E_INVALID_QUERY_FORMAT; + } + SymbolType symbolType = SqliteQueryHelper::GetSymbolType(nextIter->operFlag); + if (symbolType == INVALID_SYMBOL || symbolType == LINK_SYMBOL || symbolType == SPECIAL_SYMBOL) { + LOGE("Must be followed by comparison operation! operflag[%u], symbolType[%u]", + static_cast(nextIter->operFlag), static_cast(symbolType)); + return -E_INVALID_QUERY_FORMAT; + } + return CheckLinkerBefore(iter); +} + +int QueryObject::CheckSuggestIndexFormat(const std::list::iterator &iter) const +{ + auto next = std::next(iter, 1); + if (next != queryObjNodes_.end()) { + LOGE("SuggestIndex only allowed once, and must appear at the end!"); + return -E_INVALID_QUERY_FORMAT; + } + return E_OK; +} + +int QueryObject::CheckOrderByFormat(const std::list::iterator &iter) +{ + if (!schema_.IsSchemaValid()) { + return -E_NOT_SUPPORT; + } + + FieldType schemaFieldType; + FieldPath fieldPath; + + int errCode = SchemaUtils::ParseAndCheckFieldPath(iter->fieldName, fieldPath); + if (errCode != E_OK) { + return -E_INVALID_QUERY_FIELD; + } + errCode = schema_.CheckQueryableAndGetFieldType(fieldPath, schemaFieldType); + if (errCode != E_OK) { + return -E_INVALID_QUERY_FIELD; + } + if (schemaFieldType == FieldType::LEAF_FIELD_BOOL) { + return -E_INVALID_QUERY_FORMAT; + } + hasOrderBy_ = true; + ++orderByCounts_; + LOGD("Need order by %d filed value!", orderByCounts_); + return E_OK; +} + +int QueryObject::CheckLimitFormat(const std::list::iterator &iter) const +{ + auto next = std::next(iter, 1); + if (next != queryObjNodes_.end() && SqliteQueryHelper::GetSymbolType(next->operFlag) != SUGGEST_INDEX_SYMBOL) { + LOGE("Limit should be last node or just before suggest-index node!"); + return -E_INVALID_QUERY_FORMAT; + } + return E_OK; +} + +bool QueryObject::IsQueryOnlyByKey() const +{ + return std::none_of(queryObjNodes_.begin(), queryObjNodes_.end(), [&](const QueryObjNode &node) { + return node.operFlag != QueryObjType::LIMIT && node.operFlag != QueryObjType::QUERY_BY_KEY_PREFIX && + node.operFlag != QueryObjType::IN_KEYS; + }); +} + +bool QueryObject::IsQueryForRelationalDB() const +{ + return isTableNameSpecified_ && + std::none_of(queryObjNodes_.begin(), queryObjNodes_.end(), [&](const QueryObjNode &node) { + return node.operFlag != QueryObjType::EQUALTO && node.operFlag != QueryObjType::NOT_EQUALTO && + node.operFlag != QueryObjType::AND && node.operFlag != QueryObjType::OR && + node.operFlag != QueryObjType::ORDERBY && node.operFlag != QueryObjType::LIMIT; + }); +} + +bool QueryObject::HasOrderBy() const +{ + return hasOrderBy_; +} + +bool QueryObject::Empty() const +{ + return queryObjNodes_.empty(); +} + +int QueryObject::CheckInKeys() const +{ + if (keys_.empty()) { + LOGE("Inkeys cannot be empty."); + return -E_INVALID_ARGS; + } + if (keys_.size() > DBConstant::MAX_INKEYS_SIZE) { + LOGE("Inkeys cannot be over 128."); + return -E_MAX_LIMITS; + } + for (const auto &key : keys_) { + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + LOGE("The key in Inkeys cannot be empty or overlong, size:%zu.", key.size()); + return -E_INVALID_ARGS; + } + } + return E_OK; +} + +#ifdef RELATIONAL_STORE +int QueryObject::SetSchema(const RelationalSchemaObject &schemaObj) +{ + if (!isTableNameSpecified_) { + return -E_INVALID_ARGS; + } + const auto &tableInfo = schemaObj.GetTable(tableName_); + SchemaObject schema(tableInfo); + schema_ = schema; + return E_OK; +} +#endif +} + diff --git a/mock/distributeddb/storage/src/sqlite/query_object.h b/mock/distributeddb/storage/src/sqlite/query_object.h new file mode 100644 index 0000000000000000000000000000000000000000..84d2836edb5cb6e4e68b40d81dc652d892f7ca91 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/query_object.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef QUERY_OBJECT_H +#define QUERY_OBJECT_H + +#include + +#include "query.h" +#include "relational_schema_object.h" +#include "schema_object.h" +#include "sqlite_query_helper.h" + +namespace DistributedDB { +class QueryObject { +public: + QueryObject(); + explicit QueryObject(const Query &query); + // for query sync + QueryObject(const std::list &queryObjNodes, const std::vector &prefixKey, + const std::set &keys); + virtual ~QueryObject(); + int Init(); + SqliteQueryHelper GetQueryHelper(int &errCode); + + // suggest: get those attributes after init or GetQueryHelper to parsed query + bool IsValid(); + bool HasLimit() const; + void GetLimitVal(int &limit, int &offset) const; + bool IsCountValid() const; + + const std::vector &GetPrefixKey() const; + void SetSchema(const SchemaObject &schema); + + bool IsQueryOnlyByKey() const; + bool IsQueryForRelationalDB() const; + + void SetTableName(const std::string &tableName) + { + tableName_ = tableName; + isTableNameSpecified_ = true; + } + + const std::string &GetTableName() const + { + return tableName_; + } + + bool HasOrderBy() const; + + int ParseQueryObjNodes(); + + bool Empty() const; + + bool HasInKeys() const + { + return hasInKeys_; + } + +#ifdef RELATIONAL_STORE + int SetSchema(const RelationalSchemaObject &schemaObj); // The interface can only be used in relational query. +#endif + +protected: + std::list queryObjNodes_; + std::vector prefixKey_; + std::string tableName_ = "sync_data"; + std::string suggestIndex_; + std::set keys_; + + bool isValid_ = true; + + bool initialized_ = false; // use function need after init + bool isTableNameSpecified_ = false; + +private: + int Parse(); + int ParseNode(const std::list::iterator &iter); + int ParseNodeByOperFlag(const std::list::iterator &iter); + int CheckEqualFormat(const std::list::iterator &iter) const; + int CheckLinkerFormat(const std::list::iterator &iter) const; + int CheckSuggestIndexFormat(const std::list::iterator &iter) const; + int CheckOrderByFormat(const std::list::iterator &iter); + int CheckLimitFormat(const std::list::iterator &iter) const; + int CheckLinkerBefore(const std::list::iterator &iter) const; + void ClearNodesFlag(); + void GetAttrFromQueryObjNodes(); + int CheckInKeys() const; + bool IsRelationalQuery() const; + + SchemaObject schema_; // used to check and parse schema filed + int limit_; + int offset_; + bool hasOrderBy_; + bool hasLimit_; + bool hasPrefixKey_; + bool hasInKeys_; + int orderByCounts_; +}; +} +#endif diff --git a/mock/distributeddb/storage/src/sqlite/query_sync_object.cpp b/mock/distributeddb/storage/src/sqlite/query_sync_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90a535bec6bd0dbc839f66e6713cdb6b520143e9 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/query_sync_object.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "query_sync_object.h" + +#include "db_errno.h" +#include "log_print.h" +#include "db_common.h" +#include "version.h" + +namespace DistributedDB { +namespace { +const std::string MAGIC = "remote query"; + +int SerializeDataObjNode(Parcel &parcel, const QueryObjNode &objNode) +{ + if (objNode.operFlag == QueryObjType::OPER_ILLEGAL) { + return -E_INVALID_QUERY_FORMAT; + } + (void)parcel.WriteUInt32(static_cast(objNode.operFlag)); + parcel.EightByteAlign(); + (void)parcel.WriteString(objNode.fieldName); + (void)parcel.WriteInt(static_cast(objNode.type)); + (void)parcel.WriteUInt32(objNode.fieldValue.size()); + + for (const FieldValue &value : objNode.fieldValue) { + (void)parcel.WriteString(value.stringValue); + + // string may not closely arranged continuously + // longValue is maximum length in union + (void)parcel.WriteInt64(value.longValue); + } + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + return E_OK; +} + +int DeSerializeDataObjNode(Parcel &parcel, QueryObjNode &objNode) +{ + uint32_t readOperFlag = 0; + (void)parcel.ReadUInt32(readOperFlag); + objNode.operFlag = static_cast(readOperFlag); + parcel.EightByteAlign(); + + (void)parcel.ReadString(objNode.fieldName); + + int readInt = -1; + (void)parcel.ReadInt(readInt); + objNode.type = static_cast(readInt); + + uint32_t valueSize = 0; + (void)parcel.ReadUInt32(valueSize); + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + + for (size_t i = 0; i < valueSize; i++) { + FieldValue value; + (void)parcel.ReadString(value.stringValue); + + (void)parcel.ReadInt64(value.longValue); + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + objNode.fieldValue.push_back(value); + } + return E_OK; +} +} + +QuerySyncObject::QuerySyncObject() +{} + +QuerySyncObject::QuerySyncObject(const std::list &queryObjNodes, const std::vector &prefixKey, + const std::set &keys) + : QueryObject(queryObjNodes, prefixKey, keys) +{} + +QuerySyncObject::QuerySyncObject(const Query &query) + : QueryObject(query) +{} + +QuerySyncObject::~QuerySyncObject() +{} + +uint32_t QuerySyncObject::GetVersion() const +{ + uint32_t version = QUERY_SYNC_OBJECT_VERSION_0; + if (isTableNameSpecified_ || !keys_.empty()) { + version = QUERY_SYNC_OBJECT_VERSION_1; + } + return version; +} + +int QuerySyncObject::GetObjContext(ObjContext &objContext) const +{ + if (!isValid_) { + return -E_INVALID_QUERY_FORMAT; + } + objContext.version = GetVersion(); + objContext.prefixKey.assign(prefixKey_.begin(), prefixKey_.end()); + objContext.suggestIndex = suggestIndex_; + objContext.queryObjNodes = queryObjNodes_; + return E_OK; +} + +std::string QuerySyncObject::GetIdentify() const +{ + if (!isValid_) { + return std::string(); + } + // suggestionIndex is local attribute, do not need to be propagated to remote + uint64_t len = Parcel::GetVectorCharLen(prefixKey_); + for (const QueryObjNode &node : queryObjNodes_) { + if (node.operFlag == QueryObjType::LIMIT || node.operFlag == QueryObjType::ORDERBY || + node.operFlag == QueryObjType::SUGGEST_INDEX) { + continue; + } + // operFlag and valueType is int + len += Parcel::GetUInt32Len() + Parcel::GetIntLen() + Parcel::GetStringLen(node.fieldName); + for (const FieldValue &value : node.fieldValue) { + len += Parcel::GetStringLen(value.stringValue) + Parcel::GetInt64Len(); + } + } + + // QUERY_SYNC_OBJECT_VERSION_1 added. + len += isTableNameSpecified_ ? Parcel::GetStringLen(tableName_) : 0; + for (const auto &key : keys_) { + len += Parcel::GetVectorCharLen(key); + } // QUERY_SYNC_OBJECT_VERSION_1 end. + + std::vector buff(len, 0); // It will affect the hash result, the default value cannot be modified + Parcel parcel(buff.data(), len); + + // The order needs to be consistent, otherwise it will affect the hash result + (void)parcel.WriteVectorChar(prefixKey_); + for (const QueryObjNode &node : queryObjNodes_) { + if (node.operFlag == QueryObjType::LIMIT || node.operFlag == QueryObjType::ORDERBY || + node.operFlag == QueryObjType::SUGGEST_INDEX) { + continue; + } + (void)parcel.WriteUInt32(static_cast(node.operFlag)); + (void)parcel.WriteInt(static_cast(node.type)); + (void)parcel.WriteString(node.fieldName); + for (const FieldValue &value : node.fieldValue) { + (void)parcel.WriteInt64(value.longValue); + (void)parcel.WriteString(value.stringValue); + } + } + + // QUERY_SYNC_OBJECT_VERSION_1 added. + if (isTableNameSpecified_) { + (void)parcel.WriteString(tableName_); + } + for (const auto &key : keys_) { + (void)parcel.WriteVectorChar(key); + } // QUERY_SYNC_OBJECT_VERSION_1 end. + + std::vector hashBuff; + if (parcel.IsError() || DBCommon::CalcValueHash(buff, hashBuff) != E_OK) { + return std::string(); + } + + return DBCommon::VectorToHexString(hashBuff); +} + +uint32_t QuerySyncObject::CalculateParcelLen(uint32_t softWareVersion) const +{ + if (softWareVersion == SOFTWARE_VERSION_CURRENT) { + return CalculateLen(); + } + LOGE("current not support!"); + return 0; +} + +int QuerySyncObject::SerializeData(Parcel &parcel, uint32_t softWareVersion) +{ + ObjContext context; + int errCode = GetObjContext(context); + if (errCode != E_OK) { + return errCode; + } + + (void)parcel.WriteString(MAGIC); + (void)parcel.WriteUInt32(context.version); + (void)parcel.WriteVectorChar(context.prefixKey); + (void)parcel.WriteString(context.suggestIndex); + (void)parcel.WriteUInt32(context.queryObjNodes.size()); + + parcel.EightByteAlign(); + + for (const QueryObjNode &node : context.queryObjNodes) { + errCode = SerializeDataObjNode(parcel, node); + if (errCode != E_OK) { + return errCode; + } + } + + // QUERY_SYNC_OBJECT_VERSION_1 added. + if (context.version >= QUERY_SYNC_OBJECT_VERSION_1) { + (void)parcel.WriteUInt32(static_cast(isTableNameSpecified_)); + if (isTableNameSpecified_) { + (void)parcel.WriteString(tableName_); + } + (void)parcel.WriteUInt32(keys_.size()); + for (const auto &key : keys_) { + (void)parcel.WriteVectorChar(key); + } + } // QUERY_SYNC_OBJECT_VERSION_1 end. + + if (parcel.IsError()) { // parcel almost success + return -E_INVALID_ARGS; + } + parcel.EightByteAlign(); + return E_OK; +} + +namespace { +int DeSerializeVersion1Data(uint32_t version, Parcel &parcel, std::string &tableName, std::set &keys) +{ + if (version >= QUERY_SYNC_OBJECT_VERSION_1) { + uint32_t isTblNameExist = 0; + (void)parcel.ReadUInt32(isTblNameExist); + if (isTblNameExist) { + (void)parcel.ReadString(tableName); + } + uint32_t keysSize = 0; + (void)parcel.ReadUInt32(keysSize); + if (keysSize > DBConstant::MAX_INKEYS_SIZE) { + return -E_PARSE_FAIL; + } + for (uint32_t i = 0; i < keysSize; ++i) { + Key key; + (void)parcel.ReadVector(key); + keys.emplace(key); + } + } + return E_OK; +} +} + +int QuerySyncObject::DeSerializeData(Parcel &parcel, QuerySyncObject &queryObj) +{ + std::string magic; + (void)parcel.ReadString(magic); + if (magic != MAGIC) { + return -E_INVALID_ARGS; + } + + ObjContext context; + (void)parcel.ReadUInt32(context.version); + if (context.version > QUERY_SYNC_OBJECT_VERSION_CURRENT) { + LOGE("Parcel version and deserialize version not matched! ver=%u", context.version); + return -E_VERSION_NOT_SUPPORT; + } + + (void)parcel.ReadVectorChar(context.prefixKey); + (void)parcel.ReadString(context.suggestIndex); + + uint32_t nodesSize = 0; + (void)parcel.ReadUInt32(nodesSize); + if (parcel.IsError()) { // almost success + return -E_INVALID_ARGS; + } + parcel.EightByteAlign(); + for (size_t i = 0; i < nodesSize; i++) { + QueryObjNode node; + int errCode = DeSerializeDataObjNode(parcel, node); + if (errCode != E_OK) { + return errCode; + } + context.queryObjNodes.emplace_back(node); + } + + // QUERY_SYNC_OBJECT_VERSION_1 added. + std::string tableName; + std::set keys; + int errCode = DeSerializeVersion1Data(context.version, parcel, tableName, keys); + if (errCode != E_OK) { + return errCode; + } // QUERY_SYNC_OBJECT_VERSION_1 end. + + if (parcel.IsError()) { // almost success + return -E_INVALID_ARGS; + } + queryObj = QuerySyncObject(context.queryObjNodes, context.prefixKey, keys); + if (!tableName.empty()) { + queryObj.SetTableName(tableName); + } + return E_OK; +} + +uint32_t QuerySyncObject::CalculateLen() const +{ + uint64_t len = Parcel::GetStringLen(MAGIC); + len += Parcel::GetUInt32Len(); // version + len += Parcel::GetVectorCharLen(prefixKey_); + len += Parcel::GetStringLen(suggestIndex_); + len += Parcel::GetUInt32Len(); // nodes size + len = Parcel::GetEightByteAlign(len); + for (const QueryObjNode &node : queryObjNodes_) { + if (node.operFlag == QueryObjType::OPER_ILLEGAL) { + LOGE("contain illegal operator for query sync!"); + return 0; + } + // operflag, fieldName, query value type, value size, union max size, string value + len += Parcel::GetUInt32Len(); + len = Parcel::GetEightByteAlign(len); + len += Parcel::GetStringLen(node.fieldName) + + Parcel::GetIntLen() + Parcel::GetUInt32Len(); + for (size_t i = 0; i < node.fieldValue.size(); i++) { + len += Parcel::GetInt64Len() + Parcel::GetStringLen(node.fieldValue[i].stringValue); + } + } + + // QUERY_SYNC_OBJECT_VERSION_1 added. + len += Parcel::GetUInt32Len(); // whether the table name exists. + if (isTableNameSpecified_) { + len += Parcel::GetStringLen(tableName_); + } + len += Parcel::GetUInt32Len(); // size of keys_ + for (const auto &key : keys_) { + len += Parcel::GetVectorCharLen(key); + } // QUERY_SYNC_OBJECT_VERSION_1 end. + + len = Parcel::GetEightByteAlign(len); + if (len > INT32_MAX) { + return 0; + } + return static_cast(len); +} + +std::string QuerySyncObject::GetRelationTableName() const +{ + if (!isTableNameSpecified_) { + return {}; + } + return tableName_; +} +} \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/query_sync_object.h b/mock/distributeddb/storage/src/sqlite/query_sync_object.h new file mode 100644 index 0000000000000000000000000000000000000000..7d8ca7630c1a2ea98a03dc2229c43e871ce39c2f --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/query_sync_object.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef QUERY_SYNC_OBJECT_H +#define QUERY_SYNC_OBJECT_H + +#include + +#include "query_object.h" +#include "parcel.h" + +namespace DistributedDB { +const uint32_t QUERY_SYNC_OBJECT_VERSION_0 = 0; +const uint32_t QUERY_SYNC_OBJECT_VERSION_1 = 1; // Add tableName_ and keys_. +const uint32_t QUERY_SYNC_OBJECT_VERSION_CURRENT = QUERY_SYNC_OBJECT_VERSION_1; // always point the last. + +struct ObjContext { + uint32_t version = QUERY_SYNC_OBJECT_VERSION_0; // serialized struct version + std::vector prefixKey{}; + std::string suggestIndex{}; + std::list queryObjNodes{}; + std::vector keys{}; +}; + +class QuerySyncObject : public QueryObject { +public: + QuerySyncObject(); + QuerySyncObject(const std::list &queryObjNodes, const std::vector &prefixKey, + const std::set &keys); + explicit QuerySyncObject(const Query &query); + ~QuerySyncObject() override; + + std::string GetIdentify() const; + + int SerializeData(Parcel &parcel, uint32_t softWareVersion); + // should call Parcel.IsError() to Get result. + static int DeSerializeData(Parcel &parcel, QuerySyncObject &queryObj); + uint32_t CalculateParcelLen(uint32_t softWareVersion) const; + + std::string GetRelationTableName() const; + +private: + uint32_t CalculateLen() const; + int GetObjContext(ObjContext &objContext) const; + uint32_t GetVersion() const; +}; +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.cpp b/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58f4d6ed460bc223aaca3260537dcf28642c99a5 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.cpp @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "sqlite_relational_store.h" + +#include "db_common.h" +#include "db_dump_helper.h" +#include "db_errno.h" +#include "log_print.h" +#include "db_types.h" +#include "sqlite_relational_store_connection.h" +#include "storage_engine_manager.h" + +namespace DistributedDB { +namespace { + constexpr const char *RELATIONAL_SCHEMA_KEY = "relational_schema"; + constexpr const char *LOG_TABLE_VERSION_KEY = "log_table_version"; + constexpr const char *LOG_TABLE_VERSION_1 = "1.0"; +} + +SQLiteRelationalStore::~SQLiteRelationalStore() +{ + delete sqliteStorageEngine_; + sqliteStorageEngine_ = nullptr; +} + +// Called when a new connection created. +void SQLiteRelationalStore::IncreaseConnectionCounter() +{ + connectionCount_.fetch_add(1, std::memory_order_seq_cst); + if (connectionCount_.load() > 0) { + sqliteStorageEngine_->SetConnectionFlag(true); + } +} + +RelationalStoreConnection *SQLiteRelationalStore::GetDBConnection(int &errCode) +{ + std::lock_guard lock(connectMutex_); + RelationalStoreConnection* connection = new (std::nothrow) SQLiteRelationalStoreConnection(this); + if (connection == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + IncObjRef(this); + IncreaseConnectionCounter(); + return connection; +} + +static void InitDataBaseOption(const RelationalDBProperties &properties, OpenDbProperties &option) +{ + option.uri = properties.GetStringProp(DBProperties::DATA_DIR, ""); + option.createIfNecessary = properties.GetBoolProp(DBProperties::CREATE_IF_NECESSARY, false); +} + +int SQLiteRelationalStore::InitStorageEngine(const RelationalDBProperties &properties) +{ + OpenDbProperties option; + InitDataBaseOption(properties, option); + std::string identifier = properties.GetStringProp(DBProperties::IDENTIFIER_DATA, ""); + + StorageEngineAttr poolSize = {1, 1, 0, 16}; // at most 1 write 16 read. + int errCode = sqliteStorageEngine_->InitSQLiteStorageEngine(poolSize, option, identifier); + if (errCode != E_OK) { + LOGE("Init the sqlite storage engine failed:%d", errCode); + } + return errCode; +} + +void SQLiteRelationalStore::ReleaseResources() +{ + if (sqliteStorageEngine_ != nullptr) { + sqliteStorageEngine_->ClearEnginePasswd(); + (void)StorageEngineManager::ReleaseStorageEngine(sqliteStorageEngine_); + } + RefObject::DecObjRef(storageEngine_); +} + +int SQLiteRelationalStore::CheckDBMode() +{ + int errCode = E_OK; + auto *handle = GetHandle(false, errCode); + if (handle == nullptr) { + return errCode; + } + errCode = handle->CheckDBModeForRelational(); + if (errCode != E_OK) { + LOGE("check relational DB mode failed. %d", errCode); + } + + ReleaseHandle(handle); + return errCode; +} + +int SQLiteRelationalStore::GetSchemaFromMeta() +{ + const Key schemaKey(RELATIONAL_SCHEMA_KEY, RELATIONAL_SCHEMA_KEY + strlen(RELATIONAL_SCHEMA_KEY)); + Value schemaVal; + int errCode = storageEngine_->GetMetaData(schemaKey, schemaVal); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + LOGE("Get relational schema from meta table failed. %d", errCode); + return errCode; + } else if (errCode == -E_NOT_FOUND || schemaVal.empty()) { + LOGW("No relational schema info was found."); + return E_OK; + } + + std::string schemaStr; + DBCommon::VectorToString(schemaVal, schemaStr); + RelationalSchemaObject schema; + errCode = schema.ParseFromSchemaString(schemaStr); + if (errCode != E_OK) { + LOGE("Parse schema string from meta table failed."); + return errCode; + } + + sqliteStorageEngine_->SetSchema(schema); + return E_OK; +} + +int SQLiteRelationalStore::SaveSchemaToMeta() +{ + const Key schemaKey(RELATIONAL_SCHEMA_KEY, RELATIONAL_SCHEMA_KEY + strlen(RELATIONAL_SCHEMA_KEY)); + Value schemaVal; + DBCommon::StringToVector(sqliteStorageEngine_->GetSchemaRef().ToSchemaString(), schemaVal); + int errCode = storageEngine_->PutMetaData(schemaKey, schemaVal); + if (errCode != E_OK) { + LOGE("Save relational schema to meta table failed. %d", errCode); + } + return errCode; +} + +int SQLiteRelationalStore::SaveLogTableVersionToMeta() +{ + LOGD("save log table version to meta table, key: %s, val: %s", LOG_TABLE_VERSION_KEY, LOG_TABLE_VERSION_1); + const Key logVersionKey(LOG_TABLE_VERSION_KEY, LOG_TABLE_VERSION_KEY + strlen(LOG_TABLE_VERSION_KEY)); + Value logVersionVal(LOG_TABLE_VERSION_1, LOG_TABLE_VERSION_1 + strlen(LOG_TABLE_VERSION_1)); + int errCode = storageEngine_->PutMetaData(logVersionKey, logVersionVal); + if (errCode != E_OK) { + LOGE("save log table version to meta table failed. %d", errCode); + } + return errCode; +} + +int SQLiteRelationalStore::CleanDistributedDeviceTable() +{ + std::vector missingTables; + int errCode = sqliteStorageEngine_->CleanDistributedDeviceTable(missingTables); + if (errCode != E_OK) { + LOGE("Clean distributed device table failed. %d", errCode); + } + for (const auto &deviceTableName : missingTables) { + std::string deviceHash; + std::string tableName; + DBCommon::GetDeviceFromName(deviceTableName, deviceHash, tableName); + syncAbleEngine_->EraseDeviceWaterMark(deviceHash, false, tableName); + if (errCode != E_OK) { + LOGE("Erase water mark failed:%d", errCode); + return errCode; + } + } + return errCode; +} + +int SQLiteRelationalStore::Open(const RelationalDBProperties &properties) +{ + std::lock_guard lock(initalMutex_); + if (isInitialized_) { + LOGD("[RelationalStore][Open] relational db was already initialized."); + return E_OK; + } + + sqliteStorageEngine_ = new (std::nothrow) SQLiteSingleRelationalStorageEngine(properties); + if (sqliteStorageEngine_ == nullptr) { + LOGE("[RelationalStore][Open] Create storage engine failed"); + return -E_OUT_OF_MEMORY; + } + + int errCode = E_OK; + do { + errCode = InitStorageEngine(properties); + if (errCode != E_OK) { + LOGE("[RelationalStore][Open] Init database context fail! errCode = [%d]", errCode); + break; + } + + storageEngine_ = new (std::nothrow) RelationalSyncAbleStorage(sqliteStorageEngine_); + if (storageEngine_ == nullptr) { + LOGE("[RelationalStore][Open] Create syncable storage failed"); + errCode = -E_OUT_OF_MEMORY; + break; + } + + syncAbleEngine_ = std::make_unique(storageEngine_); + + errCode = CheckDBMode(); + if (errCode != E_OK) { + break; + } + + properties_ = properties; + errCode = GetSchemaFromMeta(); + if (errCode != E_OK) { + break; + } + + errCode = SaveLogTableVersionToMeta(); + if (errCode != E_OK) { + break; + } + + errCode = CleanDistributedDeviceTable(); + if (errCode != E_OK) { + break; + } + + isInitialized_ = true; + return E_OK; + } while (false); + + ReleaseResources(); + return errCode; +} + +void SQLiteRelationalStore::OnClose(const std::function ¬ifier) +{ + AutoLock lockGuard(this); + if (notifier) { + closeNotifiers_.push_back(notifier); + } else { + LOGW("Register 'Close()' notifier failed, notifier is null."); + } +} + +SQLiteSingleVerRelationalStorageExecutor *SQLiteRelationalStore::GetHandle(bool isWrite, int &errCode) const +{ + if (sqliteStorageEngine_ == nullptr) { + errCode = -E_INVALID_DB; + return nullptr; + } + + return static_cast(sqliteStorageEngine_->FindExecutor(isWrite, + OperatePerm::NORMAL_PERM, errCode)); +} +void SQLiteRelationalStore::ReleaseHandle(SQLiteSingleVerRelationalStorageExecutor *&handle) const +{ + if (handle == nullptr) { + return; + } + + if (sqliteStorageEngine_ != nullptr) { + StorageExecutor *databaseHandle = handle; + sqliteStorageEngine_->Recycle(databaseHandle); + handle = nullptr; + } +} + +int SQLiteRelationalStore::Sync(const ISyncer::SyncParma &syncParam, uint64_t connectionId) +{ + return syncAbleEngine_->Sync(syncParam, connectionId); +} + +// Called when a connection released. +void SQLiteRelationalStore::DecreaseConnectionCounter() +{ + int count = connectionCount_.fetch_sub(1, std::memory_order_seq_cst); + if (count <= 0) { + LOGF("Decrease db connection counter failed, count <= 0."); + return; + } + if (count != 1) { + return; + } + + LockObj(); + auto notifiers = std::move(closeNotifiers_); + UnlockObj(); + + for (auto ¬ifier : notifiers) { + if (notifier) { + notifier(); + } + } + + // Sync Close + syncAbleEngine_->Close(); + + if (sqliteStorageEngine_ != nullptr) { + delete sqliteStorageEngine_; + sqliteStorageEngine_ = nullptr; + } + // close will dec sync ref of storageEngine_ + DecObjRef(storageEngine_); +} + +void SQLiteRelationalStore::ReleaseDBConnection(RelationalStoreConnection *connection) +{ + if (connectionCount_.load() == 1) { + sqliteStorageEngine_->SetConnectionFlag(false); + } + + connectMutex_.lock(); + if (connection != nullptr) { + KillAndDecObjRef(connection); + DecreaseConnectionCounter(); + connectMutex_.unlock(); + KillAndDecObjRef(this); + } else { + connectMutex_.unlock(); + } +} + +void SQLiteRelationalStore::WakeUpSyncer() +{ + syncAbleEngine_->WakeUpSyncer(); +} + +int SQLiteRelationalStore::CreateDistributedTable(const std::string &tableName) +{ + bool schemaChanged = false; + int errCode = sqliteStorageEngine_->CreateDistributedTable(tableName, schemaChanged); + if (errCode != E_OK) { + LOGE("Create distributed table failed. %d", errCode); + } + if (schemaChanged) { + LOGD("Notify schema changed."); + storageEngine_->NotifySchemaChanged(); + } + return errCode; +} + +int SQLiteRelationalStore::RemoveDeviceData(const std::string &device, const std::string &tableName) +{ + std::map tables = sqliteStorageEngine_->GetSchemaRef().GetTables(); + if (!tableName.empty() && tables.find(tableName) == tables.end()) { + LOGW("Remove device data with table name which is not a distributed table or not exist."); + return E_OK; + } + + int errCode = E_OK; + auto *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + ReleaseHandle(handle); + return errCode; + } + + errCode = handle->DeleteDistributedDeviceTable(device, tableName); + if (errCode != E_OK) { + LOGE("delete device data failed. %d", errCode); + (void)handle->Rollback(); + ReleaseHandle(handle); + return errCode; + } + errCode = handle->Commit(); + storageEngine_->NotifySchemaChanged(); + ReleaseHandle(handle); + return (errCode != E_OK) ? errCode : syncAbleEngine_->EraseDeviceWaterMark(device, true, tableName); +} + +void SQLiteRelationalStore::RegisterObserverAction(const RelationalObserverAction &action) +{ + storageEngine_->RegisterObserverAction(action); +} + +int SQLiteRelationalStore::StopLifeCycleTimer() +{ + auto runtimeCxt = RuntimeContext::GetInstance(); + if (runtimeCxt == nullptr) { + return -E_INVALID_ARGS; + } + if (lifeTimerId_ != 0) { + TimerId timerId = lifeTimerId_; + lifeTimerId_ = 0; + runtimeCxt->RemoveTimer(timerId, false); + } + return E_OK; +} + +int SQLiteRelationalStore::StartLifeCycleTimer(const DatabaseLifeCycleNotifier ¬ifier) +{ + auto runtimeCxt = RuntimeContext::GetInstance(); + if (runtimeCxt == nullptr) { + return -E_INVALID_ARGS; + } + RefObject::IncObjRef(this); + TimerId timerId = 0; + int errCode = runtimeCxt->SetTimer(DBConstant::DEF_LIFE_CYCLE_TIME, + [this](TimerId id) -> int { + std::lock_guard lock(lifeCycleMutex_); + if (lifeCycleNotifier_) { + // normal identifier mode + auto identifier = properties_.GetStringProp(DBProperties::IDENTIFIER_DATA, ""); + auto userId = properties_.GetStringProp(DBProperties::USER_ID, ""); + lifeCycleNotifier_(identifier, userId); + } + return 0; + }, + [this]() { + int ret = RuntimeContext::GetInstance()->ScheduleTask([this]() { + RefObject::DecObjRef(this); + }); + if (ret != E_OK) { + LOGE("SQLiteSingleVerNaturalStore timer finalizer ScheduleTask, errCode %d", ret); + } + }, + timerId); + if (errCode != E_OK) { + lifeTimerId_ = 0; + LOGE("SetTimer failed:%d", errCode); + RefObject::DecObjRef(this); + return errCode; + } + + lifeCycleNotifier_ = notifier; + lifeTimerId_ = timerId; + return E_OK; +} + +int SQLiteRelationalStore::RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) +{ + int errCode; + { + std::lock_guard lock(lifeCycleMutex_); + if (lifeTimerId_ != 0) { + errCode = StopLifeCycleTimer(); + if (errCode != E_OK) { + LOGE("Stop the life cycle timer failed:%d", errCode); + return errCode; + } + } + + if (!notifier) { + return E_OK; + } + errCode = StartLifeCycleTimer(notifier); + if (errCode != E_OK) { + LOGE("Register life cycle timer failed:%d", errCode); + return errCode; + } + } + auto listener = std::bind(&SQLiteRelationalStore::HeartBeat, this); + storageEngine_->RegisterHeartBeatListener(listener); + return errCode; +} + +void SQLiteRelationalStore::HeartBeat() +{ + std::lock_guard lock(lifeCycleMutex_); + int errCode = ResetLifeCycleTimer(); + if (errCode != E_OK) { + LOGE("Heart beat for life cycle failed:%d", errCode); + } +} + +int SQLiteRelationalStore::ResetLifeCycleTimer() +{ + if (lifeTimerId_ == 0) { + return E_OK; + } + auto lifeNotifier = lifeCycleNotifier_; + lifeCycleNotifier_ = nullptr; + int errCode = StopLifeCycleTimer(); + if (errCode != E_OK) { + LOGE("[Reset timer]Stop the life cycle timer failed:%d", errCode); + } + return StartLifeCycleTimer(lifeNotifier); +} + +std::string SQLiteRelationalStore::GetStorePath() const +{ + return properties_.GetStringProp(DBProperties::DATA_DIR, ""); +} + +RelationalDBProperties SQLiteRelationalStore::GetProperties() const +{ + return properties_; +} + +void SQLiteRelationalStore::StopSync(uint64_t connectionId) +{ + return syncAbleEngine_->StopSync(connectionId); +} + +void SQLiteRelationalStore::Dump(int fd) +{ + std::string userId = ""; + std::string appId = ""; + std::string storeId = ""; + std::string label = ""; + if (sqliteStorageEngine_ != nullptr) { + userId = sqliteStorageEngine_->GetProperties().GetStringProp(DBProperties::USER_ID, ""); + appId = sqliteStorageEngine_->GetProperties().GetStringProp(DBProperties::APP_ID, ""); + storeId = sqliteStorageEngine_->GetProperties().GetStringProp(DBProperties::STORE_ID, ""); + label = sqliteStorageEngine_->GetProperties().GetStringProp(DBProperties::IDENTIFIER_DATA, ""); + } + label = DBCommon::TransferStringToHex(label); + DBDumpHelper::Dump(fd, "\tdb userId = %s, appId = %s, storeId = %s, label = %s\n", + userId.c_str(), appId.c_str(), storeId.c_str(), label.c_str()); + if (syncAbleEngine_ != nullptr) { + syncAbleEngine_->Dump(fd); + } +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.h b/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.h new file mode 100644 index 0000000000000000000000000000000000000000..42b867988888db44f8f4764c1149e950bb1d25ad --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SQLITE_RELATIONAL_STORE_H +#define SQLITE_RELATIONAL_STORE_H +#ifdef RELATIONAL_STORE + +#include +#include +#include + +#include "irelational_store.h" +#include "sqlite_single_relational_storage_engine.h" +#include "isyncer.h" +#include "sync_able_engine.h" +#include "relational_sync_able_storage.h" +#include "runtime_context.h" + +namespace DistributedDB { +using RelationalObserverAction = std::function; +class SQLiteRelationalStore : public IRelationalStore { +public: + SQLiteRelationalStore() = default; + ~SQLiteRelationalStore() override; + + RelationalStoreConnection *GetDBConnection(int &errCode) override; + int Open(const RelationalDBProperties &properties) override; + void OnClose(const std::function ¬ifier); + + SQLiteSingleVerRelationalStorageExecutor *GetHandle(bool isWrite, int &errCode) const; + void ReleaseHandle(SQLiteSingleVerRelationalStorageExecutor *&handle) const; + + int Sync(const ISyncer::SyncParma &syncParam, uint64_t connectionId); + + void ReleaseDBConnection(RelationalStoreConnection *connection); + + void WakeUpSyncer() override; + + // for test mock + const RelationalSyncAbleStorage *GetStorageEngine() + { + return storageEngine_; + } + + int CreateDistributedTable(const std::string &tableName); + + int RemoveDeviceData(const std::string &device, const std::string &tableName); + + void RegisterObserverAction(const RelationalObserverAction &action); + int RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier); + + std::string GetStorePath() const override; + + RelationalDBProperties GetProperties() const override; + + void StopSync(uint64_t connectionId); + + void Dump(int fd) override; + +private: + void ReleaseResources(); + + // 1 store 1 connection + void DecreaseConnectionCounter(); + int CheckDBMode(); + int GetSchemaFromMeta(); + int SaveSchemaToMeta(); + + int SaveLogTableVersionToMeta(); + + int CleanDistributedDeviceTable(); + + int StopLifeCycleTimer(); + int StartLifeCycleTimer(const DatabaseLifeCycleNotifier ¬ifier); + void HeartBeat(); + int ResetLifeCycleTimer(); + + // use for sync Interactive + std::unique_ptr syncAbleEngine_ = nullptr; // For storage operate sync function + // use ref obj same as kv + RelationalSyncAbleStorage *storageEngine_ = nullptr; // For storage operate data + SQLiteSingleRelationalStorageEngine *sqliteStorageEngine_ = nullptr; + + void IncreaseConnectionCounter(); + int InitStorageEngine(const RelationalDBProperties &kvDBProp); + std::mutex connectMutex_; + std::atomic connectionCount_ = 0; + std::vector> closeNotifiers_; + + mutable std::mutex schemaMutex_; + RelationalDBProperties properties_; + + mutable std::mutex initalMutex_; + bool isInitialized_ = false; + + // lifeCycle + std::mutex lifeCycleMutex_; + DatabaseLifeCycleNotifier lifeCycleNotifier_; + TimerId lifeTimerId_; +}; +} // namespace DistributedDB +#endif +#endif // SQLITE_RELATIONAL_STORE_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.cpp b/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0628f32ef384b3a223815a75fa44a8382e7c01b7 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "sqlite_relational_store_connection.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +SQLiteRelationalStoreConnection::SQLiteRelationalStoreConnection(SQLiteRelationalStore *store) + : RelationalStoreConnection(store) +{ + OnKill([this]() { + auto *store = GetDB(); + if (store == nullptr) { + return; + } + UnlockObj(); + store->StopSync(GetConnectionId()); + LockObj(); + }); +} +// Close and release the connection. +int SQLiteRelationalStoreConnection::Close() +{ + if (store_ == nullptr) { + return -E_INVALID_CONNECTION; + } + + if (isExclusive_.load()) { + return -E_BUSY; + } + + // check if transaction closed + { + std::lock_guard transactionLock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGW("Transaction started, need to rollback before close."); + int errCode = RollBack(); + if (errCode != E_OK) { + LOGE("Rollback transaction failed, %d.", errCode); + } + ReleaseExecutor(writeHandle_); + } + } + + static_cast(store_)->ReleaseDBConnection(this); + return E_OK; +} + +std::string SQLiteRelationalStoreConnection::GetIdentifier() +{ + return store_->GetProperties().GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, ""); +} + +SQLiteSingleVerRelationalStorageExecutor *SQLiteRelationalStoreConnection::GetExecutor(bool isWrite, int &errCode) const +{ + auto *store = GetDB(); + if (store == nullptr) { + errCode = -E_NOT_INIT; + LOGE("[RelationalConnection] store is null, get executor failed! errCode = [%d]", errCode); + return nullptr; + } + + return store->GetHandle(isWrite, errCode); +} + +void SQLiteRelationalStoreConnection::ReleaseExecutor(SQLiteSingleVerRelationalStorageExecutor *&executor) const +{ + auto *store = GetDB(); + if (store != nullptr) { + store->ReleaseHandle(executor); + } +} + +int SQLiteRelationalStoreConnection::StartTransaction() +{ + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + return -E_TRANSACT_STATE; + } + + int errCode = E_OK; + auto *handle = GetExecutor(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->StartTransaction(TransactType::DEFERRED); + if (errCode != E_OK) { + ReleaseExecutor(handle); + return errCode; + } + + writeHandle_ = handle; + return E_OK; +} + +// Commit the transaction +int SQLiteRelationalStoreConnection::Commit() +{ + std::lock_guard lock(transactionMutex_); + if (writeHandle_ == nullptr) { + LOGE("single version database is null or the transaction has not been started"); + return -E_INVALID_DB; + } + + int errCode = writeHandle_->Commit(); + ReleaseExecutor(writeHandle_); + LOGD("connection commit transaction!"); + return errCode; +} + +// Roll back the transaction +int SQLiteRelationalStoreConnection::RollBack() +{ + std::lock_guard lock(transactionMutex_); + if (writeHandle_ == nullptr) { + LOGE("Invalid handle for rollback or the transaction has not been started."); + return -E_INVALID_DB; + } + + int errCode = writeHandle_->Rollback(); + ReleaseExecutor(writeHandle_); + LOGI("connection rollback transaction!"); + return errCode; +} + +int SQLiteRelationalStoreConnection::CreateDistributedTable(const std::string &tableName) +{ + auto *store = GetDB(); + if (store == nullptr) { + LOGE("[RelationalConnection] store is null, get DB failed!"); + return -E_INVALID_CONNECTION; + } + + int errCode = store->CreateDistributedTable(tableName); + if (errCode != E_OK) { + LOGE("[RelationalConnection] crete distributed table failed. %d", errCode); + } + return errCode; +} + +int SQLiteRelationalStoreConnection::RemoveDeviceData(const std::string &device) +{ + return RemoveDeviceData(device, {}); +} + +int SQLiteRelationalStoreConnection::RemoveDeviceData(const std::string &device, const std::string &tableName) +{ + auto *store = GetDB(); + if (store == nullptr) { + LOGE("[RelationalConnection] store is null, get DB failed!"); + return -E_INVALID_CONNECTION; + } + + int errCode = store->RemoveDeviceData(device, tableName); + if (errCode != E_OK) { + LOGE("[RelationalConnection] remove device data failed. %d", errCode); + } + return errCode; +} + +int SQLiteRelationalStoreConnection::Pragma(int cmd, void *parameter) // reserve for interface function fix +{ + (void)cmd; + (void)parameter; + return E_OK; +} + +int SQLiteRelationalStoreConnection::TriggerAutoSync() +{ + return E_OK; +} + +int SQLiteRelationalStoreConnection::SyncToDevice(SyncInfo &info) +{ + auto *store = GetDB(); + if (store == nullptr) { + LOGE("[RelationalConnection] store is null, get executor failed!"); + return -E_INVALID_CONNECTION; + } + + { + AutoLock lockGuard(this); + IncObjRef(this); + ISyncer::SyncParma syncParam; + syncParam.devices = info.devices; + syncParam.mode = info.mode; + syncParam.wait = info.wait; + syncParam.isQuerySync = true; + syncParam.relationOnComplete = info.onComplete; + syncParam.syncQuery = QuerySyncObject(info.query); + syncParam.onFinalize = [this]() { DecObjRef(this); }; + int errCode = store->Sync(syncParam, GetConnectionId()); + if (errCode != E_OK) { + DecObjRef(this); + return errCode; + } + } + return E_OK; +} + +int SQLiteRelationalStoreConnection::RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) +{ + auto *store = GetDB(); + if (store == nullptr) { + LOGE("[RelationalConnection] store is null, get executor failed!"); + return -E_INVALID_CONNECTION; + } + + return store->RegisterLifeCycleCallback(notifier); +} + +void SQLiteRelationalStoreConnection::RegisterObserverAction(const RelationalObserverAction &action) +{ + static_cast(store_)->RegisterObserverAction(action); +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.h b/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..13b2dbd264a5b333f8ac36ba3cc6f1d16a423ba2 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SQLITE_RELATIONAL_STORE_CONNECTION_H +#define SQLITE_RELATIONAL_STORE_CONNECTION_H +#ifdef RELATIONAL_STORE + +#include +#include +#include "macro_utils.h" +#include "relational_store_connection.h" +#include "sqlite_single_ver_relational_storage_executor.h" +#include "sqlite_relational_store.h" + +namespace DistributedDB { +class SQLiteRelationalStoreConnection : public RelationalStoreConnection { +public: + explicit SQLiteRelationalStoreConnection(SQLiteRelationalStore *store); + + ~SQLiteRelationalStoreConnection() override = default; + + DISABLE_COPY_ASSIGN_MOVE(SQLiteRelationalStoreConnection); + + // Close and release the connection. + int Close() override; + int TriggerAutoSync() override; + int SyncToDevice(SyncInfo &info) override; + std::string GetIdentifier() override; + int CreateDistributedTable(const std::string &tableName) override; + int RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) override; + + int RemoveDeviceData(const std::string &device) override; + int RemoveDeviceData(const std::string &device, const std::string &tableName) override; + void RegisterObserverAction(const RelationalObserverAction &action) override; + +protected: + + int Pragma(int cmd, void *parameter) override; +private: + + SQLiteSingleVerRelationalStorageExecutor *GetExecutor(bool isWrite, int &errCode) const; + void ReleaseExecutor(SQLiteSingleVerRelationalStorageExecutor *&executor) const; + int StartTransaction(); + // Commit the transaction + int Commit(); + + // Roll back the transaction + int RollBack(); + + SQLiteSingleVerRelationalStorageExecutor *writeHandle_ = nullptr; + mutable std::mutex transactionMutex_; // used for transaction +}; +} // namespace DistributedDB +#endif +#endif // SQLITE_RELATIONAL_STORE_CONNECTION_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp b/mock/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28078bfcda86457f434b62ba0a68ad45abf1ff4a --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "sqlite_single_relational_storage_engine.h" + +#include "db_common.h" +#include "db_errno.h" +#include "res_finalizer.h" +#include "sqlite_single_ver_relational_storage_executor.h" + + +namespace DistributedDB { +namespace { + constexpr const char *RELATIONAL_SCHEMA_KEY = "relational_schema"; +} + +SQLiteSingleRelationalStorageEngine::SQLiteSingleRelationalStorageEngine(RelationalDBProperties properties) + : properties_(properties) +{} + +SQLiteSingleRelationalStorageEngine::~SQLiteSingleRelationalStorageEngine() {}; + +StorageExecutor *SQLiteSingleRelationalStorageEngine::NewSQLiteStorageExecutor(sqlite3 *dbHandle, bool isWrite, + bool isMemDb) +{ + return new (std::nothrow) SQLiteSingleVerRelationalStorageExecutor(dbHandle, isWrite); +} + +int SQLiteSingleRelationalStorageEngine::Upgrade(sqlite3 *db) +{ + return SQLiteUtils::CreateRelationalMetaTable(db); +} + +int SQLiteSingleRelationalStorageEngine::RegisterFunction(sqlite3 *db) const +{ + int errCode = SQLiteUtils::RegisterCalcHash(db); + if (errCode != E_OK) { + LOGE("[engine] register calculate hash failed!"); + return errCode; + } + + errCode = SQLiteUtils::RegisterGetSysTime(db); + if (errCode != E_OK) { + LOGE("[engine] register get sys time failed!"); + } + return E_OK; +} + +int SQLiteSingleRelationalStorageEngine::CreateNewExecutor(bool isWrite, StorageExecutor *&handle) +{ + sqlite3 *db = nullptr; + int errCode = SQLiteUtils::OpenDatabase(option_, db, false); + if (errCode != E_OK) { + return errCode; + } + do { + errCode = Upgrade(db); // create meta_data table. + if (errCode != E_OK) { + break; + } + + errCode = RegisterFunction(db); + if (errCode != E_OK) { + break; + } + + handle = NewSQLiteStorageExecutor(db, isWrite, false); + if (handle == nullptr) { + LOGE("[Relational] New SQLiteStorageExecutor[%d] for the pool failed.", isWrite); + errCode = -E_OUT_OF_MEMORY; + break; + } + return E_OK; + } while (false); + + (void)sqlite3_close_v2(db); + db = nullptr; + return errCode; +} + +int SQLiteSingleRelationalStorageEngine::ReleaseExecutor(SQLiteSingleVerRelationalStorageExecutor *&handle) +{ + if (handle == nullptr) { + return E_OK; + } + StorageExecutor *databaseHandle = handle; + Recycle(databaseHandle); + handle = nullptr; + return E_OK; +} + +void SQLiteSingleRelationalStorageEngine::SetSchema(const RelationalSchemaObject &schema) +{ + std::lock_guard lock(schemaMutex_); + schema_ = schema; +} + +const RelationalSchemaObject &SQLiteSingleRelationalStorageEngine::GetSchemaRef() const +{ + std::lock_guard lock(schemaMutex_); + return schema_; +} + +namespace { +int SaveSchemaToMetaTable(SQLiteSingleVerRelationalStorageExecutor *handle, const RelationalSchemaObject &schema) +{ + const Key schemaKey(RELATIONAL_SCHEMA_KEY, RELATIONAL_SCHEMA_KEY + strlen(RELATIONAL_SCHEMA_KEY)); + Value schemaVal; + DBCommon::StringToVector(schema.ToSchemaString(), schemaVal); + int errCode = handle->PutKvData(schemaKey, schemaVal); // save schema to meta_data + if (errCode != E_OK) { + LOGE("Save schema to meta table failed. %d", errCode); + } + return errCode; +} +} + +int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(const std::string &tableName, bool &schemaChanged) +{ + std::lock_guard lock(schemaMutex_); + RelationalSchemaObject tmpSchema = schema_; + bool isUpgrade = false; + if (tmpSchema.GetTable(tableName).GetTableName() == tableName) { + LOGW("distributed table was already created."); + isUpgrade = true; + int errCode = UpgradeDistributedTable(tableName); + if (errCode != E_OK) { + LOGE("Upgrade distributed table failed. %d", errCode); + return errCode; + } + } + + if (tmpSchema.GetTables().size() >= DBConstant::MAX_DISTRIBUTED_TABLE_COUNT) { + LOGE("The number of distributed tables is exceeds limit."); + return -E_MAX_LIMITS; + } + + LOGD("Create distributed table."); + int errCode = E_OK; + auto *handle = static_cast(FindExecutor(true, OperatePerm::NORMAL_PERM, + errCode)); + if (handle == nullptr) { + return errCode; + } + ResFinalizer finalizer([&handle, this] { this->ReleaseExecutor(handle); }); + + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + return errCode; + } + + TableInfo table; + errCode = handle->CreateDistributedTable(tableName, table, isUpgrade); + if (errCode != E_OK) { + LOGE("create distributed table failed. %d", errCode); + (void)handle->Rollback(); + return errCode; + } + + tmpSchema.AddRelationalTable(table); + errCode = SaveSchemaToMetaTable(handle, tmpSchema); + if (errCode != E_OK) { + LOGE("Save schema to meta table for create distributed table failed. %d", errCode); + (void)handle->Rollback(); + return errCode; + } + + errCode = handle->Commit(); + if (errCode == E_OK) { + schema_ = tmpSchema; + schemaChanged = true; + } + return errCode; +} + +int SQLiteSingleRelationalStorageEngine::UpgradeDistributedTable(const std::string &tableName) +{ + LOGD("Upgrade distributed table."); + RelationalSchemaObject tmpSchema = schema_; + int errCode = E_OK; + auto *handle = static_cast(FindExecutor(true, OperatePerm::NORMAL_PERM, + errCode)); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + ReleaseExecutor(handle); + return errCode; + } + + TableInfo newTable; + errCode = handle->UpgradeDistributedTable(tmpSchema.GetTable(tableName), newTable); + if (errCode != E_OK) { + LOGE("Upgrade distributed table failed. %d", errCode); + (void)handle->Rollback(); + ReleaseExecutor(handle); + return errCode; + } + + tmpSchema.AddRelationalTable(newTable); + errCode = SaveSchemaToMetaTable(handle, tmpSchema); + if (errCode != E_OK) { + LOGE("Save schema to meta table for upgrade distributed table failed. %d", errCode); + (void)handle->Rollback(); + ReleaseExecutor(handle); + return errCode; + } + + errCode = handle->Commit(); + if (errCode == E_OK) { + schema_ = tmpSchema; + } + ReleaseExecutor(handle); + return errCode; +} + +int SQLiteSingleRelationalStorageEngine::CleanDistributedDeviceTable(std::vector &missingTables) +{ + int errCode = E_OK; + auto handle = static_cast(FindExecutor(true, OperatePerm::NORMAL_PERM, + errCode)); + if (handle == nullptr) { + return errCode; + } + + std::lock_guard lock(schemaMutex_); + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + ReleaseExecutor(handle); + return errCode; + } + + errCode = handle->CheckAndCleanDistributedTable(schema_.GetTableNames(), missingTables); + if (errCode == E_OK) { + errCode = handle->Commit(); + if (errCode == E_OK) { + // Remove non-existent tables from the schema + for (const auto &tableName : missingTables) { + schema_.RemoveRelationalTable(tableName); + } + SaveSchemaToMetaTable(handle, schema_); // save schema to meta_data + } + } else { + LOGE("Check distributed table failed. %d", errCode); + (void)handle->Rollback(); + } + + ReleaseExecutor(handle); + return errCode; +} + +const RelationalDBProperties &SQLiteSingleRelationalStorageEngine::GetProperties() const +{ + return properties_; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h b/mock/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..34c89ab212f609558e267b7d91e8fc48705f6769 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SQLITE_RELATIONAL_ENGINE_H +#define SQLITE_RELATIONAL_ENGINE_H +#ifdef RELATIONAL_STORE + +#include "macro_utils.h" +#include "relationaldb_properties.h" +#include "sqlite_storage_engine.h" +#include "sqlite_single_ver_relational_storage_executor.h" + +namespace DistributedDB { +class SQLiteSingleRelationalStorageEngine : public SQLiteStorageEngine { +public: + explicit SQLiteSingleRelationalStorageEngine(RelationalDBProperties properties); + ~SQLiteSingleRelationalStorageEngine() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleRelationalStorageEngine); + + void SetSchema(const RelationalSchemaObject &schema); + + const RelationalSchemaObject &GetSchemaRef() const; + + int CreateDistributedTable(const std::string &tableName, bool &schemaChanged); + + int CleanDistributedDeviceTable(std::vector &missingTables); + + const RelationalDBProperties &GetProperties() const; + +protected: + StorageExecutor *NewSQLiteStorageExecutor(sqlite3 *db, bool isWrite, bool isMemDb) override; + int Upgrade(sqlite3 *db) override; + int CreateNewExecutor(bool isWrite, StorageExecutor *&handle) override; + +private: + // For executor. + int ReleaseExecutor(SQLiteSingleVerRelationalStorageExecutor *&handle); + + // For db. + int RegisterFunction(sqlite3 *db) const; + + int UpgradeDistributedTable(const std::string &tableName); + + RelationalSchemaObject schema_; + mutable std::mutex schemaMutex_; + + RelationalDBProperties properties_; +}; +} // namespace DistributedDB +#endif +#endif // SQLITE_RELATIONAL_ENGINE_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_import.h b/mock/distributeddb/storage/src/sqlite/sqlite_import.h new file mode 100644 index 0000000000000000000000000000000000000000..a21885cb83765adf9301896db6d010a2b2fb6983 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_import.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_IMPORT_H +#define SQLITE_IMPORT_H + +// using the "sqlite3sym.h" in OHOS +#ifndef USE_SQLITE_SYMBOLS +#include "sqlite3.h" +#else +#include "sqlite3sym.h" +#endif +#endif // SQLITE_IMPORT_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7816daa92a03a584c267d5178ad0beeb46d88df --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb.cpp @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_local_kvdb.h" + +#include + +#include "db_constant.h" +#include "db_common.h" +#include "log_print.h" +#include "platform_specific.h" +#include "package_file.h" +#include "kvdb_utils.h" +#include "local_database_oper.h" +#include "sqlite_local_kvdb_connection.h" +#include "sqlite_local_storage_engine.h" + +namespace DistributedDB { +namespace { + const std::string CREATE_SQL = + "CREATE TABLE IF NOT EXISTS data(key BLOB PRIMARY key, value BLOB);"; +} + +SQLiteLocalKvDB::SQLiteLocalKvDB() + : storageEngine_(nullptr) +{} + +SQLiteLocalKvDB::~SQLiteLocalKvDB() +{ + if (storageEngine_ != nullptr) { + delete storageEngine_; + storageEngine_ = nullptr; + } +} + +int SQLiteLocalKvDB::Open(const KvDBProperties &kvDBProp) +{ + int databaseType = kvDBProp.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + if (databaseType == KvDBProperties::LOCAL_TYPE) { + std::unique_ptr operation = std::make_unique(this, nullptr); + (void)operation->ClearExportedTempFiles(kvDBProp); + int errCode = operation->RekeyRecover(kvDBProp); + if (errCode != E_OK) { + LOGE("Recover for open db failed in local db:%d", errCode); + return errCode; + } + + errCode = operation->ClearImportTempFile(kvDBProp); + if (errCode != E_OK) { + LOGE("Recover for open db failed in multi version:%d", errCode); + return errCode; + } + } + + bool createIfNecessary = kvDBProp.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + std::string subDir = KvDBProperties::GetStoreSubDirectory(databaseType); + std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifierDir = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + int errCode = DBCommon::CreateStoreDirectory(dataDir, identifierDir, subDir, createIfNecessary); + if (errCode != E_OK) { + LOGE("Create directory for local database failed:%d", errCode); + return errCode; + } + + errCode = InitStorageEngine(kvDBProp); + if (errCode != E_OK) { + return errCode; + } + MyProp() = kvDBProp; + return E_OK; +} + +GenericKvDBConnection *SQLiteLocalKvDB::NewConnection(int &errCode) +{ + auto connection = new (std::nothrow) SQLiteLocalKvDBConnection(this); + if (connection == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + + errCode = E_OK; + return connection; +} + +void SQLiteLocalKvDB::Close() {} + +int SQLiteLocalKvDB::Rekey(const CipherPassword &passwd) +{ + if (storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + + std::unique_ptr operation = std::make_unique(this, storageEngine_); + return operation->Rekey(passwd); +} + +int SQLiteLocalKvDB::Export(const std::string &filePath, const CipherPassword &passwd) +{ + int errCode = E_OK; + // Exclusively write resources + SQLiteLocalStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + std::string devId = "local"; + + std::unique_ptr operation = std::make_unique(this, storageEngine_); + operation->SetLocalDevId(DBCommon::TransferHashString(devId)); + errCode = operation->Export(filePath, passwd); + + ReleaseHandle(handle); + return errCode; +} + +int SQLiteLocalKvDB::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = storageEngine_->TryToDisable(true, OperatePerm::IMPORT_MONOPOLIZE_PERM); + if (errCode != E_OK) { + LOGE("Failed to disable the database"); + return errCode; + } + + // Need to monopolize the entire process + std::unique_ptr operation = std::make_unique(this, storageEngine_); + errCode = operation->Import(filePath, passwd); + // restore the storage engine and the syncer. + storageEngine_->Enable(OperatePerm::IMPORT_MONOPOLIZE_PERM); + return errCode; +} + +int SQLiteLocalKvDB::InitDatabaseContext(const KvDBProperties &kvDBProp) +{ + return InitStorageEngine(kvDBProp); +} + +void SQLiteLocalKvDB::EnableAutonomicUpgrade() +{ + isAutonomicUpgradeEnable_ = true; +} + +int SQLiteLocalKvDB::RunExportLogic(CipherType type, const CipherPassword &passwd, const std::string &newDbName) +{ + OpenDbProperties option; + InitDataBaseOption(MyProp(), option); + option.createIfNecessary = true; + sqlite3 *db = nullptr; + int errCode = SQLiteUtils::OpenDatabase(option, db); + if (errCode != E_OK) { + LOGE("Open db for export error:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::ExportDatabase(db, type, passwd, newDbName); + if (errCode != E_OK) { + goto END; + } + +END: + (void)sqlite3_close_v2(db); + db = nullptr; + return errCode; +} + +int SQLiteLocalKvDB::RunRekeyLogic(CipherType type, const CipherPassword &passwd) +{ + OpenDbProperties option; + InitDataBaseOption(MyProp(), option); + option.createIfNecessary = true; + sqlite3 *db = nullptr; + int errCode = SQLiteUtils::OpenDatabase(option, db); + if (errCode != E_OK) { + LOGE("Open db for rekey error:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::Rekey(db, passwd); + if (errCode != E_OK) { + (void)sqlite3_close_v2(db); + db = nullptr; + return errCode; + } + (void)sqlite3_close_v2(db); + db = nullptr; + MyProp().SetPassword(option.cipherType, passwd); + if (storageEngine_ != nullptr) { + storageEngine_->Release(); + } + + return InitStorageEngine(MyProp()); +} + +SQLiteLocalStorageExecutor *SQLiteLocalKvDB::GetHandle(bool isWrite, int &errCode, OperatePerm perm) const +{ + if (storageEngine_ == nullptr) { + errCode = -E_INVALID_DB; + return nullptr; + } + + return static_cast(storageEngine_->FindExecutor(isWrite, perm, errCode)); +} + +int SQLiteLocalKvDB::GetVersion(const KvDBProperties &kvDBProp, int &version, bool &isDbExisted) const +{ + OpenDbProperties option; + InitDataBaseOption(kvDBProp, option); + isDbExisted = OS::CheckPathExistence(option.uri); + + int errCode = E_OK; + if (isDbExisted) { + errCode = SQLiteUtils::GetVersion(option, version); + } + return errCode; +} + +int SQLiteLocalKvDB::SetVersion(const KvDBProperties &kvDBProp, int version) +{ + OpenDbProperties option; + InitDataBaseOption(kvDBProp, option); + bool isDbExisted = OS::CheckPathExistence(option.uri); + if (!isDbExisted) { + return -E_NOT_FOUND; + } + return SQLiteUtils::SetUserVer(option, version); +} + +const KvDBProperties &SQLiteLocalKvDB::GetDbProperties() const +{ + return GetMyProperties(); +} + +KvDBProperties &SQLiteLocalKvDB::GetDbPropertyForUpdate() +{ + return MyProp(); +} + +void SQLiteLocalKvDB::ReleaseHandle(SQLiteLocalStorageExecutor *&handle) const +{ + if (storageEngine_ != nullptr) { + bool isCorrupted = handle->GetCorruptedStatus(); + StorageExecutor *databaseHandle = handle; + storageEngine_->Recycle(databaseHandle); + handle = nullptr; + if (isCorrupted) { + CorruptNotify(); + } + } +} + +int SQLiteLocalKvDB::InitStorageEngine(const KvDBProperties &kvDBProp) +{ + if (storageEngine_ == nullptr) { + // Create HandlePool + storageEngine_ = new (std::nothrow) SQLiteLocalStorageEngine(); + if (storageEngine_ == nullptr) { + LOGE("Create local sqlite storage engine OOM"); + return -E_OUT_OF_MEMORY; + } + } + + OpenDbProperties option; + InitDataBaseOption(kvDBProp, option); + StorageEngineAttr poolSize = {0, 1, 0, 4}; // 1 write 4 read at most. + int errCode = storageEngine_->InitSQLiteStorageEngine(poolSize, option); + if (errCode != E_OK) { + goto END; + } + + // We don't have to do version check here if the SQLiteLocalKvDB does not work as LocalStore. + // The isAutonomicUpgradeEnable_ true indicate that it work as LocalStore. Do version check in three case: + // Open the database, which call Open then InitStorageEngine. + // Import the database, which call InitDatabaseContext then InitStorageEngine. + // Rekey the database, which call RunRekeyLogic then InitStorageEngine. (This case is not necessary in fact) + errCode = CheckVersionAndUpgradeIfNeed(option); +END: + if (errCode != E_OK) { + LOGE("Init sqlite handler pool failed:%d", errCode); + // Transform the errCode. + } + return errCode; +} + +void SQLiteLocalKvDB::InitDataBaseOption(const KvDBProperties &kvDBProp, OpenDbProperties &option) const +{ + std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifierDir = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + std::string dbName = kvDBProp.GetStringProp(KvDBProperties::FILE_NAME, DBConstant::LOCAL_DATABASE_NAME); + int databaseType = kvDBProp.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + bool createIfNecessary = kvDBProp.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + std::string subDir = KvDBProperties::GetStoreSubDirectory(databaseType); + // Table name "data" should not be changed in the future, otherwise when an older software open a newer database + // with table of other name, we will create an second table as result which is not expected. + std::vector createTableSqls = {CREATE_SQL}; + CipherType cipherType; + CipherPassword passwd; + kvDBProp.GetPassword(cipherType, passwd); + std::string uri = dataDir + "/" + identifierDir + "/" + subDir + "/" + dbName + DBConstant::SQLITE_DB_EXTENSION; + option = {uri, createIfNecessary, false, createTableSqls, cipherType, passwd}; +} + +int SQLiteLocalKvDB::BackupCurrentDatabase(const KvDBProperties &properties, const std::string &dir) +{ + std::string baseDir; + int errCode = GetWorkDir(properties, baseDir); + if (errCode != E_OK) { + LOGE("[SqlLocalDb][Backup] GetWorkDir fail, errCode=%d.", errCode); + return errCode; + } + std::string dbName = properties.GetStringProp(KvDBProperties::FILE_NAME, DBConstant::LOCAL_DATABASE_NAME); + int databaseType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + std::string subDir = KvDBProperties::GetStoreSubDirectory(databaseType); + std::string currentDb = baseDir + "/" + subDir + "/" + dbName + DBConstant::SQLITE_DB_EXTENSION; + std::string dstDb = dir + "/" + dbName + DBConstant::SQLITE_DB_EXTENSION; + errCode = DBCommon::CopyFile(currentDb, dstDb); + if (errCode != E_OK) { + LOGE("Copy the local current db error:%d", errCode); + } + return errCode; +} + +int SQLiteLocalKvDB::ImportDatabase(const KvDBProperties &properties, const std::string &dir, + const CipherPassword &passwd) +{ + std::string baseDir; + int errCode = GetWorkDir(properties, baseDir); + if (errCode != E_OK) { + return errCode; + } + std::string dbName = properties.GetStringProp(KvDBProperties::FILE_NAME, DBConstant::LOCAL_DATABASE_NAME); + int databaseType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + std::string subDir = KvDBProperties::GetStoreSubDirectory(databaseType); + std::string dstDb = baseDir + "/" + subDir + "/" + dbName + DBConstant::SQLITE_DB_EXTENSION; + std::string currentDb = dir + "/" + dbName + DBConstant::SQLITE_DB_EXTENSION; + CipherType cipherType; + CipherPassword dstPasswd; + properties.GetPassword(cipherType, dstPasswd); + return SQLiteUtils::ExportDatabase(currentDb, cipherType, passwd, dstDb, dstPasswd); +} + +int SQLiteLocalKvDB::RemoveKvDB(const KvDBProperties &properties) +{ + // Only care the data directory and the db name. + std::string storeOnlyDir; + std::string storeDir; + GenericKvDB::GetStoreDirectory(properties, KvDBProperties::LOCAL_TYPE, storeDir, storeOnlyDir); + int dbType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + return KvDBUtils::RemoveKvDB(storeDir, storeOnlyDir, KvDBProperties::GetStoreSubDirectory(dbType)); +} + +int SQLiteLocalKvDB::GetKvDBSize(const KvDBProperties &properties, uint64_t &size) const +{ + std::string storeOnlyDir; + std::string storeDir; + GenericKvDB::GetStoreDirectory(properties, KvDBProperties::LOCAL_TYPE, storeDir, storeOnlyDir); + int dbType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + return KvDBUtils::GetKvDbSize(storeDir, storeOnlyDir, KvDBProperties::GetStoreSubDirectory(dbType), size); +} + +int SQLiteLocalKvDB::CheckVersionAndUpgradeIfNeed(const OpenDbProperties &openProp) +{ + if (!isAutonomicUpgradeEnable_) { + return E_OK; + } + int dbVersion = 0; + int errCode = SQLiteUtils::GetVersion(openProp, dbVersion); + if (errCode != E_OK) { + LOGE("[SqlLocalDb][CheckUpgrade] GetVersion fail, errCode=%d.", errCode); + return errCode; + } + LOGD("[SqlLocalDb][CheckUpgrade] DbFile Version=%d, CurVersion=%d.", dbVersion, LOCAL_STORE_VERSION_CURRENT); + if (dbVersion > LOCAL_STORE_VERSION_CURRENT) { + return -E_VERSION_NOT_SUPPORT; + } + // For version equal or less LOCAL_STORE_VERSION_CURRENT except zero, we can do nothing currently + if (dbVersion != 0) { + return E_OK; + } + errCode = SQLiteUtils::SetUserVer(openProp, LOCAL_STORE_VERSION_CURRENT); + if (errCode != E_OK) { + LOGE("[SqlLocalDb][CheckUpgrade] SetUserVer fail, errCode=%d.", errCode); + return errCode; + } + return E_OK; +} + +DEFINE_OBJECT_TAG_FACILITIES(SQLiteLocalKvDB) +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb.h b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb.h new file mode 100644 index 0000000000000000000000000000000000000000..631497a23c45ca77e6b9e9dc8408ac79410383d6 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_LOCAL_KV_DB_H +#define SQLITE_LOCAL_KV_DB_H + +#include +#include + +#include "local_kvdb.h" +#include "sqlite_local_storage_executor.h" +#include "sqlite_storage_engine.h" + +namespace DistributedDB { +class SQLiteLocalKvDB final : public LocalKvDB { +public: + SQLiteLocalKvDB(); + ~SQLiteLocalKvDB() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteLocalKvDB); + + // Save the option and uri for sqlite + int Open(const KvDBProperties &kvDBProp) override; + + // Create a connection object. + GenericKvDBConnection *NewConnection(int &errCode) override; + + // Invoked automatically when connection count is zero + void Close() override; + + int Rekey(const CipherPassword &passwd) override; + + int Export(const std::string &filePath, const CipherPassword &passwd) override; + + int Import(const std::string &filePath, const CipherPassword &passwd) override; + + int RunExportLogic(CipherType type, const CipherPassword &passwd, const std::string &newDbName); + + int RunRekeyLogic(CipherType type, const CipherPassword &passwd); + + SQLiteLocalStorageExecutor *GetHandle(bool isWrite, int &errCode, + OperatePerm perm = OperatePerm::NORMAL_PERM) const; + + void ReleaseHandle(SQLiteLocalStorageExecutor *&handle) const; + + int GetVersion(const KvDBProperties &kvDBProp, int &version, bool &isDbExisted) const; + + int SetVersion(const KvDBProperties &kvDBProp, int version); + + const KvDBProperties &GetDbProperties() const; + + KvDBProperties &GetDbPropertyForUpdate(); + + static int BackupCurrentDatabase(const KvDBProperties &properties, const std::string &dir); + + static int ImportDatabase(const KvDBProperties &properties, const std::string &dir, const CipherPassword &passwd); + + int RemoveKvDB(const KvDBProperties &properties) override; + + int GetKvDBSize(const KvDBProperties &properties, uint64_t &size) const override; + + int InitDatabaseContext(const KvDBProperties &kvDBProp); + + void EnableAutonomicUpgrade() override; + +private: + int InitStorageEngine(const KvDBProperties &kvDBProp); + + void InitDataBaseOption(const KvDBProperties &kvDBProp, OpenDbProperties &option) const; + + int CheckVersionAndUpgradeIfNeed(const OpenDbProperties &openProp); + + DECLARE_OBJECT_TAG(SQLiteLocalKvDB); + + bool isAutonomicUpgradeEnable_ = false; + SQLiteStorageEngine *storageEngine_; +}; +} // namespace DistributedDB + +#endif // SQLITE_LOCAL_KV_DB_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4379fa68027942e869ae3dbc5896b5e6bb11d20c --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.cpp @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_local_kvdb_connection.h" + +#include + +#include "log_print.h" +#include "db_constant.h" +#include "sqlite_utils.h" +#include "sqlite_local_kvdb.h" +#include "sqlite_local_kvdb_snapshot.h" +#include "kvdb_commit_notify_filterable_data.h" +#include "sqlite_local_storage_executor.h" + +namespace DistributedDB { +SQLiteLocalKvDBConnection::SQLiteLocalKvDBConnection(SQLiteLocalKvDB *kvDB) + : GenericKvDBConnection(kvDB), + writeHandle_(nullptr) +{} + +SQLiteLocalKvDBConnection::~SQLiteLocalKvDBConnection() +{} + +int SQLiteLocalKvDBConnection::Get(const IOption &option, const Key &key, Value &value) const +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + { + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + return writeHandle_->Get(key, value); + } + } + int errCode = E_OK; + SQLiteLocalStorageExecutor *handle = GetDB()->GetHandle(false, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->Get(key, value); + GetDB()->ReleaseHandle(handle); + return errCode; +} + +int SQLiteLocalKvDBConnection::Put(const IOption &option, const Key &key, const Value &value) +{ + int errCode = CheckDataStatus(key, value, false); + if (errCode != E_OK) { + return errCode; + } + std::lock_guard lock(transactionMutex_); + bool isAuto = false; + errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("StartTransaction failed when Put error:%d", errCode); + return errCode; + } + + errCode = writeHandle_->Put(key, value); + if (errCode != E_OK) { + if (isAuto) { + int errCodeRollBack = RollBackInner(); + LOGI("Put failed,need rollback! errCode:[%d]", errCodeRollBack); + } + return errCode; + } + if (isAuto) { + errCode = CommitInner(); + if (errCode != E_OK) { + LOGE("CommitTransaction failed when Put error:%d", errCode); + return errCode; + } + } + + return errCode; +} + +int SQLiteLocalKvDBConnection::Delete(const IOption &option, const Key &key) +{ + int errCode = CheckDataStatus(key, {}, true); + if (errCode != E_OK) { + return errCode; + } + std::lock_guard lock(transactionMutex_); + bool isAuto = false; + errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("StartTransaction failed when Delete error:%d", errCode); + return errCode; + } + + errCode = writeHandle_->Delete(key); + if (errCode != E_OK) { + if (isAuto) { + int errCodeRollBack = RollBackInner(); + LOGI("Delete failed, need rollback! errcode:[%d]", errCodeRollBack); + } + return errCode; + } + + if (isAuto) { + errCode = CommitInner(); + if (errCode != E_OK) { + LOGE("CommitInner failed while delete:%d", errCode); + return errCode; + } + } + return E_OK; +} + +int SQLiteLocalKvDBConnection::Clear(const IOption &option) +{ + std::lock_guard lock(transactionMutex_); + bool isAuto = false; + int errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("StartTransaction failed when Clear error:%d", errCode); + return errCode; + } + + errCode = writeHandle_->Clear(); + if (errCode != E_OK) { + if (isAuto) { + int errCodeRollBack = RollBackInner(); + LOGI("Clear failed, need rollback! RollBack result is [%d]", errCodeRollBack); + } + return errCode; + } + + if (isAuto) { + errCode = CommitInner(); + if (errCode != E_OK) { + LOGE("CommitInner failed when Clear error:%d", errCode); + return errCode; + } + } + + return E_OK; +} + +int SQLiteLocalKvDBConnection::GetEntries(const IOption &option, const Key &keyPrefix, + std::vector &entries) const +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + if (keyPrefix.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + { + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + return writeHandle_->GetEntries(keyPrefix, entries); + } + } + int errCode = E_OK; + SQLiteLocalStorageExecutor *handle = GetDB()->GetHandle(false, errCode); + if (handle == nullptr) { + return errCode; + } + errCode = handle->GetEntries(keyPrefix, entries); + GetDB()->ReleaseHandle(handle); + return errCode; +} + +int SQLiteLocalKvDBConnection::PutBatch(const IOption &option, const std::vector &entries) +{ + if (entries.empty() || entries.size() > DBConstant::MAX_BATCH_SIZE) { + return -E_INVALID_ARGS; + } + for (const auto &item : entries) { + if (CheckDataStatus(item.key, item.value, false) != E_OK) { + return -E_INVALID_ARGS; + } + } + + bool isAuto = false; + std::lock_guard lock(transactionMutex_); + int errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("StartTransaction failed when PutBatch error:%d", errCode); + return errCode; + } + + for (const auto &entry : entries) { + // first argument is key and second argument is value. + errCode = writeHandle_->Put(entry.key, entry.value); + if (errCode != E_OK) { + if (isAuto) { + int errCodeRollBack = RollBackInner(); + LOGI("PutBatch failed,need rollback! RollBack result is %d", errCodeRollBack); + } + return errCode; + } + } + + if (isAuto) { + errCode = CommitInner(); + if (errCode != E_OK) { + LOGE("CommitTransaction failed when PutBatch error:%d", errCode); + return errCode; + } + } + + return E_OK; +} + +int SQLiteLocalKvDBConnection::DeleteBatch(const IOption &option, const std::vector &keys) +{ + if (keys.empty() || keys.size() > DBConstant::MAX_BATCH_SIZE) { + LOGE("[Local]DeleteBatch size[%zu]!", keys.size()); + return -E_INVALID_ARGS; + } + for (const auto &item : keys) { + if (item.empty() || item.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + } + + bool isAuto = false; + std::lock_guard lock(transactionMutex_); + int errCode = StartTransactionInner(isAuto); + if (errCode != E_OK) { + LOGE("StartTransaction failed when DeleteBatch error:%d", errCode); + return errCode; + } + + errCode = writeHandle_->DeleteBatch(keys); + + if (isAuto) { + if (errCode == E_OK) { + errCode = CommitInner(); + if (errCode != E_OK) { + LOGE("CommitTransaction failed when DeleteBatch error:%d", errCode); + return errCode; + } + } else { + int errCodeRollBack = RollBackInner(); + LOGI("DeleteBatchm need rollback! RollBack result is [%d]", errCodeRollBack); + return errCode; + } + } + + return errCode; +} + +// when GetSnapshot successfully, you must delete snapshot by ReleaseSnapshot +int SQLiteLocalKvDBConnection::GetSnapshot(IKvDBSnapshot *&snapshot) const +{ + if (kvDB_ == nullptr) { + snapshot = nullptr; + return -E_INVALID_DB; + } + + int errCode = E_OK; + IKvDBConnection *newConnect = kvDB_->GetDBConnection(errCode); + if (errCode != E_OK) { + LOGE("failed to get the new connection"); + return errCode; + } + + SQLiteLocalKvDBSnapshot *dbSnapshot = new (std::nothrow) SQLiteLocalKvDBSnapshot(newConnect); + if (dbSnapshot == nullptr) { + newConnect->Close(); + delete newConnect; + return -E_OUT_OF_MEMORY; + } + + snapshot = dbSnapshot; + { + std::lock_guard lock(snapshotMutex_); + snapshots_.insert(dbSnapshot); + } + + return E_OK; +} + +void SQLiteLocalKvDBConnection::ReleaseSnapshot(IKvDBSnapshot *&snapshot) +{ + if (snapshot != nullptr && kvDB_ != nullptr) { + std::lock_guard lock(snapshotMutex_); + SQLiteLocalKvDBSnapshot *sqliteSnapshot = static_cast(snapshot); + sqliteSnapshot->Close(); + snapshots_.erase(snapshot); + delete snapshot; + snapshot = nullptr; + } +} + +int SQLiteLocalKvDBConnection::StartTransaction() +{ + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + return -E_TRANSACT_STATE; + } + bool isAuto = false; + return StartTransactionInner(isAuto); +} + +int SQLiteLocalKvDBConnection::Commit() +{ + std::lock_guard lock(transactionMutex_); + return CommitInner(); +} + +int SQLiteLocalKvDBConnection::RollBack() +{ + std::lock_guard lock(transactionMutex_); + return RollBackInner(); +} + +bool SQLiteLocalKvDBConnection::IsTransactionStarted() const +{ + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + return true; + } + return false; +} + +int SQLiteLocalKvDBConnection::PreClose() +{ + { + std::lock_guard snapshotLock(snapshotMutex_); + if (snapshots_.size() != 0) { + LOGE("Close failed, the connection have unreleased snapshot."); + return -E_BUSY; + } + } + std::lock_guard transactionLock(transactionMutex_); + if (writeHandle_ != nullptr) { + writeHandle_->RollBack(); + GetDB()->ReleaseHandle(writeHandle_); + } + return E_OK; +} + +int SQLiteLocalKvDBConnection::TranslateObserverModeToEventTypes(unsigned mode, + std::list &eventTypes) const +{ + return E_OK; +} + +int SQLiteLocalKvDBConnection::StartTransactionInner(bool &isAuto) +{ + // if the transaction has been started, writeHandle wouldn't be nullptr. + if (writeHandle_ != nullptr) { + return E_OK; + } + + if (kvDB_ == nullptr) { + LOGE("local database is null"); + return -E_INVALID_DB; + } + + int errCode = E_OK; + SQLiteLocalStorageExecutor *handle = GetDB()->GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->StartTransaction(); + if (errCode != E_OK) { + GetDB()->ReleaseHandle(handle); + return errCode; + } + writeHandle_ = handle; + // only the transaction has not been started before, set the flag to true. + // the manual operation would ignore the flag. + isAuto = true; + return E_OK; +} + +int SQLiteLocalKvDBConnection::CommitInner() +{ + if (writeHandle_ == nullptr) { + LOGE("local database is null or the transaction has not been started"); + return -E_INVALID_DB; + } + + int errCode = writeHandle_->Commit(); + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + GetDB()->ReleaseHandle(writeHandle_); + return errCode; +} + +int SQLiteLocalKvDBConnection::RollBackInner() +{ + if (writeHandle_ == nullptr) { + LOGE("Invalid handle for rollback or the transaction has not been started."); + return -E_INVALID_DB; + } + + int errCode = writeHandle_->RollBack(); + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + GetDB()->ReleaseHandle(writeHandle_); + return errCode; +} + +int SQLiteLocalKvDBConnection::Rekey(const CipherPassword &passwd) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + std::lock_guard lock(transactionMutex_); + // return BUSY if in transaction + if (writeHandle_ != nullptr) { + LOGE("Transaction exists for rekey failed"); + return -E_BUSY; + } + // Check the connection number. + int errCode = kvDB_->TryToDisableConnection(OperatePerm::REKEY_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + + // Check the observer. + errCode = GenericKvDBConnection::PreCheckExclusiveStatus(); + if (errCode != E_OK) { + kvDB_->ReEnableConnection(OperatePerm::REKEY_MONOPOLIZE_PERM); + return errCode; + } + // If only have one connection, just have the transactionMutex_; + // It means there would not be another operation on this connection. + errCode = kvDB_->Rekey(passwd); + + GenericKvDBConnection::ResetExclusiveStatus(); + kvDB_->ReEnableConnection(OperatePerm::REKEY_MONOPOLIZE_PERM); + return errCode; +} + +int SQLiteLocalKvDBConnection::Export(const std::string &filePath, const CipherPassword &passwd) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + return kvDB_->Export(filePath, passwd); +} + +int SQLiteLocalKvDBConnection::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + { + std::lock_guard lock(transactionMutex_); + // return BUSY if in transaction + if (writeHandle_ != nullptr) { + LOGE("Transaction exists for rekey failed"); + return -E_BUSY; + } + } + std::lock_guard importLock(importMutex_); + int errCode = kvDB_->TryToDisableConnection(OperatePerm::IMPORT_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + + errCode = GenericKvDBConnection::PreCheckExclusiveStatus(); + if (errCode != E_OK) { + kvDB_->ReEnableConnection(OperatePerm::IMPORT_MONOPOLIZE_PERM); + return errCode; + } + errCode = kvDB_->Import(filePath, passwd); + GenericKvDBConnection::ResetExclusiveStatus(); + kvDB_->ReEnableConnection(OperatePerm::IMPORT_MONOPOLIZE_PERM); + return errCode; +} + +int SQLiteLocalKvDBConnection::CheckDataStatus(const Key &key, const Value &value, bool isDeleted) const +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + return static_cast(kvDB_)->CheckDataStatus(key, value, isDeleted); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.h b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..f4efd05b6474ffe003dda05fa725050df1a0e9fb --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_LOCAL_KV_DB_CONNECTION_H +#define SQLITE_LOCAL_KV_DB_CONNECTION_H + +#include +#include + +#include "db_errno.h" +#include "kvdb_properties.h" +#include "generic_kvdb_connection.h" +#include "sqlite_local_storage_executor.h" + +namespace DistributedDB { +class SQLiteLocalKvDB; + +class SQLiteLocalKvDBConnection : public GenericKvDBConnection { +public: + explicit SQLiteLocalKvDBConnection(SQLiteLocalKvDB *kvDB); + ~SQLiteLocalKvDBConnection() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteLocalKvDBConnection); + + // Get the value from the sqlite database + int Get(const IOption &option, const Key &key, Value &value) const override; + + // Put the value to the sqlite database + int Put(const IOption &option, const Key &key, const Value &value) override; + + // Delete the value from the sqlite database + int Delete(const IOption &option, const Key &key) override; + + // Clear all the data from the sqlite database + int Clear(const IOption &option) override; + + // Get all the data which have the prefix key from the sqlite database + int GetEntries(const IOption &option, const Key &keyPrefix, std::vector &entries) const override; + + // Put the batch data to the sqlite database + int PutBatch(const IOption &option, const std::vector &entries) override; + + // Delete the batch data from the sqlite database according to the key from the set + int DeleteBatch(const IOption &option, const std::vector &keys) override; + + // Get the snapshot + int GetSnapshot(IKvDBSnapshot *&snapshot) const override; + + // Release the snapshot + void ReleaseSnapshot(IKvDBSnapshot *&snapshot) override; + + // Next step interface + // Start the transaction + int StartTransaction() override; + + // Commit the transaction + int Commit() override; + + // Roll back the transaction + int RollBack() override; + + // Check if the transaction already started manually + bool IsTransactionStarted() const override; + + // Called when close the connection + int PreClose() override; + + // Parse event types(from observer mode). + int TranslateObserverModeToEventTypes(unsigned mode, std::list &eventTypes) const override; + + int Rekey(const CipherPassword &passwd) override; + + int Export(const std::string &filePath, const CipherPassword &passwd) override; + + int Import(const std::string &filePath, const CipherPassword &passwd) override; + +private: + // Start the transaction + int StartTransactionInner(bool &isAuto); + + // Commit the transaction + int CommitInner(); + + // Roll back the transaction + int RollBackInner(); + + int CheckDataStatus(const Key &key, const Value &value, bool isDeleted) const; + + SQLiteLocalStorageExecutor *writeHandle_; // only existed while in transaction. + + mutable std::mutex transactionMutex_; + mutable std::set snapshots_; + mutable std::mutex snapshotMutex_; + std::mutex importMutex_; +}; +}; // namespace DistributedDB + +#endif // SQLITE_LOCAL_KV_DB_CONNECTION_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..430ebd6051e50b27c96be626c53b0fdad6c674a7 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_local_kvdb_snapshot.h" +#include "sqlite_local_kvdb_connection.h" + +namespace DistributedDB { +SQLiteLocalKvDBSnapshot::SQLiteLocalKvDBSnapshot(IKvDBConnection *connect) + : connect_(connect) +{} + +SQLiteLocalKvDBSnapshot::~SQLiteLocalKvDBSnapshot() +{ + connect_ = nullptr; +} + +int SQLiteLocalKvDBSnapshot::Get(const Key &key, Value &value) const +{ + if (connect_ == nullptr) { + return -E_INVALID_DB; + } + IOption option; + return connect_->Get(option, key, value); +} + +int SQLiteLocalKvDBSnapshot::GetEntries(const Key &keyPrefix, std::vector &entries) const +{ + if (connect_ == nullptr) { + return -E_INVALID_DB; + } + IOption option; + return connect_->GetEntries(option, keyPrefix, entries); +} + +void SQLiteLocalKvDBSnapshot::Close() +{ + if (connect_ != nullptr) { + connect_->Close(); + connect_ = nullptr; + } +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.h b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.h new file mode 100644 index 0000000000000000000000000000000000000000..cc8b7a1ce46f84eaff624f41e83ce2a71a90c2bd --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_LOCAL_KV_DB_SNAP_SHOT_H +#define SQLITE_LOCAL_KV_DB_SNAP_SHOT_H + +#include + +#include "macro_utils.h" +#include "db_errno.h" +#include "db_types.h" +#include "ikvdb_snapshot.h" +#include "sqlite_local_kvdb_connection.h" + +namespace DistributedDB { +class SQLiteLocalKvDBSnapshot : public IKvDBSnapshot { +public: + explicit SQLiteLocalKvDBSnapshot(IKvDBConnection *connect); + ~SQLiteLocalKvDBSnapshot(); + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteLocalKvDBSnapshot); + + // Get the value of the key + int Get(const Key &key, Value &value) const override; + + // Get the entries of the key set + int GetEntries(const Key &keyPrefix, std::vector &entries) const override; + + void Close(); + +private: + IKvDBConnection *connect_; +}; +} // namespace DistributedDB + +#endif // SQLITE_LOCAL_KV_DB_SNAP_SHOT_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..872887855dcb209f7e5392df803a68b8476aeb2f --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_local_storage_engine.h" +#include "sqlite_local_storage_executor.h" + +namespace DistributedDB { +SQLiteLocalStorageEngine::SQLiteLocalStorageEngine() +{} + +SQLiteLocalStorageEngine::~SQLiteLocalStorageEngine() +{} + +StorageExecutor *SQLiteLocalStorageEngine::NewSQLiteStorageExecutor(sqlite3 *dbHandle, bool isWrite, bool isMemDb) +{ + return new (std::nothrow) SQLiteLocalStorageExecutor(dbHandle, isWrite, isMemDb); +} +} diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.h b/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..d36ea4b1f4293eaa5d82e232ab0166af989194a1 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_LOCAL_STORAGE_ENGINE_H +#define SQLITE_LOCAL_STORAGE_ENGINE_H + +#include "macro_utils.h" +#include "sqlite_storage_engine.h" + +namespace DistributedDB { +class SQLiteLocalStorageEngine : public SQLiteStorageEngine { +public: + SQLiteLocalStorageEngine(); + ~SQLiteLocalStorageEngine() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteLocalStorageEngine); + +protected: + StorageExecutor *NewSQLiteStorageExecutor(sqlite3 *dbHandle, bool isWrite, bool isMemDb) override; +}; +} // namespace DistributedDB + +#endif // SQLITE_DB_HANDLE_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c6bbc76400808f7e1063c5ba15c38d7894c2a877 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_local_storage_executor.h" + +#include "log_print.h" +#include "db_errno.h" +#include "sqlite_utils.h" + +namespace DistributedDB { +namespace { + const std::string CLEAR_SQL = "DELETE FROM data;"; + const std::string SELECT_SQL = "SELECT value FROM data WHERE key=?;"; + const std::string SELECT_BATCH_SQL = + "SELECT key, value FROM data WHERE key>=? AND key<=? ORDER BY key ASC;"; + const std::string INSERT_SQL = "INSERT OR REPLACE INTO data VALUES(?,?);"; + const std::string DELETE_SQL = "DELETE FROM data WHERE key=?;"; + + const int BIND_KEY_INDEX = 1; + const int BIND_VAL_INDEX = 2; + + const int SELECT_BIND_KEY_INDEX = 1; // index of the binding key index for select one entry. + + const int SELECT_RESULT_KEY_INDEX = 0; + const int SELECT_RESULT_VAL_INDEX = 1; +} + +SQLiteLocalStorageExecutor::SQLiteLocalStorageExecutor(sqlite3 *dbHandle, bool writable, bool isMemDb) + : SQLiteStorageExecutor(dbHandle, writable, isMemDb) +{} + +SQLiteLocalStorageExecutor::~SQLiteLocalStorageExecutor() +{} + +int SQLiteLocalStorageExecutor::Get(const Key &key, Value &value) const +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_SQL, statement); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, SELECT_BIND_KEY_INDEX, key, false); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = -E_NOT_FOUND; + goto END; + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + goto END; + } + + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, value); + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteLocalStorageExecutor::Clear() +{ + int errCode = SQLiteUtils::ExecuteRawSQL(dbHandle_, CLEAR_SQL); + return CheckCorruptedStatus(errCode); +} + +int SQLiteLocalStorageExecutor::GetEntries(const Key &keyPrefix, + std::vector &entries) const +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_BATCH_SQL, statement); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + Entry entry; + errCode = SQLiteUtils::BindPrefixKey(statement, SELECT_BIND_KEY_INDEX, keyPrefix); // first arg is prefix key + if (errCode != E_OK) { + goto END; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = SQLiteUtils::GetColumnBlobValue(statement, SELECT_RESULT_KEY_INDEX, entry.key); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::GetColumnBlobValue(statement, SELECT_RESULT_VAL_INDEX, entry.value); + if (errCode != E_OK) { + goto END; + } + + entries.push_back(std::move(entry)); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + break; + } else { + LOGE("SQLite step failed:%d", errCode); + goto END; + } + } while (true); + + // if select no result, return the -E_NOT_FOUND. + if (entries.empty()) { + errCode = -E_NOT_FOUND; + } else { + errCode = E_OK; + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteLocalStorageExecutor::PutBatch(const std::vector &entries) +{ + if (entries.empty()) { + return -E_INVALID_ARGS; + } + + for (const auto &entry : entries) { + // first argument is key and second argument is value. + int errCode = Put(entry.key, entry.value); + if (errCode != E_OK) { + LOGE("PutBatch failed: %d", errCode); + return CheckCorruptedStatus(errCode); + } + } + + return E_OK; +} + +int SQLiteLocalStorageExecutor::DeleteBatch(const std::vector &keys) +{ + if (keys.empty()) { + return -E_INVALID_ARGS; + } + + bool isAllNoExisted = true; + + for (const auto &key : keys) { + int errCode = Delete(key); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + return CheckCorruptedStatus(errCode); + } + if (errCode != -E_NOT_FOUND && isAllNoExisted == true) { + isAllNoExisted = false; + } + } + + if (isAllNoExisted) { + return -E_NOT_FOUND; + } + + return E_OK; +} + +int SQLiteLocalStorageExecutor::StartTransaction() +{ + int errCode = SQLiteUtils::BeginTransaction(dbHandle_); + return CheckCorruptedStatus(errCode); +} + +int SQLiteLocalStorageExecutor::Commit() +{ + int errCode = SQLiteUtils::CommitTransaction(dbHandle_); + return CheckCorruptedStatus(errCode); +} + +int SQLiteLocalStorageExecutor::RollBack() +{ + int errCode = SQLiteUtils::RollbackTransaction(dbHandle_); + return CheckCorruptedStatus(errCode); +} + +int SQLiteLocalStorageExecutor::Put(const Key &key, const Value &value) +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, INSERT_SQL, statement); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_KEY_INDEX, key, false); + if (errCode != E_OK) { + LOGE("Failed to bind the key."); + goto END; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_VAL_INDEX, value, true); + if (errCode != E_OK) { + LOGE("Failed to bind the value"); + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } else { + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteLocalStorageExecutor::Delete(const Key &key) +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, DELETE_SQL, statement); + if (errCode != E_OK) { + LOGE("Failed to get the delete statememt."); + return CheckCorruptedStatus(errCode); + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_KEY_INDEX, key, false); + if (errCode != E_OK) { + LOGE("Bind key failed"); + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGE("Delete step error:%d", errCode); + } else { + if (sqlite3_changes(dbHandle_) > 0) { + errCode = E_OK; + } else { + errCode = -E_NOT_FOUND; + } + } +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.h b/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.h new file mode 100644 index 0000000000000000000000000000000000000000..a1e6b5d294732e49fcd0b5e48070a5fa910a028e --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_LOCAL_DB_HANDLE_H +#define SQLITE_LOCAL_DB_HANDLE_H + +#include "sqlite_import.h" +#include "macro_utils.h" +#include "db_types.h" +#include "sqlite_storage_executor.h" + +namespace DistributedDB { +class SQLiteLocalStorageExecutor : public SQLiteStorageExecutor { +public: + SQLiteLocalStorageExecutor(sqlite3 *dbHandle, bool writable, bool isMemDb); + ~SQLiteLocalStorageExecutor() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteLocalStorageExecutor); + + int Get(const Key &key, Value &value) const; + + // Put the value to the sqlite database + int Put(const Key &key, const Value &value); + + // Delete the value from the sqlite database + int Delete(const Key &key); + + // Clear all the data from the sqlite database + int Clear(); + + // Get all the data which have the prefix key from the sqlite database + int GetEntries(const Key &keyPrefix, std::vector &entries) const; + + // Put the batch data to the sqlite database + int PutBatch(const std::vector &entries); + + // Delete the batch data from the sqlite database according to the key from the set + int DeleteBatch(const std::vector &keys); + + // Next step interface + // Start the transaction + int StartTransaction(); + + // Commit the transaction + int Commit(); + + // Roll back the transaction + int RollBack(); + +private: + // Put the value to the sqlite database, used by Put &PutBach + int PutInner(const Key &key, const Value &value); + + // Delete the value from the sqlite database, used by Delete &DeleteBach + int DeleteInner(const Key &key); + + // Start the transaction + int StartTransactionInner(bool &isAuto); + + // Commit the transaction + int CommitInner(); + + // Roll back the transaction + int RollBackInner(); +}; +} // namespace DistributedDB + +#endif // SQLITE_DB_HANDLE_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6255829e3fa8dc6d3f6e28a47ab6eea3692e3d07 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "sqlite_multi_ver_data_storage.h" + +#include +#include +#include +#include + +#include "db_constant.h" +#include "db_types.h" +#include "log_print.h" +#include "sqlite_utils.h" +#include "multi_ver_kv_entry.h" +#include "multi_ver_value_object.h" +#include "value_hash_calc.h" +#include "db_common.h" +#include "multi_ver_natural_store.h" +#include "platform_specific.h" + +namespace DistributedDB { +namespace { + const std::string CREATE_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS version_data(key BLOB, value BLOB, oper_flag INTEGER, version INTEGER, " \ + "timestamp INTEGER, ori_timestamp INTEGER, hash_key BLOB, " \ + "PRIMARY key(hash_key, version));" \ + "CREATE INDEX IF NOT EXISTS version_index ON version_data (version);" \ + "CREATE INDEX IF NOT EXISTS flag_index ON version_data (oper_flag);"; + + const std::size_t MAX_READ_CONNECT_NUM = 16; +} + +SQLiteMultiVerDataStorage::SQLiteMultiVerDataStorage() + : writeTransaction_(nullptr), + writeTransactionUsed_(false) +{} + +SQLiteMultiVerDataStorage::~SQLiteMultiVerDataStorage() +{ + writeTransaction_ = nullptr; +} + +int SQLiteMultiVerDataStorage::CheckVersion(const Property &property, bool &isDbExist) const +{ + int dbVer = 0; + int errCode = GetVersion(property, dbVer, isDbExist); + if (errCode != E_OK) { + LOGE("[DataStorage][CheckVer] GetVersion failed, errCode=%d.", errCode); + return errCode; + } + if (!isDbExist) { + return E_OK; + } + LOGD("[DataStorage][CheckVer] DbVersion=%d, CurVersion=%d.", dbVer, MULTI_VER_DATA_STORAGE_VERSION_CURRENT); + if (dbVer > MULTI_VER_DATA_STORAGE_VERSION_CURRENT) { + LOGE("[DataStorage][CheckVer] Version Not Support!"); + return -E_VERSION_NOT_SUPPORT; + } + return E_OK; +} + +int SQLiteMultiVerDataStorage::GetVersion(const Property &property, int &version, bool &isDbExisted) const +{ + std::string uri = property.path + "/" + property.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/" + + DBConstant::MULTI_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + isDbExisted = OS::CheckPathExistence(uri); + if (isDbExisted) { + std::vector tableVect; + OpenDbProperties option = {uri, property.isNeedCreate, false, tableVect, property.cipherType, property.passwd}; + return SQLiteUtils::GetVersion(option, version); + } + return E_OK; +} + +int SQLiteMultiVerDataStorage::Open(const Property &property) +{ + // only set the property para or create the database and the table? + // whether create the transactions. + property_ = property; + uri_ = property.path + "/" + property_.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/" + + DBConstant::MULTI_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + std::vector tableVect; + tableVect.push_back(CREATE_TABLE_SQL); + + OpenDbProperties option = {uri_, property.isNeedCreate, false, tableVect, property.cipherType, property.passwd}; + sqlite3 *db = nullptr; + int errCode = SQLiteUtils::OpenDatabase(option, db); + if (errCode != E_OK) { + LOGE("Open the multi ver data store error:%d", errCode); + goto END; + } + + // Version had been check before open and currently no upgrade to do + errCode = SQLiteUtils::SetUserVer(option, MULTI_VER_DATA_STORAGE_VERSION_CURRENT); + if (errCode != E_OK) { + LOGE("Init the version multi ver store error:%d", errCode); + } + +END: + if (db != nullptr) { + (void)sqlite3_close_v2(db); + db = nullptr; + } + + return errCode; +} + +// start one write transaction +// do the transaction initialization and call the start transaction; +int SQLiteMultiVerDataStorage::StartWrite(KvDataType dataType, IKvDBMultiVerTransaction *&transaction) +{ + (void)dataType; + std::unique_lock lock(transactionMutex_); + // if same thread. return nullptr. + if (std::this_thread::get_id() == writeHolderId_) { + transaction = nullptr; + return -E_BUSY; + } + + if (writeTransaction_ != nullptr) { + writeCondition_.wait(lock, [&] { + return !writeTransactionUsed_; + }); + + writeTransactionUsed_ = true; + writeHolderId_ = std::this_thread::get_id(); + transaction = writeTransaction_; + return E_OK; + } + + transaction = new (std::nothrow) SQLiteMultiVerTransaction(); + if (transaction == nullptr) { + LOGE("Failed to create the SQLite write transaction"); + return -E_OUT_OF_MEMORY; + } + + // initialize the transaction. + int errCode = static_cast(transaction)->Initialize(uri_, false, + property_.cipherType, property_.passwd); + if (errCode != E_OK) { + LOGE("Init write transaction failed:%d", errCode); + delete transaction; + transaction = nullptr; + return errCode; + } + + writeTransaction_ = static_cast(transaction); + writeTransactionUsed_ = true; + writeHolderId_ = std::this_thread::get_id(); + return E_OK; +} + +// do the first step of commit record. +// commit the transaction, and avoid other operation reading the new data. +int SQLiteMultiVerDataStorage::CommitWritePhaseOne(IKvDBMultiVerTransaction *transaction, + const UpdateVerTimestamp &multiVerTimestamp) +{ + if (transaction == nullptr) { + LOGE("Invalid transaction!"); + return -E_INVALID_DB; + } + // Get versionInfo from transaction. + // Call the commit of the sqlite. + Version versionInfo = transaction->GetVersion(); + + if (multiVerTimestamp.isNeedUpdate) { + (void)transaction->UpdateTimestampByVersion(versionInfo, multiVerTimestamp.timestamp); + } + + int errCode = transaction->CommitTransaction(); + if (errCode != E_OK) { + auto sqliteTransaction = static_cast(transaction); + if (transaction != nullptr) { + (void)sqliteTransaction->Reset(property_.cipherType, property_.passwd); + } + LOGE("SQLite commit the transaction failed:%d", errCode); + } + return errCode; +} + +// when the commit history update failed, need delete the commit +int SQLiteMultiVerDataStorage::RollbackWritePhaseOne(IKvDBMultiVerTransaction *transaction, + const Version &versionInfo) +{ + if (transaction == nullptr) { + LOGE("Invalid transaction!"); + return -E_INVALID_DB; + } + + SQLiteMultiVerTransaction *sqliteTransaction = static_cast(transaction); + sqliteTransaction->StartTransaction(); + int errCode = sqliteTransaction->ClearEntriesByVersion(versionInfo); + if (errCode == E_OK) { + sqliteTransaction->CommitTransaction(); + } else { + sqliteTransaction->RollBackTransaction(); + } + + return errCode; +} + +// Rollback the write transaction. +int SQLiteMultiVerDataStorage::RollbackWrite(IKvDBMultiVerTransaction *transaction) +{ + if (transaction == nullptr) { + LOGE("Invalid transaction!"); + return -E_INVALID_DB; + } + // call the rollback of the sqlite. + int errCode = static_cast(transaction)->RollBackTransaction(); + if (errCode != E_OK) { + (void)static_cast(transaction)->Reset(property_.cipherType, property_.passwd); + LOGE("SQLite rollback failed:%d", errCode); + } + return errCode; +} + +// should update the flag indicated that other operating could read the new record. +void SQLiteMultiVerDataStorage::CommitWritePhaseTwo(IKvDBMultiVerTransaction *transaction) +{ + // just change the head version? + (void)transaction; +} + +// Get one start transaction. +IKvDBMultiVerTransaction *SQLiteMultiVerDataStorage::StartRead(KvDataType dataType, + const Version &versionInfo, int &errCode) +{ + (void)dataType; + std::unique_lock lock(transactionMutex_); + for (auto &iter : readTransactions_) { + if (iter.second) { + iter.second = false; + (iter.first)->SetVersion(versionInfo); + errCode = E_OK; + return iter.first; + } + } + // need wait. + if (readTransactions_.size() > MAX_READ_CONNECT_NUM) { + LOGE("Over the max transaction num"); + errCode = -E_BUSY; + return nullptr; + } + + IKvDBMultiVerTransaction *transaction = new (std::nothrow) SQLiteMultiVerTransaction; + if (transaction == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + errCode = static_cast(transaction)->Initialize(uri_, + true, property_.cipherType, property_.passwd); + if (errCode != E_OK) { + delete transaction; + transaction = nullptr; + return nullptr; + } + + transaction->SetVersion(versionInfo); + readTransactions_.insert(std::make_pair(transaction, false)); + return transaction; +} + +// Release the transaction created. +void SQLiteMultiVerDataStorage::ReleaseTransaction(IKvDBMultiVerTransaction *transaction) +{ + // whether need manage the transaction. + std::unique_lock lock(transactionMutex_); + if (transaction == nullptr) { + LOGE("Invalid transaction!"); + return; + } + + if (transaction == writeTransaction_) { + static_cast(writeTransaction_)->ResetVersion(); + writeTransactionUsed_ = false; + writeHolderId_ = std::thread::id(); + writeCondition_.notify_all(); + return; + } + + auto iter = readTransactions_.find(transaction); + if (iter != readTransactions_.end()) { + static_cast(iter->first)->ResetVersion(); + iter->second = true; + } + return; +} + +void SQLiteMultiVerDataStorage::Close() +{ + std::lock_guard lock(transactionMutex_); + // close all the transaction? + for (auto iter = readTransactions_.begin(); iter != readTransactions_.end(); iter++) { + if (iter->first != nullptr) { + delete iter->first; + } + } + readTransactions_.clear(); + + if (writeTransaction_ != nullptr) { + delete writeTransaction_; + writeTransaction_ = nullptr; + } +} + +int SQLiteMultiVerDataStorage::RunRekeyLogic(CipherType type, const CipherPassword &passwd) +{ + (void)type; + // openDatabase to get the sqlite3 pointer + std::vector tableVect; + tableVect.push_back(CREATE_TABLE_SQL); + sqlite3 *db = nullptr; + OpenDbProperties option = {uri_, property_.isNeedCreate, false, tableVect, property_.cipherType, property_.passwd}; + int errCode = SQLiteUtils::OpenDatabase(option, db); + if (errCode != E_OK) { + LOGE("Open db error:%d", errCode); + return errCode; + } + + // execute rekey + errCode = SQLiteUtils::Rekey(db, passwd); + if (errCode != E_OK) { + LOGE("multi ver data rekey failed:%d", errCode); + } + // close db + (void)sqlite3_close_v2(db); + db = nullptr; + + return errCode; +} + +int SQLiteMultiVerDataStorage::RunExportLogic(CipherType type, const CipherPassword &passwd, const std::string &dbDir) +{ + // openDatabase to get the sqlite3 pointer + std::vector tableVect; + sqlite3 *db = nullptr; + OpenDbProperties option = {uri_, true, false, tableVect, property_.cipherType, property_.passwd}; + int errCode = SQLiteUtils::OpenDatabase(option, db); + if (errCode != E_OK) { + LOGE("Open db error:%d", errCode); + return errCode; + } + + // execute export + std::string newDbName = dbDir + "/" + DBConstant::MULTI_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + errCode = SQLiteUtils::ExportDatabase(db, type, passwd, newDbName); + if (errCode != E_OK) { + LOGE("multi ver data export failed:%d", errCode); + } + // close db + (void)sqlite3_close_v2(db); + db = nullptr; + + return errCode; +} + +int SQLiteMultiVerDataStorage::BackupCurrentDatabase(const Property &property, const std::string &dir) +{ + std::string currentDb = property.path + "/" + property.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/" + + DBConstant::MULTI_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + std::string dstDb = dir + "/" + DBConstant::MULTI_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + int errCode = DBCommon::CopyFile(currentDb, dstDb); + if (errCode != E_OK) { + LOGE("Copy the local current db error:%d", errCode); + } + return errCode; +} + +int SQLiteMultiVerDataStorage::ImportDatabase(const Property &property, const std::string &dir, + const CipherPassword &passwd) +{ + std::string currentDb = property.path + "/" + property.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/" + + DBConstant::MULTI_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + std::string srcDb = dir + "/" + DBConstant::MULTI_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + int errCode = SQLiteUtils::ExportDatabase(srcDb, property.cipherType, passwd, currentDb, property.passwd); + if (errCode != E_OK) { + LOGE("import the multi ver data db error:%d", errCode); + } + return E_OK; +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.h b/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.h new file mode 100644 index 0000000000000000000000000000000000000000..d924196c64d8e408808666d0361845565376acfb --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_KV_DB_MULTI_VER_DATA_STORAGE_H +#define SQLITE_KV_DB_MULTI_VER_DATA_STORAGE_H + +#ifndef OMIT_MULTI_VER +#include +#include +#include +#include +#include +#include + +#include "db_types.h" +#include "kvdb_properties.h" +#include "ikvdb_multi_ver_data_storage.h" +#include "macro_utils.h" +#include "multi_ver_value_object.h" +#include "generic_multi_ver_kv_entry.h" +#include "sqlite_multi_ver_transaction.h" + +namespace DistributedDB { +class SQLiteMultiVerDataStorage : public IKvDBMultiVerDataStorage { +public: + SQLiteMultiVerDataStorage(); + ~SQLiteMultiVerDataStorage() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteMultiVerDataStorage); + + int Open(const Property &property) override; + + int StartWrite(KvDataType dataType, IKvDBMultiVerTransaction *&transaction) override; + + int CommitWritePhaseOne(IKvDBMultiVerTransaction *transaction, + const UpdateVerTimestamp &multiVerTimestamp) override; + + int RollbackWritePhaseOne(IKvDBMultiVerTransaction *transaction, const Version &versionInfo) override; + + int RollbackWrite(IKvDBMultiVerTransaction *transaction) override; + + void CommitWritePhaseTwo(IKvDBMultiVerTransaction *transaction) override; + + IKvDBMultiVerTransaction *StartRead(KvDataType dataType, const Version &versionInfo, int &errCode) override; + + void ReleaseTransaction(IKvDBMultiVerTransaction *transaction) override; + + void Close() override; + + int RunRekeyLogic(CipherType type, const CipherPassword &passwd); + + int RunExportLogic(CipherType type, const CipherPassword &passwd, const std::string &dbDir); + + int CheckVersion(const Property &property, bool &isDbExist) const override; + + int GetVersion(const Property &property, int &version, bool &isDbExisted) const override; + + int BackupCurrentDatabase(const Property &property, const std::string &dir) override; + + int ImportDatabase(const Property &property, const std::string &dir, const CipherPassword &passwd) override; + +private: + Property property_; + std::string uri_; + std::map readTransactions_; + SQLiteMultiVerTransaction *writeTransaction_; + bool writeTransactionUsed_; + std::mutex transactionMutex_; + std::condition_variable readCondition_; + std::condition_variable writeCondition_; + std::thread::id writeHolderId_; +}; +} // namespace DistributedDB + +#endif // SQLITE_KV_DB_MULTI_VER_DATA_STORAGE_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..37bcfe7da8a03092bfb7592181619e9751769dfb --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.cpp @@ -0,0 +1,1524 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "sqlite_multi_ver_transaction.h" + +#include +#include +#include +#include + +#include "securec.h" + +#include "db_common.h" +#include "db_constant.h" +#include "db_types.h" +#include "log_print.h" +#include "sqlite_utils.h" +#include "multi_ver_kv_entry.h" +#include "multi_ver_value_object.h" +#include "value_hash_calc.h" +#include "time_helper.h" + +namespace DistributedDB { +const std::string SQLiteMultiVerTransaction::CREATE_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS version_data(key BLOB, value BLOB, oper_flag INTEGER, version INTEGER, " \ + "timestamp INTEGER, ori_timestamp INTEGER, hash_key BLOB, " \ + "PRIMARY key(hash_key, version));" \ + "CREATE INDEX IF NOT EXISTS version_index ON version_data (version);" \ + "CREATE INDEX IF NOT EXISTS flag_index ON version_data (oper_flag);"; + +const std::string SQLiteMultiVerTransaction::SELECT_ONE_SQL = + "SELECT oper_flag, key, value FROM version_data WHERE hash_key=? AND (timestamp>? OR (timestamp=? AND rowid>=?)) " \ + "AND version<=? AND (oper_flag&0x08=0x08) ORDER BY version DESC LIMIT 1;"; +const std::string SQLiteMultiVerTransaction::SELECT_BY_HASHKEY_VER_SQL = + "SELECT oper_flag, value FROM version_data WHERE hash_key=? AND version=? "; +const std::string SQLiteMultiVerTransaction::SELECT_BATCH_SQL = + "SELECT oper_flag, key, value, version FROM version_data WHERE key>=? AND key<=?" \ + "AND (timestamp>? OR (timestamp=? AND rowid>=?)) AND version<=? AND (oper_flag&0x08=0x08) " \ + "ORDER BY key ASC, version DESC;"; +// select the data whose hash key is same to the current data. +const std::string SQLiteMultiVerTransaction::SELECT_HASH_ENTRY_SQL = + "SELECT oper_flag FROM version_data WHERE hash_key=? AND version>? AND version<=? AND (oper_flag&0x08=0x08) " \ + "ORDER BY version DESC LIMIT 1;"; +const std::string SQLiteMultiVerTransaction::SELECT_ONE_VER_RAW_SQL = + "SELECT key, value, oper_flag, timestamp, ori_timestamp, hash_key FROM version_data " \ + "WHERE version=? ORDER BY rowid ASC;"; +const std::string SQLiteMultiVerTransaction::INSERT_SQL = + "INSERT OR REPLACE INTO version_data VALUES(?,?,?,?,?,?,?);"; +const std::string SQLiteMultiVerTransaction::DELETE_VER_SQL = + "DELETE FROM version_data WHERE version=?;"; +const std::string SQLiteMultiVerTransaction::DELETE_BY_VER_HASHKEY_SQL = + "DELETE FROM version_data WHERE version=? and hash_key=?;"; +const std::string SQLiteMultiVerTransaction::SELECT_PRE_PUT_VER_DATA_SQL = + "SELECT value FROM version_data WHERE version=? AND timestamp<=?;"; +const std::string SQLiteMultiVerTransaction::DELETE_PRE_PUT_VER_DATA_SQL = + "DELETE FROM version_data WHERE version=? AND timestamp<=?;"; + +const std::string SQLiteMultiVerTransaction::SELECT_ONE_BY_KEY_TIMESTAMP_SQL = + "SELECT timestamp, ori_timestamp, version, value FROM version_data WHERE hash_key=? AND (oper_flag&0x08=0x08) " \ + "ORDER BY version DESC LIMIT 1;"; + +const std::string SQLiteMultiVerTransaction::SELECT_LATEST_CLEAR_ID = + "SELECT rowid, timestamp FROM version_data WHERE (oper_flag&0x07=0x03) AND (oper_flag&0x08=0x08) " \ + "ORDER BY rowid DESC LIMIT 1;"; // clear flag and the local flag. + +const std::string SQLiteMultiVerTransaction::SELECT_MAX_LOCAL_VERSION = + "SELECT MAX(version) FROM version_data WHERE (oper_flag&0x08=0x08);"; + +const std::string SQLiteMultiVerTransaction::SELECT_MAX_VERSION = + "SELECT MAX(version) FROM version_data;"; + +const std::string SQLiteMultiVerTransaction::SELECT_MAX_TIMESTAMP = + "SELECT MAX(timestamp) FROM version_data;"; + +const std::string SQLiteMultiVerTransaction::UPDATE_VERSION_TIMESTAMP = + "UPDATE version_data SET timestamp=? WHERE version=?;"; + +const std::string SQLiteMultiVerTransaction::SELECT_OVERWRITTEN_CLEAR_TYPE = + "SELECT hash_key, oper_flag, version FROM version_data WHERE version tableVect; + tableVect.push_back(CREATE_TABLE_SQL); + OpenDbProperties option = {uri, true, false, tableVect, type, passwd}; + int errCode = SQLiteUtils::OpenDatabase(option, db_); + if (errCode != E_OK) { + LOGE("Init db error:%d", errCode); + return errCode; + } + + uri_ = uri; + isReadOnly_ = isReadOnly; + return E_OK; +} + +void SQLiteMultiVerTransaction::SetVersion(const Version &versionInfo) +{ + version_ = versionInfo; +} + +int SQLiteMultiVerTransaction::Put(const Key &key, const Value &value) +{ + // for only read transaction, not support for writing. + if (isReadOnly_) { + return -E_NOT_SUPPORT; + } + + uint64_t flag = ADD_FLAG | LOCAL_FLAG; + MultiVerEntryAuxData data = {flag, NO_TIMESTAMP, NO_TIMESTAMP}; + return AddRecord(key, value, data); +} + +int SQLiteMultiVerTransaction::Delete(const Key &key) +{ + if (key.empty() || key.size() > DBConstant::MAX_VALUE_SIZE) { + return -E_INVALID_ARGS; + } + Value valueRead; + int errCode = Get(key, valueRead); + if (errCode != E_OK) { + return errCode; + } + + valueRead.clear(); + MultiVerValueObject valueObject; + errCode = valueObject.SetValue(valueRead); + if (errCode != E_OK) { + return errCode; + } + + Value value; + errCode = valueObject.GetSerialData(value); + if (errCode != E_OK) { + return errCode; + } + + Key hashKey; + errCode = DBCommon::CalcValueHash(key, hashKey); + if (errCode != E_OK) { + return errCode; + } + + MultiVerEntryAuxData data = {DEL_FLAG | LOCAL_FLAG, NO_TIMESTAMP, NO_TIMESTAMP}; + return AddRecord(hashKey, value, data); +} + +int SQLiteMultiVerTransaction::Clear() +{ + if (isReadOnly_) { + return -E_NOT_SUPPORT; + } + + Key key = {'c', 'l', 'e', 'a', 'r'}; + Value emptyValue; + MultiVerValueObject valueObject; + int errCode = valueObject.SetValue(emptyValue); + if (errCode != E_OK) { + return errCode; + } + + Value value; + errCode = valueObject.GetSerialData(value); + if (errCode != E_OK) { + return errCode; + } + + MultiVerEntryAuxData data = {LOCAL_FLAG | CLEAR_FLAG, NO_TIMESTAMP, NO_TIMESTAMP}; + errCode = AddRecord(key, value, data); + + clearId_ = 0; + GetClearId(); + return errCode; +} + +int SQLiteMultiVerTransaction::Get(const Key &key, Value &value) const +{ + sqlite3_stmt *statement = nullptr; + std::lock_guard lock(readMutex_); + int errCode = SQLiteUtils::GetStatement(db_, SELECT_ONE_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + + GetClearId(); // query the clear rowid, and only concern the later entry. + Key readKey; + Key hashKey; + errCode = DBCommon::CalcValueHash(key, hashKey); + if (errCode != E_OK) { + goto END; + } + errCode = GetKeyAndValueByHashKey(statement, hashKey, readKey, value, false); +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::GetValueForTrimSlice(const Key &hashKey, const Version version, Value &value) const +{ + sqlite3_stmt *statement = nullptr; + std::lock_guard lock(readMutex_); + int errCode = SQLiteUtils::GetStatement(db_, SELECT_BY_HASHKEY_VER_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + + uint64_t operFlag; + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, hashKey, false); // bind the 1st para for hash key + if (errCode != E_OK) { + goto END; + } + + errCode = sqlite3_bind_int64(statement, 2, version); // bind the 2nd para for version + if (errCode != SQLITE_OK) { + LOGE("Bind the clear id for query error:%d", errCode); + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = -E_NOT_FOUND; + goto END; + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + goto END; + } + + errCode = E_OK; + operFlag = static_cast(sqlite3_column_int64(statement, 0)); + // only the added data should be operated. + if ((OPERATE_MASK & operFlag) == ADD_FLAG) { + errCode = SQLiteUtils::GetColumnBlobValue(statement, 1, value); // Get the value. + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::GetEntries(const Key &keyPrefix, std::vector &entries) const +{ + GetEntriesStatements statements; + std::lock_guard lock(readMutex_); + int errCode = PrepareForGetEntries(keyPrefix, statements); + if (errCode != E_OK) { + return errCode; + } + + Entry entry; + Key lastKey; + int stepResult; + int innerCode; + + entries.clear(); + entries.shrink_to_fit(); + do { + errCode = SQLiteUtils::StepWithRetry(statements.getEntriesStatement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + stepResult = GetOneEntry(statements, lastKey, entry, errCode); + SQLiteUtils::ResetStatement(statements.hashFilterStatement, false, errCode); + if (stepResult == STEP_SUCCESS) { + lastKey = entry.key; + entries.push_back(std::move(entry)); + } else if (stepResult == STEP_NEXTKEY) { // this key would be dispatched + lastKey = entry.key; + } else if (stepResult == STEP_CONTINUE) { + continue; + } else { + goto END; + } + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + break; + } else { + LOGE("SQLite step failed:%d", errCode); + goto END; + } + } while (true); + + // if select no result, return -E_NOT_FOUND. + if (entries.empty()) { + errCode = -E_NOT_FOUND; + } else { + errCode = E_OK; + } +END: + innerCode = ReleaseGetEntriesStatements(statements); + if (innerCode != E_OK) { + errCode = innerCode; + } + return errCode; +} + +int SQLiteMultiVerTransaction::CheckToSaveRecord(const MultiVerKvEntry *entry, bool &isNeedSave, + std::vector &values) +{ + Value disVal; + int errCode = CheckIfNeedSaveRecord(entry, isNeedSave, disVal); + if (errCode != E_OK) { + return errCode; + } + + if (!isNeedSave) { + static_cast(entry)->GetValue(disVal); + return E_OK; + } + // Should erase the data inserted before the clear operation. + uint64_t operFlag = 0; + uint64_t timestamp = 0; + (static_cast(entry))->GetOperFlag(operFlag); + entry->GetTimestamp(timestamp); + if ((operFlag & OPERATE_MASK) == CLEAR_FLAG && version_ != 0) { + LOGD("Erase one version:%" PRIu64, version_); + errCode = GetPrePutValues(version_, timestamp, values); + if (errCode != E_OK) { + return errCode; + } + errCode = RemovePrePutEntries(version_, timestamp); + if (errCode != E_OK) { + LOGE("Delete version data before clear oper failed:%d", errCode); + return errCode; + } + clearId_ = 0; // Clear the clear id. + } + + return E_OK; +} + +int SQLiteMultiVerTransaction::PutBatch(const std::vector &entries) +{ + for (auto iter = entries.begin(); iter != entries.end(); iter++) { + int errCode = Put(iter->key, iter->value); + if (errCode != E_OK) { + LOGE("put failed:%d!", errCode); + return errCode; + } + } + return E_OK; +} + +int SQLiteMultiVerTransaction::PutBatch(const std::vector &entries, bool isLocal, + std::vector &values) +{ + for (const auto &item : entries) { + if (item == nullptr) { + continue; + } + + auto entry = static_cast(item); + MultiVerEntryAuxData data; + entry->GetOperFlag(data.operFlag); + entry->GetTimestamp(data.timestamp); + entry->GetOriTimestamp(data.oriTimestamp); + data.operFlag &= OPERATE_MASK; + + // isLocal means that the entries need merge. + if (isLocal) { + data.operFlag |= LOCAL_FLAG; // set to local + } + + bool isNeedSave = false; + int errCode = CheckToSaveRecord(item, isNeedSave, values); + if (errCode != E_OK) { + return errCode; + } + // already add to the values. + if (!isNeedSave) { + continue; + } + + Key key; + Value value; + (void)entry->GetKey(key); + errCode = entry->GetValue(value); + if (errCode != E_OK) { + return errCode; + } + + values.push_back(value); + errCode = AddRecord(key, value, data); + if (errCode != E_OK) { + LOGE("Put batch data failed:%d", errCode); + return errCode; + } + } + return E_OK; +} + +int SQLiteMultiVerTransaction::GetDiffEntries(const Version &begin, const Version &end, MultiVerDiffData &data) const +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, SELECT_ONE_VER_RAW_SQL, statement); + if (errCode != E_OK) { + LOGE("Fail to get the version raw data statement:%d", errCode); + return errCode; + } + + Value value; + std::vector savedEntries; + errCode = GetRawDataByVersion(statement, end, savedEntries); // Get all the data of the end version. + if (errCode != E_OK) { + LOGE("Get raw data for diff version failed:%d", errCode); + goto ERROR; + } + + for (auto &item : savedEntries) { + if ((item.auxData.operFlag & OPERATE_MASK) == CLEAR_FLAG) { + data.Reset(); + data.isCleared = true; + continue; + } + value.clear(); + if (begin == 0) { // no begin version, means no value + errCode = -E_NOT_FOUND; + } else { + // Need get the origin key of the deleted data. + if ((item.auxData.operFlag & OPERATE_MASK) == ADD_FLAG) { + errCode = Get(item.key, value); + } else { + errCode = GetOriginKeyValueByHash(item, value); + } + } + + if (errCode == E_OK || errCode == -E_NOT_FOUND) { + ClassifyDiffEntries(errCode, (item.auxData.operFlag & OPERATE_MASK), value, item, data); + errCode = E_OK; + } else { + break; + } + } + +ERROR: + if (errCode != E_OK) { + data.Reset(); + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::GetMaxVersion(MultiVerDataType type, Version &maxVersion) const +{ + std::string sql = SELECT_MAX_VERSION; + if (type == MultiVerDataType::NATIVE_TYPE) { + sql = SELECT_MAX_LOCAL_VERSION; + } + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, sql, statement); + if (errCode != E_OK) { + return errCode; + } + + // Step for getting the latest version + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGI("Initial the new max local version"); + maxVersion = 0; + errCode = E_OK; + } else { + LOGE("Execute max version failed:%d", errCode); + } + } else { + maxVersion = static_cast(sqlite3_column_int64(statement, 0)); // only select the first result. + errCode = E_OK; + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::ClearEntriesByVersion(const Version &versionInfo) +{ + // consider to get the statement. + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, DELETE_VER_SQL, statement); + if (errCode != E_OK) { + LOGE("Get delete version statement error:%d", errCode); + return errCode; + } + + // bind the version info. + errCode = sqlite3_bind_int64(statement, 1, versionInfo); // bind the first argument; + if (errCode != SQLITE_OK) { + LOGE("bind the delete version statement error:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto END; + } + + // Step for getting the latest version + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGE("Delete records error:%d", errCode); + } else { + errCode = E_OK; + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::GetPrePutValues(const Version &versionInfo, Timestamp timestamp, + std::vector &values) const +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, SELECT_PRE_PUT_VER_DATA_SQL, statement); + if (errCode != E_OK) { + LOGE("get delete version statement for clear error:%d", errCode); + return errCode; + } + + // bind the versioninfo + errCode = sqlite3_bind_int64(statement, 1, versionInfo); // bind the first argument; + if (errCode != SQLITE_OK) { + LOGE("bind the delete version statement for clear error:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto ERROR; + } + + // bind the clear timestamp + errCode = sqlite3_bind_int64(statement, 2, timestamp); // bind the second argument for timestamp; + if (errCode != SQLITE_OK) { + LOGE("bind the clear timestamp for delete ver data error:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto ERROR; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + Value value; + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, value); // get the 1st for value. + if (errCode != E_OK) { + goto ERROR; + } + values.push_back(std::move(value)); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + goto ERROR; + } else { + goto ERROR; + } + } while (true); + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::RemovePrePutEntries(const Version &versionInfo, Timestamp timestamp) +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, DELETE_PRE_PUT_VER_DATA_SQL, statement); + if (errCode != E_OK) { + LOGE("get delete version statement for clear error:%d", errCode); + return errCode; + } + + // bind the versioninfo + errCode = sqlite3_bind_int64(statement, 1, versionInfo); // bind the first argument; + if (errCode != SQLITE_OK) { + LOGE("bind the delete version statement for clear error:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto ERROR; + } + + // bind the clear timestamp + errCode = sqlite3_bind_int64(statement, 2, timestamp); // bind the second argument for timestamp; + if (errCode != SQLITE_OK) { + LOGE("bind the clear timestamp for delete ver data error:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto ERROR; + } + + // Step for getting the latest version + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGE("Delete records for clear error:%d", errCode); + } else { + errCode = E_OK; + } + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::StartTransaction() +{ + return SQLiteUtils::BeginTransaction(db_); +} + +int SQLiteMultiVerTransaction::RollBackTransaction() +{ + return SQLiteUtils::RollbackTransaction(db_); +} + +int SQLiteMultiVerTransaction::CommitTransaction() +{ + return SQLiteUtils::CommitTransaction(db_); +} + +int SQLiteMultiVerTransaction::GetEntriesByVersion(Version version, std::list &data) const +{ + std::lock_guard lock(readMutex_); + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, SELECT_ONE_VER_RAW_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + + std::vector savedEntries; + errCode = GetRawDataByVersion(statement, version, savedEntries); + if (errCode != E_OK) { + LOGE("get raw data failed:%d", errCode); + goto ERROR; + } + + for (auto &item : savedEntries) { + MultiVerTrimedVersionData versionData; + versionData.operFlag = item.auxData.operFlag; + if ((versionData.operFlag & OPERATE_MASK) == ADD_FLAG) { + (void)DBCommon::CalcValueHash(item.key, versionData.key); + } else { + versionData.key = item.key; + } + versionData.version = version; + data.push_front(versionData); + } + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::GetEntriesByVersion(const Version &versionInfo, + std::vector &entries) const +{ + std::lock_guard lock(readMutex_); + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, SELECT_ONE_VER_RAW_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + + std::vector savedEntries; + errCode = GetRawDataByVersion(statement, versionInfo, savedEntries); + if (errCode != E_OK) { + LOGE("get raw data failed:%d", errCode); + goto ERROR; + } + + for (auto &item : savedEntries) { + GenericMultiVerKvEntry *entry = new (std::nothrow) GenericMultiVerKvEntry; + if (entry == nullptr) { + errCode = -E_OUT_OF_MEMORY; + break; + } + entry->SetOperFlag(item.auxData.operFlag); + entry->SetKey(item.key); + entry->SetValue(item.value); + entry->SetTimestamp(item.auxData.timestamp); + entry->SetOriTimestamp(item.auxData.oriTimestamp); + entries.push_back(entry); + } + +ERROR: + if (errCode != E_OK) { + for (auto &entry : entries) { + delete entry; + entry = nullptr; + } + + entries.clear(); + entries.shrink_to_fit(); + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +Timestamp SQLiteMultiVerTransaction::GetCurrentMaxTimestamp() const +{ + // consider to get the statement. + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, SELECT_MAX_TIMESTAMP, statement); + if (errCode != E_OK) { + LOGE("Get current max timestamp statement error:%d", errCode); + return 0; + } + Timestamp timestamp = 0; + // Step for getting the latest version + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGI("Initial the current max timestamp"); + } + } else { + timestamp = static_cast(sqlite3_column_int64(statement, 0)); // the first result. + } + SQLiteUtils::ResetStatement(statement, true, errCode); + return timestamp; +} + +int SQLiteMultiVerTransaction::UpdateTimestampByVersion(const Version &version, + Timestamp stamp) const +{ + if (isReadOnly_) { + return -E_NOT_SUPPORT; + } + + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, UPDATE_VERSION_TIMESTAMP, statement); + if (errCode != E_OK) { + LOGE("Get update timestamp statement error:%d", errCode); + return errCode; + } + + // bind the timestamp + errCode = sqlite3_bind_int64(statement, 1, static_cast(stamp)); // bind the 1st for timestamp; + if (errCode != SQLITE_OK) { + LOGE("bind the updated timestamp error:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto END; + } + + // bind the versioninfo + errCode = sqlite3_bind_int64(statement, 2, static_cast(version)); // bind the 2nd for version; + if (errCode != SQLITE_OK) { + LOGE("bind the updated version error:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto END; + } + + // Step for getting the latest version + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + currentMaxTimestamp_ = (stamp > currentMaxTimestamp_) ? stamp : currentMaxTimestamp_; + LOGD("Update the timestamp of version:%" PRIu64 " - %" PRIu64, version, stamp); + } else { + LOGE("Failed to update the timestamp of the version:%d", errCode); + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +bool SQLiteMultiVerTransaction::IsDataChanged() const +{ + if (isReadOnly_) { + return false; + } + + return isDataChanged_; +} + +void SQLiteMultiVerTransaction::ResetVersion() +{ + if (db_ != nullptr) { + sqlite3_db_release_memory(db_); + } + + version_ = 0; + clearId_ = 0; + isDataChanged_ = false; +} + +int SQLiteMultiVerTransaction::Reset(CipherType type, const CipherPassword &passwd) +{ + std::lock_guard lock(resetMutex_); + std::vector tableVect = {CREATE_TABLE_SQL}; + OpenDbProperties option = {uri_, true, false, tableVect, type, passwd}; + sqlite3 *newConnection = nullptr; + int errCode = SQLiteUtils::OpenDatabase(option, newConnection); + if (errCode != E_OK) { + LOGE("Reset the transaction error:%d", errCode); + return errCode; + } + if (db_ != nullptr) { + (void)sqlite3_close_v2(db_); + } + db_ = newConnection; + return E_OK; +} + +Version SQLiteMultiVerTransaction::GetVersion() const +{ + return version_; +} + +int SQLiteMultiVerTransaction::GetOverwrittenClearTypeEntries(Version clearVersion, + std::list &data) const +{ + sqlite3_stmt *statement = nullptr; + std::lock_guard lock(readMutex_); + int errCode = SQLiteUtils::GetStatement(db_, SELECT_OVERWRITTEN_CLEAR_TYPE, statement); + if (errCode != E_OK) { + return errCode; + } + + errCode = sqlite3_bind_int64(statement, 1, clearVersion); // bind the 1st for the clear version + if (errCode != SQLITE_OK) { + LOGE("Bind the clear id for query error:%d", errCode); + goto END; + } + + errCode = sqlite3_bind_int64(statement, 2, clearVersion); // bind the 2nd for the clear version to get timestamp + if (errCode != SQLITE_OK) { + LOGE("Bind the clear id for query error:%d", errCode); + goto END; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + uint64_t operFlag = static_cast(sqlite3_column_int64(statement, 1)); // get the 2nd for opr + + MultiVerTrimedVersionData trimedVerData; + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, trimedVerData.key); // get the 1st for key. + if (errCode != E_OK) { + goto END; + } + trimedVerData.operFlag = operFlag & OPERATE_MASK; + trimedVerData.version = static_cast(sqlite3_column_int64(statement, 2)); // get the 3rd for ver + data.push_front(trimedVerData); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + goto END; + } else { + goto END; + } + } while (true); +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::GetOverwrittenNonClearTypeEntries(Version version, const Key &hashKey, + std::list &data) const +{ + sqlite3_stmt *statement = nullptr; + std::lock_guard lock(readMutex_); + int errCode = SQLiteUtils::GetStatement(db_, SELECT_OVERWRITTEN_NO_CLEAR_TYPE, statement); + if (errCode != E_OK) { + return errCode; + } + + errCode = sqlite3_bind_int64(statement, 1, version); // bind the 1st for the version + if (errCode != SQLITE_OK) { + LOGE("Bind the clear id for query error:%d", errCode); + goto END; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 2, hashKey, false); // 2nd argument is hashKey + if (errCode != E_OK) { + goto END; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + uint64_t operFlag = static_cast(sqlite3_column_int64(statement, 1)); // 2nd for oper flag. + MultiVerTrimedVersionData trimedVerData; + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, trimedVerData.key); // column result is key. + if (errCode != E_OK) { + goto END; + } + + trimedVerData.operFlag = operFlag & OPERATE_MASK; // get the meta flag + trimedVerData.version = static_cast(sqlite3_column_int64(statement, 2)); // get the 3rd for ver + data.push_front(trimedVerData); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + goto END; + } else { + goto END; + } + } while (true); + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::DeleteEntriesByHashKey(Version version, const Key &hashKey) +{ + // consider to get the statement. + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, DELETE_BY_VER_HASHKEY_SQL, statement); + if (errCode != E_OK) { + LOGE("Get delete version statement error:%d", errCode); + return errCode; + } + + // bind the version info. + errCode = sqlite3_bind_int64(statement, 1, version); // bind the first argument; + if (errCode != SQLITE_OK) { + LOGE("bind the delete version statement error:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto END; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 2, hashKey, false); // 2nd argument is hashKey + if (errCode != E_OK) { + goto END; + } + + // Step for getting the latest version + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGE("Delete records error:%d", errCode); + } else { + errCode = E_OK; + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::GetRawMultiVerEntry(sqlite3_stmt *statement, MultiVerEntryData &keyEntry) +{ + int errCode = SQLiteUtils::GetColumnBlobValue(statement, 1, keyEntry.value); + if (errCode != E_OK) { + return errCode; + } + + uint64_t flag = static_cast(sqlite3_column_int64(statement, 2)); // oper flag index + keyEntry.auxData.operFlag = flag & OPERATE_MASK; // remove the local flag. + + keyEntry.auxData.timestamp = static_cast(sqlite3_column_int64(statement, 3)); // timestamp index + keyEntry.auxData.oriTimestamp = static_cast(sqlite3_column_int64(statement, 4)); // ori timestamp index + + // if the data is deleted data, just use the hash key. + if ((flag & OPERATE_MASK) != ADD_FLAG) { + errCode = SQLiteUtils::GetColumnBlobValue(statement, 5, keyEntry.key); // the hash key index. + if (errCode != E_OK) { + return errCode; + } + } else { + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, keyEntry.key); // the key index. + if (errCode != E_OK) { + return errCode; + } + } + if (keyEntry.key.empty()) { + return -E_INVALID_DATA; + } + return E_OK; +} + +int SQLiteMultiVerTransaction::GetRawDataByVersion(sqlite3_stmt *&statement, + const Version &version, std::vector &entries) +{ + // Bind the version + int errCode = sqlite3_bind_int64(statement, 1, static_cast(version)); // only one parameter. + if (errCode != SQLITE_OK) { + LOGE("Bind the ver for getting raw ver data error:%d", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + MultiVerEntryData entry; + errCode = GetRawMultiVerEntry(statement, entry); + if (errCode == E_OK) { + entries.push_back(std::move(entry)); + } else { + break; + } + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + // if select no result, return the E_OK. + errCode = E_OK; + break; + } else { + LOGE("SQLite step failed:%d", errCode); + break; + } + } while (true); + + SQLiteUtils::ResetStatement(statement, false, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::GetDiffOperator(int errCode, uint64_t flag) +{ + int oper = EntryOperator::FAIL; + if (errCode == -E_NOT_FOUND) { + if (flag == ADD_FLAG) { + oper = EntryOperator::INSERT; + } + } else if (errCode == E_OK) { + if (flag == DEL_FLAG) { + oper = EntryOperator::DELETE; + } else if (flag == ADD_FLAG) { + oper = EntryOperator::UPDATE; + } + } + + return oper; +} + +int SQLiteMultiVerTransaction::AddRecord(const Key &key, const Value &value, + const MultiVerEntryAuxData &data) +{ + if (isReadOnly_) { + return -E_NOT_SUPPORT; + } + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, INSERT_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + + // If the record has timestamp, it means the record is foreign. + MultiVerEntryAuxData dataCopy = data; + if (data.timestamp == NO_TIMESTAMP) { + if (currentMaxTimestamp_ == NO_TIMESTAMP) { + currentMaxTimestamp_ = std::max(GetCurrentMaxTimestamp(), currentMaxTimestamp_); + } + dataCopy.timestamp = currentMaxTimestamp_++; + if ((dataCopy.operFlag & LOCAL_FLAG) != 0) { + dataCopy.oriTimestamp = currentMaxTimestamp_; + LOGD("Origin timestamp:%" PRIu64, currentMaxTimestamp_); + } + } + + errCode = BindAddRecordArgs(statement, key, value, dataCopy); + if (errCode != E_OK) { + goto END; + } + + // Step for put the result. + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + currentMaxTimestamp_ = (dataCopy.timestamp > currentMaxTimestamp_) ? dataCopy.timestamp : currentMaxTimestamp_; + errCode = E_OK; + isDataChanged_ = true; + } else { + LOGE("SQLite step error: %d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +void SQLiteMultiVerTransaction::ClassifyDiffEntries(int errCode, uint64_t flag, + const Value &value, MultiVerEntryData &item, MultiVerDiffData &data) const +{ + int oper = GetDiffOperator(errCode, flag); + Entry entry; + entry.key.swap(item.key); + if (oper == EntryOperator::DELETE) { + if (value.empty()) { + MultiVerValueObject valueObject; + valueObject.SetValue(value); + Value newValue; + int returnCode = valueObject.GetSerialData(newValue); + if (returnCode != E_OK) { + entry.value.clear(); + } else { + entry.value.swap(newValue); + } + } else { + entry.value = value; + } + data.deleted.push_back(std::move(entry)); + } else if (oper == EntryOperator::INSERT) { + entry.value.swap(item.value); + data.inserted.push_back(std::move(entry)); + } else if (oper == EntryOperator::UPDATE) { + entry.value.swap(item.value); + data.updated.push_back(std::move(entry)); + } +} + +void SQLiteMultiVerTransaction::GetClearId() const +{ + if (clearId_ > 0) { // only changes at the begin or after clear operation. + return; + } + + // consider to get the statement. + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, SELECT_LATEST_CLEAR_ID, statement); + if (errCode != E_OK) { + LOGE("Get latest clear id error:%d", errCode); + clearId_ = 1; + clearTime_ = 0; + return; + } + + // Step for getting the latest version + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGI("Initial the new version for clear"); + } + clearId_ = 1; + clearTime_ = 0; + } else { + clearId_ = sqlite3_column_int64(statement, 0); // Get the max rowid from the 1st column. + clearTime_ = sqlite3_column_int64(statement, 1); // Get the max timestamp from the 2nd column. + } + SQLiteUtils::ResetStatement(statement, true, errCode); +} + +int SQLiteMultiVerTransaction::BindClearIdAndVersion(sqlite3_stmt *statement, int index) const +{ + int errCode = sqlite3_bind_int64(statement, index, clearTime_); // bind the 1st for the clear time + if (errCode != SQLITE_OK) { + LOGE("Bind the clear id for query error:%d", errCode); + goto END; + } + + // bind the next argument for the clear time in the same transact + errCode = sqlite3_bind_int64(statement, index + 1, clearTime_); + if (errCode != SQLITE_OK) { + LOGE("Bind the clear id for query error:%d", errCode); + goto END; + } + + errCode = sqlite3_bind_int64(statement, index + 2, clearId_); // combination using with the clear time. + if (errCode != SQLITE_OK) { + LOGE("Bind the clear id for query error:%d", errCode); + goto END; + } + + errCode = sqlite3_bind_int64(statement, index + 3, version_); // version is after the clear rowid. + if (errCode != SQLITE_OK) { + LOGE("Bind the version for query error:%d", errCode); + goto END; + } +END: + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +int SQLiteMultiVerTransaction::BindQueryEntryArgs(sqlite3_stmt *statement, + const Key &key) const +{ + int errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // first argument is key + if (errCode != E_OK) { + return errCode; + } + + return BindClearIdAndVersion(statement, 2); // the third argument is clear id. +} + +int SQLiteMultiVerTransaction::BindQueryEntriesArgs(sqlite3_stmt *statement, + const Key &key) const +{ + // bind the prefix key for the first and second args. + int errCode = SQLiteUtils::BindPrefixKey(statement, 1, key); // first argument is key + if (errCode != E_OK) { + return errCode; + } + + return BindClearIdAndVersion(statement, 3); // the third argument is clear id. +} + +int SQLiteMultiVerTransaction::BindAddRecordKeysToStatement(sqlite3_stmt *statement, const Key &key, + const MultiVerEntryAuxData &data) +{ + if ((data.operFlag & OPERATE_MASK) != ADD_FLAG) { + Key emptyKey; + int errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_INSERT_KEY_INDEX, emptyKey, true); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_INSERT_HASH_KEY_INDEX, key, false); + if (errCode != E_OK) { + return errCode; + } + return errCode; + } + int errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_INSERT_KEY_INDEX, key, false); + if (errCode != E_OK) { + return errCode; + } + Key hashKey; + errCode = DBCommon::CalcValueHash(key, hashKey); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_INSERT_HASH_KEY_INDEX, hashKey, false); + if (errCode != E_OK) { + return errCode; + } + return errCode; +} + +int SQLiteMultiVerTransaction::BindAddRecordArgs(sqlite3_stmt *statement, + const Key &key, const Value &value, const MultiVerEntryAuxData &data) const +{ + int errCode = BindAddRecordKeysToStatement(statement, key, data); + if (errCode != E_OK) { + LOGE("Failed to bind the keys:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_INSERT_VAL_INDEX, value, true); + if (errCode != E_OK) { + return errCode; + } + + errCode = sqlite3_bind_int64(statement, BIND_INSERT_OPER_FLG_INDEX, static_cast(data.operFlag)); + if (errCode != SQLITE_OK) { + goto END; + } + + errCode = sqlite3_bind_int64(statement, BIND_INSERT_VER_INDEX, static_cast(version_)); + if (errCode != SQLITE_OK) { + goto END; + } + + errCode = sqlite3_bind_int64(statement, BIND_INSERT_TIME_INDEX, static_cast(data.timestamp)); + if (errCode != SQLITE_OK) { + goto END; + } + + errCode = sqlite3_bind_int64(statement, BIND_INSERT_ORI_TIME_INDEX, static_cast(data.oriTimestamp)); + if (errCode != SQLITE_OK) { + goto END; + } + +END: + if (errCode != SQLITE_OK) { + LOGE("Failed to bind the value:%d", errCode); + } + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +int SQLiteMultiVerTransaction::GetOneEntry(const GetEntriesStatements &statements, + const Key &lastKey, Entry &entry, int &errCode) const +{ + // SQL: "select oper_flag, key, value, version from data;" + errCode = SQLiteUtils::GetColumnBlobValue(statements.getEntriesStatement, 1, entry.key); // 2th is key + if (errCode != E_OK) { + return STEP_ERROR; + } + + // if equal to the last key, just step to the next one. + if (lastKey == entry.key) { + entry.key.clear(); + return STEP_CONTINUE; + } + uint64_t flag = static_cast(sqlite3_column_int64(statements.getEntriesStatement, 0)); // 1th is flag + if ((flag & OPERATE_MASK) != ADD_FLAG) { + return STEP_NEXTKEY; + } + + errCode = SQLiteUtils::GetColumnBlobValue(statements.getEntriesStatement, 2, entry.value); // 3rd is value + if (errCode != E_OK) { + return STEP_ERROR; + } + + Version curVer = static_cast(sqlite3_column_int64(statements.getEntriesStatement, 3)); // 4th is ver + // select the version that is greater than the curEntryVer; + Key hashKey; + errCode = DBCommon::CalcValueHash(entry.key, hashKey); + if (errCode != E_OK) { + return STEP_ERROR; + } + errCode = SQLiteUtils::BindBlobToStatement(statements.hashFilterStatement, 1, hashKey, false); + if (errCode != E_OK) { + return STEP_ERROR; + } + + errCode = sqlite3_bind_int64(statements.hashFilterStatement, 2, static_cast(curVer)); + if (errCode != E_OK) { + return STEP_ERROR; + } + errCode = sqlite3_bind_int64(statements.hashFilterStatement, 3, static_cast(version_)); + if (errCode != E_OK) { + return STEP_ERROR; + } + errCode = SQLiteUtils::StepWithRetry(statements.hashFilterStatement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + return STEP_NEXTKEY; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + return STEP_SUCCESS; + } else { + LOGE("Filter the entries hash key error:%d", errCode); + return STEP_ERROR; + } +} + +bool SQLiteMultiVerTransaction::IsRecordCleared(const Timestamp timestamp) const +{ + GetClearId(); + if (clearTime_ < 0) { + return true; + } + if (timestamp <= static_cast(clearTime_)) { + return true; + } + return false; +} + +int SQLiteMultiVerTransaction::CheckIfNeedSaveRecord(sqlite3_stmt *statement, const MultiVerKvEntry *multiVerKvEntry, + bool &isNeedSave, Value &origVal) const +{ + // Bind the input args for sql + int errCode; + Key key; + Value value; + uint64_t operFlag = 0; + static_cast(multiVerKvEntry)->GetKey(key); + static_cast(multiVerKvEntry)->GetOperFlag(operFlag); + static_cast(multiVerKvEntry)->GetValue(value); + if ((operFlag & OPERATE_MASK) == ADD_FLAG) { + Key hashKey; + errCode = DBCommon::CalcValueHash(key, hashKey); + if (errCode != E_OK) { + return errCode; + } + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, hashKey, false); // key is the first arg + } else { + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // key is the first arg + } + + if (errCode != E_OK) { + return errCode; + } + + // ori_stamp should diff from timstamp + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + isNeedSave = true; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + auto readTime = static_cast(sqlite3_column_int64(statement, 0)); // the first for time + auto readOriTime = static_cast(sqlite3_column_int64(statement, 1)); // the second for orig time. + auto readVersion = static_cast(sqlite3_column_int64(statement, 2)); // the third for version. + errCode = SQLiteUtils::GetColumnBlobValue(statement, 3, origVal); // the fourth for origin value. + if (errCode != E_OK) { + return errCode; + } + Timestamp timestamp = NO_TIMESTAMP; + static_cast(multiVerKvEntry)->GetTimestamp(timestamp); + Timestamp oriTimestamp = NO_TIMESTAMP; + static_cast(multiVerKvEntry)->GetOriTimestamp(oriTimestamp); + // Only the latest origin time is same or the reading time is bigger than putting time. + isNeedSave = ((readTime < timestamp) && (readOriTime != oriTimestamp || value != origVal)); + LOGD("Timestamp :%" PRIu64 " vs %" PRIu64 ", %" PRIu64 " vs %" PRIu64 ", readVersion:%" PRIu64 ", version:" + "%" PRIu64 ", %d", readOriTime, oriTimestamp, readTime, timestamp, readVersion, version_, isNeedSave); + // if the version of the data to be saved is same to the original, you should notify the caller. + if (readVersion != version_) { + origVal.resize(0); + } + } else { + LOGE("Check if need store sync entry failed:%d", errCode); + } + + return errCode; +} + +int SQLiteMultiVerTransaction::CheckIfNeedSaveRecord(const MultiVerKvEntry *multiVerKvEntry, bool &isNeedSave, + Value &value) const +{ + auto entry = static_cast(multiVerKvEntry); + Timestamp timestamp = NO_TIMESTAMP; + entry->GetTimestamp(timestamp); + if (IsRecordCleared(timestamp)) { + isNeedSave = false; + entry->GetValue(value); + return E_OK; + } + + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, SELECT_ONE_BY_KEY_TIMESTAMP_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + + errCode = CheckIfNeedSaveRecord(statement, entry, isNeedSave, value); + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::PrepareForGetEntries(const Key &keyPrefix, GetEntriesStatements &statements) const +{ + int innerCode; + int errCode = SQLiteUtils::GetStatement(db_, SELECT_BATCH_SQL, statements.getEntriesStatement); + if (errCode != E_OK) { + goto END; + } + errCode = SQLiteUtils::GetStatement(db_, SELECT_HASH_ENTRY_SQL, statements.hashFilterStatement); + if (errCode != E_OK) { + goto END; + } + + GetClearId(); // for read data. + errCode = BindQueryEntriesArgs(statements.getEntriesStatement, keyPrefix); + if (errCode != E_OK) { + goto END; + } + return E_OK; +END: + innerCode = ReleaseGetEntriesStatements(statements); + if (errCode == E_OK) { + errCode = innerCode; + } + return errCode; +} + +int SQLiteMultiVerTransaction::ReleaseGetEntriesStatements(GetEntriesStatements &statements) const +{ + int errCode = E_OK; + SQLiteUtils::ResetStatement(statements.getEntriesStatement, true, errCode); + SQLiteUtils::ResetStatement(statements.hashFilterStatement, true, errCode); + return errCode; +} + +int SQLiteMultiVerTransaction::GetKeyAndValueByHashKey(sqlite3_stmt *statement, const Key &hashKey, + Key &key, Value &value, bool isNeedReadKey) const +{ + int errCode = BindQueryEntryArgs(statement, hashKey); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + return -E_NOT_FOUND; + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + return errCode; + } + + uint64_t flag = static_cast(sqlite3_column_int64(statement, 0)); // get the flag + if ((flag & OPERATE_MASK) != ADD_FLAG) { // if not add or replace, + return -E_NOT_FOUND; + } + if (isNeedReadKey) { + errCode = SQLiteUtils::GetColumnBlobValue(statement, 1, key); // 2nd column result is key. + if (errCode != E_OK) { + return errCode; + } + } + + return SQLiteUtils::GetColumnBlobValue(statement, 2, value); // 3rd column result is value. +} + +int SQLiteMultiVerTransaction::GetOriginKeyValueByHash(MultiVerEntryData &item, Value &value) const +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db_, SELECT_ONE_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + Key origKey; + errCode = GetKeyAndValueByHashKey(statement, item.key, origKey, value, true); + if (errCode != E_OK) { + goto END; + } + item.key = origKey; +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.h b/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.h new file mode 100644 index 0000000000000000000000000000000000000000..bee608f78697d991b6f54cb1f8fce778af106edd --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_MULTI_VER_TRANSACTION_H +#define SQLITE_MULTI_VER_TRANSACTION_H + +#ifndef OMIT_MULTI_VER +#include +#include +#include + +#include "sqlite_import.h" +#include "db_types.h" +#include "kvdb_properties.h" +#include "ikvdb_multi_ver_transaction.h" +#include "macro_utils.h" +#include "multi_ver_value_object.h" +#include "generic_multi_ver_kv_entry.h" + +namespace DistributedDB { +class SQLiteMultiVerTransaction : public IKvDBMultiVerTransaction { +public: + SQLiteMultiVerTransaction(); + ~SQLiteMultiVerTransaction() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteMultiVerTransaction); + + int Initialize(const std::string &uri, bool isReadOnly, CipherType type, const CipherPassword &passwd); + + int Put(const Key &key, const Value &value) override; + + int Delete(const Key &key) override; + + int Clear() override; + + int Get(const Key &key, Value &value) const override; + + int GetEntries(const Key &keyPrefix, std::vector &entries) const override; + + int PutBatch(const std::vector &entries); + + int PutBatch(const std::vector &entries, bool isLocal, std::vector &values) override; + + int GetDiffEntries(const Version &begin, const Version &end, MultiVerDiffData &data) const override; + + int GetMaxVersion(MultiVerDataType type, Version &maxVersion) const override; + + int ClearEntriesByVersion(const Version &versionInfo) override; + + int StartTransaction() override; + + int RollBackTransaction() override; + + int CommitTransaction() override; + + int GetEntriesByVersion(Version version, std::list &data) const override; + + // Get Entries from the version. + int GetEntriesByVersion(const Version &versionInfo, std::vector &entries) const override; + + // Update the timestamp of the version. + int UpdateTimestampByVersion(const Version &version, Timestamp stamp) const override; + + bool IsDataChanged() const override; + + // Get the max timestamp to generate the new version for the writing transaction + Timestamp GetCurrentMaxTimestamp() const override; + + // Reset the version. + void ResetVersion(); + + // Reset the transaction while committing failed. + int Reset(CipherType type, const CipherPassword &passwd); + + // Check if the entry already cleared + bool IsRecordCleared(const Timestamp timestamp) const override; + + void SetVersion(const Version &versionInfo) override; + + Version GetVersion() const override; + + int GetOverwrittenClearTypeEntries(Version clearVersion, std::list &data) const override; + + int GetOverwrittenNonClearTypeEntries(Version version, const Key &hashKey, + std::list &data) const override; + + int DeleteEntriesByHashKey(Version version, const Key &hashKey) override; + + int GetValueForTrimSlice(const Key &hashKey, const Version version, Value &value) const override; + + int CheckIfNeedSaveRecord(sqlite3_stmt *statement, const MultiVerKvEntry *multiVerKvEntry, + bool &isNeedSave, Value &origVal) const; + +private: + struct GetEntriesStatements { + sqlite3_stmt *getEntriesStatement = nullptr; + sqlite3_stmt *hashFilterStatement = nullptr; + }; + + enum EntryOperator { + INSERT, + UPDATE, + DELETE, + CLEAR, + FAIL, + }; + + static int GetRawMultiVerEntry(sqlite3_stmt *statement, MultiVerEntryData &keyEntry); + + static int GetRawDataByVersion(sqlite3_stmt *&statement, const Version &version, + std::vector &entries); + + static int GetDiffOperator(int errCode, uint64_t flag); + + static int BindAddRecordKeysToStatement(sqlite3_stmt *statement, const Key &key, + const MultiVerEntryAuxData &data); + + int AddRecord(const Key &key, const Value &value, const MultiVerEntryAuxData &data); + + void ClassifyDiffEntries(int errCode, uint64_t flag, const Value &value, + MultiVerEntryData &item, MultiVerDiffData &data) const; + + void GetClearId() const; + + int BindClearIdAndVersion(sqlite3_stmt *statement, int index) const; + + int BindQueryEntryArgs(sqlite3_stmt *statement, const Key &key) const; + + int BindQueryEntriesArgs(sqlite3_stmt *statement, const Key &key) const; + + int BindAddRecordArgs(sqlite3_stmt *statement, const Key &key, const Value &value, + const MultiVerEntryAuxData &data) const; + + int GetOneEntry(const GetEntriesStatements &statements, const Key &lastKey, Entry &entry, int &errCode) const; + + int RemovePrePutEntries(const Version &versionInfo, Timestamp timestamp); + + int CheckToSaveRecord(const MultiVerKvEntry *entry, bool &isNeedSave, std::vector &values); + + // Check if the entry with later timestamp already exist in the database + int CheckIfNeedSaveRecord(const MultiVerKvEntry *multiVerKvEntry, bool &isNeedSave, Value &value) const; + + int PrepareForGetEntries(const Key &keyPrefix, GetEntriesStatements &statements) const; + + int ReleaseGetEntriesStatements(GetEntriesStatements &statements) const; + + int GetKeyAndValueByHashKey(sqlite3_stmt *statement, const Key &hashKey, Key &key, Value &value, + bool isNeedReadKey) const; + + int GetOriginKeyValueByHash(MultiVerEntryData &item, Value &value) const; + + int GetPrePutValues(const Version &versionInfo, Timestamp timestamp, std::vector &values) const; + + static const std::string CREATE_TABLE_SQL; + static const std::string SELECT_ONE_SQL; // select the rowid + static const std::string SELECT_BATCH_SQL; // select the rowid and the key + static const std::string SELECT_HASH_ENTRY_SQL; // select the data according the hash key + static const std::string SELECT_ONE_VER_SQL; // select the rowid + static const std::string SELECT_BATCH_VER_SQL; // select the rowid and the key + static const std::string SELECT_ONE_VER_RAW_SQL; + static const std::string INSERT_SQL; // insert or replace the values + static const std::string DELETE_SQL; // delete the key-value record + static const std::string SELECT_ONE_BY_KEY_TIMESTAMP_SQL; // get one by key and timestamp + + static const std::string DELETE_VER_SQL; + static const std::string SELECT_PRE_PUT_VER_DATA_SQL; + static const std::string DELETE_PRE_PUT_VER_DATA_SQL; + static const std::string SELECT_LATEST_CLEAR_ID; + static const std::string SELECT_MAX_LOCAL_VERSION; + static const std::string SELECT_MAX_VERSION; + static const std::string SELECT_MAX_TIMESTAMP; + static const std::string UPDATE_VERSION_TIMESTAMP; + static const std::string SELECT_OVERWRITTEN_CLEAR_TYPE; + static const std::string SELECT_OVERWRITTEN_NO_CLEAR_TYPE; + static const std::string DELETE_BY_VER_HASHKEY_SQL; + static const std::string SELECT_BY_HASHKEY_VER_SQL; + + static const uint64_t ADD_FLAG = 0x01; // add or replace the record. + static const uint64_t DEL_FLAG = 0x02; // delete the record. + static const uint64_t CLEAR_FLAG = 0x03; // clear all the record. + + static const uint64_t LOCAL_FLAG = 0x08; // local flag for read. + static const uint64_t OPERATE_MASK = 0x07; // operate mask for add, delete + + std::mutex resetMutex_; + mutable std::mutex readMutex_; + + mutable int64_t clearId_; // for query the result after the clear operation in the same commit. + mutable int64_t clearTime_; // for query the result after the clear operation + mutable uint64_t currentMaxTimestamp_; + Version version_; // the read version or the current commit version. + sqlite3 *db_; + std::string uri_; + bool isReadOnly_; + bool isDataChanged_; // whether the transaction has new record. +}; +} // namespace DistributedDB + +#endif // SQLITE_MULTI_VER_TRANSACTION_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_query_helper.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_query_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24b52f4603a338e23f083d609f0852a9452d9347 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_query_helper.cpp @@ -0,0 +1,1067 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sqlite_query_helper.h" + +#include + +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "log_print.h" +#include "macro_utils.h" +#include "sqlite_utils.h" + +namespace DistributedDB { +using namespace TriggerMode; +namespace { +const std::string PRE_QUERY_KV_SQL = "SELECT key, value FROM sync_data "; +const std::string PRE_QUERY_ITEM_SQL = "SELECT * FROM "; +const std::string PRE_QUERY_ROWID_SQL = "SELECT rowid FROM sync_data "; +const std::string PRE_GET_COUNT_SQL = "SELECT count(*) FROM sync_data "; +const std::string FILTER_NATIVE_DATA_SQL = "WHERE (flag&0x01=0) "; +const std::string FILTER_REMOTE_QUERY = "WHERE (flag&0x03=0x02)"; +const std::string USING_INDEX = "INDEXED BY "; +const int MAX_SQL_LEN = 1024 * 1024; // 1M bytes +const int SINGLE_FIELD_VALUE_SIZE = 1; +const int MAX_CONDITIONS_SIZE = 128; +const int MAX_SQLITE_BIND_SIZE = 50000; +const uint32_t SYMBOL_TYPE_MASK = 0xff00; + +const std::map RELATIONAL_SYMBOL_TO_SQL { + {QueryObjType::EQUALTO, "= "}, + {QueryObjType::NOT_EQUALTO, "!= "}, + {QueryObjType::GREATER_THAN, "> "}, + {QueryObjType::LESS_THAN, "< "}, + {QueryObjType::GREATER_THAN_OR_EQUALTO, ">= "}, + {QueryObjType::LESS_THAN_OR_EQUALTO, "<= "}, + {QueryObjType::LIKE, " LIKE "}, + {QueryObjType::NOT_LIKE, " NOT LIKE "}, + {QueryObjType::IS_NULL, " IS NULL "}, + {QueryObjType::IS_NOT_NULL, " IS NOT NULL "}, + {QueryObjType::IN, " IN ("}, + {QueryObjType::NOT_IN, " NOT IN ("}, +}; + +const std::map LOGIC_SYMBOL_TO_SQL { + {QueryObjType::AND, " AND "}, + {QueryObjType::OR, " OR "}, + {QueryObjType::BEGIN_GROUP, "("}, + {QueryObjType::END_GROUP, ")"}, +}; + +std::string FieldValue2String(const FieldValue &val, QueryValueType type) +{ + std::stringstream ss; + switch (type) { + case QueryValueType::VALUE_TYPE_NULL: + return "NULL"; + case QueryValueType::VALUE_TYPE_BOOL: + return val.boolValue ? "1" : "0"; + case QueryValueType::VALUE_TYPE_INTEGER: + return std::to_string(val.integerValue); + case QueryValueType::VALUE_TYPE_LONG: + return std::to_string(val.longValue); + case QueryValueType::VALUE_TYPE_DOUBLE: + ss << std::setprecision(DBConstant::DOUBLE_PRECISION) << val.doubleValue; + return ss.str(); + case QueryValueType::VALUE_TYPE_STRING: + return "'" + val.stringValue + "'"; + case QueryValueType::VALUE_TYPE_INVALID: + default: + return ""; + } +} + +std::string GetSelectAndFromClauseForRDB(const std::string &tableName, const std::vector &fieldNames) +{ + std::string sql = "SELECT b.data_key," + "b.device," + "b.ori_device," + "b.timestamp as " + DBConstant::TIMESTAMP_ALIAS + "," + "b.wtimestamp," + "b.flag," + "b.hash_key,"; + if (fieldNames.empty()) { // For query check. If column count changed, can be discovered. + sql += "a.*"; + } else { + for (const auto &fieldName : fieldNames) { // For query data. + sql += "a." + fieldName + ","; + } + sql.pop_back(); + } + sql += " FROM " + tableName + " AS a INNER JOIN " + DBConstant::RELATIONAL_PREFIX + tableName + "_log AS b " + "ON a.rowid=b.data_key "; + return sql; +} + +std::string GetTimeRangeClauseForRDB() +{ + return " AND (" + DBConstant::TIMESTAMP_ALIAS + ">=? AND " + DBConstant::TIMESTAMP_ALIAS + "=? AND " + DBConstant::TIMESTAMP_ALIAS + "(static_cast(queryObjType) & SYMBOL_TYPE_MASK); +} + +bool SqliteQueryHelper::FilterSymbolToAddBracketLink(std::string &querySql, bool isNeedLink) const +{ + bool isNeedEndBracket = false; + for (const auto &iter : queryObjNodes_) { + SymbolType symbolType = GetSymbolType(iter.operFlag); + if (symbolType == COMPARE_SYMBOL || symbolType == RELATIONAL_SYMBOL || symbolType == RANGE_SYMBOL) { + querySql += isNeedLink ? " AND (" : " ("; + isNeedEndBracket = true; + break; + } else if (symbolType == LOGIC_SYMBOL || symbolType == PREFIXKEY_SYMBOL || symbolType == IN_KEYS_SYMBOL) { + continue; + } else { + break; + } + } + return isNeedEndBracket; +} + + +int SqliteQueryHelper::ParseQueryObjNodeToSQL(bool isQueryForSync) +{ + if (queryObjNodes_.empty()) { + if (!isQueryForSync) { + querySql_ += ";"; + } + return E_OK; + } + + bool isNeedEndBracket = FilterSymbolToAddBracketLink(querySql_); + + int errCode = E_OK; + for (const QueryObjNode &objNode : queryObjNodes_) { + SymbolType symbolType = GetSymbolType(objNode.operFlag); + if (symbolType == SPECIAL_SYMBOL && isNeedEndBracket) { + querySql_ += ") "; + isNeedEndBracket = false; + } + errCode = ParseQueryExpression(objNode, querySql_); + if (errCode != E_OK) { + querySql_.clear(); + return errCode; + } + } + + if (isNeedEndBracket) { + querySql_ += ") "; + } + + return errCode; +} + +int SqliteQueryHelper::ToQuerySql() +{ + int errCode = ParseQueryObjNodeToSQL(false); + if (errCode != E_OK) { + return errCode; + } + + // Limit needs to be placed after orderBy and processed separately in the limit branch + if (hasPrefixKey_ && !hasOrderBy_ && !hasLimit_ && isNeedOrderbyKey_) { + LOGD("Need add order by key at last when has prefixKey no need order by value and limit!"); + querySql_ += "ORDER BY key ASC"; + } + querySql_ += ";"; + return errCode; +} + +int SqliteQueryHelper::ToQuerySyncSql(bool hasSubQuery, bool useTimestampAlias) +{ + int errCode = ParseQueryObjNodeToSQL(true); + if (errCode != E_OK) { + return errCode; + } + + // Order by time when no order by and no limit and no need order by key. + if (!hasOrderBy_ && !hasLimit_ && !isNeedOrderbyKey_) { + querySql_ += (useTimestampAlias ? + ("ORDER BY " + DBConstant::TIMESTAMP_ALIAS + " ASC") : + "ORDER BY timestamp ASC"); + } + + if (!hasSubQuery) { + querySql_ += ";"; + } + return errCode; +} + +int SqliteQueryHelper::ToGetCountSql() +{ + countSql_.clear(); + if (queryObjNodes_.empty()) { + countSql_ += ";"; + return E_OK; + } + bool isNeedEndBracket = FilterSymbolToAddBracketLink(countSql_); + + int errCode = E_OK; + for (const QueryObjNode &objNode : queryObjNodes_) { + SymbolType symbolType = GetSymbolType(objNode.operFlag); + if (symbolType == SPECIAL_SYMBOL && isNeedEndBracket) { + countSql_ += ") "; + isNeedEndBracket = false; + } + + if (objNode.operFlag == QueryObjType::LIMIT) { + hasLimit_ = true; + continue; + } + if (objNode.operFlag == QueryObjType::ORDERBY) { + hasOrderBy_ = true; + continue; + } + errCode = ParseQueryExpression(objNode, countSql_); + if (errCode != E_OK) { + countSql_.clear(); + return errCode; + } + } + + if (isNeedEndBracket) { + countSql_ += ") "; + } + + // Limit needs to be placed after orderBy and processed separately in the limit branch + if (hasPrefixKey_ && !hasOrderBy_ && !hasLimit_ && isNeedOrderbyKey_) { + LOGD("Need add order by key at last when has prefixKey no need order by value and limit!"); + countSql_ += "ORDER BY key ASC"; + } + countSql_ += ";"; + return errCode; +} + +int SqliteQueryHelper::GetQuerySql(std::string &sql, bool onlyRowid) +{ + if (!isValid_) { + return -E_INVALID_QUERY_FORMAT; + } + + const std::string &querySqlForUse = (onlyRowid ? PRE_QUERY_ROWID_SQL : PRE_QUERY_KV_SQL); + sql = AssembleSqlForSuggestIndex(querySqlForUse, FILTER_NATIVE_DATA_SQL); + sql = !hasPrefixKey_ ? sql : (sql + " AND (key>=? AND key<=?) "); + sql = keys_.empty() ? sql : (sql + " AND " + MapKeysInToSql(keys_.size())); + if (transformed_) { + LOGD("This query object has been parsed."); + sql += querySql_; + return E_OK; + } + int errCode = ToQuerySql(); + if (errCode != E_OK) { + LOGE("Transfer to query sql failed! errCode[%d]", errCode); + return errCode; + } + transformed_ = true; + sql += querySql_; + return errCode; +} + +int SqliteQueryHelper::GetSyncDataCheckSql(std::string &sql) +{ + int errCode = E_OK; + if (!isValid_) { + return -E_INVALID_QUERY_FORMAT; + } + sql = PRE_QUERY_ITEM_SQL + tableName_ + " WHERE hash_key=? AND (flag&0x01=0) "; + sql += hasPrefixKey_ ? " AND (key>=? AND key<=?) " : ""; + sql = keys_.empty() ? sql : (sql + " AND " + MapKeysInToSql(keys_.size())); + if (!transformed_) { + errCode = ToQuerySql(); + if (errCode != E_OK) { + LOGE("Transfer query to sync data check sql failed! errCode[%d]", errCode); + return errCode; + } + transformed_ = true; + } + sql += querySql_; + return errCode; +} + +int SqliteQueryHelper::BindSyncDataCheckStmt(sqlite3_stmt *statement, const Key &hashKey) const +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + int index = 1; // bind statement start index 1 + int errCode = SQLiteUtils::BindBlobToStatement(statement, index++, hashKey, false); + if (errCode != E_OK) { + LOGE("Get sync data check statement failed when bind hash key, errCode = %d", errCode); + return errCode; + } + if (hasPrefixKey_) { + // bind the prefix key for the first and second args. + errCode = SQLiteUtils::BindPrefixKey(statement, index, prefixKey_); + if (errCode != E_OK) { + LOGE("Get sync data check statement failed when bind prefix key, errCode = %d", errCode); + return errCode; + } + index += 2; // prefixKey takes 2 position + } + + errCode = BindKeysToStmt(keys_, statement, index); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; + } + + for (const QueryObjNode &objNode : queryObjNodes_) { + errCode = BindFieldValue(statement, objNode, index); + if (errCode != E_OK) { + LOGE("Get sync data check statement failed when bind field value, errCode = %d", errCode); + return errCode; + } + } + return errCode; +} + +int SqliteQueryHelper::GetCountQuerySql(std::string &sql) +{ + if (!isValid_) { + return -E_INVALID_QUERY_FORMAT; + } + + int errCode = ToGetCountSql(); + if (errCode != E_OK) { + return errCode; + } + sql = AssembleSqlForSuggestIndex(PRE_GET_COUNT_SQL, FILTER_NATIVE_DATA_SQL); + sql = !hasPrefixKey_ ? sql : (sql + " AND (key>=? AND key<=?) "); + sql = keys_.empty() ? sql : (sql + " AND " + MapKeysInToSql(keys_.size())); + sql += countSql_; + return E_OK; +} + +int SqliteQueryHelper::GetQuerySqlStatement(sqlite3 *dbHandle, const std::string &sql, sqlite3_stmt *&statement) +{ + int errCode = SQLiteUtils::GetStatement(dbHandle, sql, statement); + if (errCode != E_OK) { + LOGE("[Query] Get statement fail!"); + return -E_INVALID_QUERY_FORMAT; + } + int index = 1; + if (hasPrefixKey_) { + // bind the prefix key for the first and second args. + errCode = SQLiteUtils::BindPrefixKey(statement, 1, prefixKey_); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statement, true, errCode); + LOGE("[Query] Get statement when bind prefix key, errCode = %d", errCode); + return errCode; + } + index = 3; // begin from 3rd args + } + + errCode = BindKeysToStmt(keys_, statement, index); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; + } + + for (const QueryObjNode &objNode : queryObjNodes_) { + errCode = BindFieldValue(statement, objNode, index); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statement, true, errCode); + LOGE("[Query] Get statement fail when bind field value, errCode = %d", errCode); + return errCode; + } + } + return errCode; +} + +int SqliteQueryHelper::GetQuerySqlStatement(sqlite3 *dbHandle, bool onlyRowid, sqlite3_stmt *&statement) +{ + std::string sql; + int errCode = GetQuerySql(sql, onlyRowid); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::GetStatement(dbHandle, sql, statement); + if (errCode != E_OK) { + LOGE("[Query] Get statement fail!"); + return -E_INVALID_QUERY_FORMAT; + } + int index = 1; + if (hasPrefixKey_) { + // bind the prefix key for the first and second args. + errCode = SQLiteUtils::BindPrefixKey(statement, 1, prefixKey_); + if (errCode != E_OK) { + LOGE("[Query] Get statement when bind prefix key, errCode = %d", errCode); + return errCode; + } + index = 3; // begin from 3rd args + } + + errCode = BindKeysToStmt(keys_, statement, index); + if (errCode != E_OK) { + return errCode; + } + + for (const QueryObjNode &objNode : queryObjNodes_) { + errCode = BindFieldValue(statement, objNode, index); + if (errCode != E_OK) { + LOGE("[Query] Get statement fail when bind field value, errCode = %d", errCode); + return errCode; + } + } + return errCode; +} + +int SqliteQueryHelper::GetCountSqlStatement(sqlite3 *dbHandle, sqlite3_stmt *&countStmt) +{ + std::string countSql; + int errCode = GetCountQuerySql(countSql); + if (errCode != E_OK) { + return errCode; + } + + // bind statement for count + errCode = SQLiteUtils::GetStatement(dbHandle, countSql, countStmt); + if (errCode != E_OK) { + LOGE("Get count statement error:%d", errCode); + return -E_INVALID_QUERY_FORMAT; + } + int index = 1; + if (hasPrefixKey_) { + // bind the prefix key for the first and second args. + errCode = SQLiteUtils::BindPrefixKey(countStmt, 1, prefixKey_); + if (errCode != E_OK) { + LOGE("[Query] Get count statement fail when bind prefix key, errCode = %d", errCode); + return errCode; + } + index = 3; // begin from 3rd args + } + + errCode = BindKeysToStmt(keys_, countStmt, index); + if (errCode != E_OK) { + return errCode; + } + + for (const QueryObjNode &objNode : queryObjNodes_) { + if (GetSymbolType(objNode.operFlag) == SPECIAL_SYMBOL) { + continue; + } + errCode = BindFieldValue(countStmt, objNode, index); + if (errCode != E_OK) { + LOGE("[Query] Get count statement fail when bind field value, errCode = %d", errCode); + return errCode; + } + } + return errCode; +} + +int SqliteQueryHelper::GetSyncDataQuerySql(std::string &sql, bool hasSubQuery) +{ + if (!isValid_) { + return -E_INVALID_QUERY_FORMAT; + } + + if (hasLimit_) { + hasSubQuery = true; // Need sub query. + } else { + isNeedOrderbyKey_ = false; // Need order by timestamp. + } + + sql = AssembleSqlForSuggestIndex(PRE_QUERY_ITEM_SQL + tableName_ + " ", FILTER_REMOTE_QUERY); + sql = !hasPrefixKey_ ? sql : (sql + " AND (key>=? AND key<=?) "); + sql = keys_.empty() ? sql : (sql + " AND " + MapKeysInToSql(keys_.size())); + sql = hasSubQuery ? sql : (sql + " AND (timestamp>=? AND timestamp= ? AND timestamp < ?) ORDER BY timestamp;"; + } + return errCode; +} + +int SqliteQueryHelper::BindTimeRange(sqlite3_stmt *&statement, int &index, uint64_t beginTime, uint64_t endTime) const +{ + int errCode = SQLiteUtils::BindInt64ToStatement(statement, index++, beginTime); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, index++, endTime); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statement, true, errCode); + } + return errCode; +} + +int SqliteQueryHelper::BindObjNodes(sqlite3_stmt *&statement, int &index) const +{ + int errCode = E_OK; + for (const QueryObjNode &objNode : queryObjNodes_) { + errCode = BindFieldValue(statement, objNode, index); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statement, true, errCode); + LOGE("[Query] Get statement fail when bind field value, errCode = %d", errCode); + break; + } + } + return errCode; +} + +int SqliteQueryHelper::GetQuerySyncStatement(sqlite3 *dbHandle, uint64_t beginTime, uint64_t endTime, + sqlite3_stmt *&statement) +{ + bool hasSubQuery = false; + if (hasLimit_) { + hasSubQuery = true; // Need sub query. + } else { + isNeedOrderbyKey_ = false; // Need order by timestamp. + } + std::string sql; + int errCode = GetSyncDataQuerySql(sql, hasSubQuery); + if (errCode != E_OK) { + LOGE("[Query] Get SQL fail!"); + return -E_INVALID_QUERY_FORMAT; + } + + errCode = SQLiteUtils::GetStatement(dbHandle, sql, statement); + if (errCode != E_OK) { + LOGE("[Query] Get statement fail!"); + return -E_INVALID_QUERY_FORMAT; + } + + int index = 1; // begin with 1. + if (hasPrefixKey_) { + // bind the prefix key for the first and second args. + errCode = SQLiteUtils::BindPrefixKey(statement, index, prefixKey_); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statement, true, errCode); + LOGE("[Query] Get statement when bind prefix key, errCode = %d", errCode); + return errCode; + } + index = 3; // begin with 3 next if prefix key exists. + } + + errCode = BindKeysToStmt(keys_, statement, index); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; + } + + if (hasSubQuery) { + // For sub query SQL, timestamp must be last : (prefix key), (objNodes), timestamp. + // SQL: SELECT * FROM ( SELECT * FROM sync_data WHERE (flag&0x03=0x02) LIMIT 10 OFFSET 0 ) WHERE (timestamp>=? + // AND timestamp=? AND timestamp MAX_SQLITE_BIND_SIZE) { + return -E_MAX_LIMITS; + } + errCode = sqlite3_bind_text(statement, index, queryNode.fieldValue[i].stringValue.c_str(), + queryNode.fieldValue[i].stringValue.size(), SQLITE_TRANSIENT); + } + if (errCode != SQLITE_OK) { + break; + } + index++; + } + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +std::string SqliteQueryHelper::MapCastTypeSql(const FieldType &type) const +{ + switch (type) { + case FieldType::LEAF_FIELD_BOOL: + case FieldType::LEAF_FIELD_INTEGER: + case FieldType::LEAF_FIELD_LONG: + return "INT"; + case FieldType::LEAF_FIELD_DOUBLE: + return "REAL"; + case FieldType::LEAF_FIELD_STRING: + return "TEXT"; + case FieldType::LEAF_FIELD_NULL: + return "NULL"; + default: + return ""; + } +} + +std::string SqliteQueryHelper::GetFieldShape(const QueryObjNode &queryNode, const std::string &accessStr) +{ + if (isRelationalQuery_) { + // For relational query, $. prefix is not permitted, so need not extract json. Return directly will be OK. + return "a." + queryNode.fieldName + " "; + } + return MapCastFuncSql(queryNode, accessStr); +} + +int SqliteQueryHelper::ParseQueryExpression(const QueryObjNode &queryNode, std::string &querySql, + const std::string &accessStr, bool placeholder) +{ + SymbolType symbolType = GetSymbolType(queryNode.operFlag); + if (symbolType == RANGE_SYMBOL && queryNode.fieldValue.size() > MAX_CONDITIONS_SIZE) { + LOGE("[Query][Parse][Expression] conditions is too many!"); + return -E_MAX_LIMITS; + } + + if (symbolType == COMPARE_SYMBOL || symbolType == RELATIONAL_SYMBOL || symbolType == RANGE_SYMBOL) { + querySql += GetFieldShape(queryNode, accessStr); + querySql += MapRelationalSymbolToSql(queryNode, placeholder); + } else if (symbolType == LOGIC_SYMBOL || symbolType == LINK_SYMBOL) { + querySql += MapLogicSymbolToSql(queryNode); + } else { + querySql += MapKeywordSymbolToSql(queryNode); + } + + if (querySql.size() > MAX_SQL_LEN) { + LOGE("[Query][Parse][Expression] Sql is too long!"); + return -E_MAX_LIMITS; + } + return E_OK; +} + +std::string SqliteQueryHelper::AssembleSqlForSuggestIndex(const std::string &baseSql, const std::string &filter) const +{ + std::string formatIndex = CheckAndFormatSuggestIndex(); + if (formatIndex.empty()) { + return baseSql + filter; + } + + return baseSql + USING_INDEX + "'" + formatIndex + "' " + filter; +} + +std::string SqliteQueryHelper::CheckAndFormatSuggestIndex() const +{ + if (suggestIndex_.empty()) { + return ""; + } + IndexName indexName; + int errCode = SchemaUtils::ParseAndCheckFieldPath(suggestIndex_, indexName); + if (errCode != E_OK) { + LOGW("Check and format suggest index failed! %d", errCode); + return ""; + } + + if (!schema_.IsIndexExist(indexName)) { + LOGW("The suggest index not exist!"); + return ""; + } + return SchemaUtils::FieldPathString(indexName); +} + +std::string SqliteQueryHelper::MapKeysInSubCondition(const std::string &accessStr) const +{ + std::string resultStr = "hex(" + accessStr + "key) IN ("; + for (auto iter = keys_.begin(); iter != keys_.end(); iter++) { + if (iter != keys_.begin()) { + resultStr += ", "; + } + resultStr += "'" + DBCommon::VectorToHexString(*iter) + "' "; + } + resultStr += ")"; + return resultStr; +} + +int SqliteQueryHelper::GetSubscribeCondition(const std::string &accessStr, std::string &conditionStr) +{ + if (queryObjNodes_.empty()) { + conditionStr += " (1 = 1) "; + return E_OK; + } + conditionStr += "("; + if (hasPrefixKey_) { + conditionStr += "(hex(" + accessStr + "key) LIKE '" + DBCommon::VectorToHexString(prefixKey_) + "%')"; + } + + if (!keys_.empty()) { + if (hasPrefixKey_) { + conditionStr += " AND "; + } + conditionStr += "(" + MapKeysInSubCondition(accessStr) + ")"; + } + + bool isNeedEndBracket = FilterSymbolToAddBracketLink(conditionStr, hasPrefixKey_ || !keys_.empty()); + int errCode = E_OK; + for (const QueryObjNode &objNode : queryObjNodes_) { + SymbolType symbolType = GetSymbolType(objNode.operFlag); + if (symbolType == SPECIAL_SYMBOL && isNeedEndBracket) { + conditionStr += ") "; + isNeedEndBracket = false; + } + errCode = ParseQueryExpression(objNode, conditionStr, accessStr, false); + if (errCode != E_OK) { + conditionStr.clear(); + return errCode; + } + } + + if (isNeedEndBracket) { + conditionStr += ") "; + } + conditionStr += ")"; + return errCode; +} + +int SqliteQueryHelper::GetSubscribeSql(const std::string &subscribeId, TriggerModeEnum mode, + std::string &subscribeCondition) +{ + if (!isValid_) { + return -E_INVALID_QUERY_FORMAT; + } + int errCode = E_OK; + switch (mode) { + case TriggerModeEnum::INSERT: + errCode = GetSubscribeCondition(DBConstant::TRIGGER_REFERENCES_NEW, subscribeCondition); + break; + case TriggerModeEnum::UPDATE: + errCode = GetSubscribeCondition(DBConstant::TRIGGER_REFERENCES_OLD, subscribeCondition); + if (errCode != E_OK) { + break; + } + subscribeCondition += " OR "; + errCode = GetSubscribeCondition(DBConstant::TRIGGER_REFERENCES_NEW, subscribeCondition); + break; + case TriggerModeEnum::DELETE: + errCode = GetSubscribeCondition(DBConstant::TRIGGER_REFERENCES_OLD, subscribeCondition); + break; + default: + errCode = -INVALID_ARGS; + } + if (errCode != E_OK) { + LOGD("Get subscribe query condition failed. %d", errCode); + } + return errCode; +} + +int SqliteQueryHelper::GetRelationalMissQuerySql(const std::vector &fieldNames, std::string &sql) +{ + if (!isValid_) { + return -E_INVALID_QUERY_FORMAT; + } + + if (hasPrefixKey_) { + LOGE("For relational DB query, prefix key is not supported."); + return -E_NOT_SUPPORT; + } + + sql = GetSelectAndFromClauseForRDB(tableName_, fieldNames); + sql += GetMissQueryFlagClauseForRDB(); + sql += GetTimeRangeClauseForRDB(); + sql += "ORDER BY " + DBConstant::TIMESTAMP_ALIAS + " ASC;"; + return E_OK; +} + +int SqliteQueryHelper::GetRelationalSyncDataQuerySql(std::string &sql, bool hasSubQuery, + const std::vector &fieldNames) +{ + if (!isValid_) { + return -E_INVALID_QUERY_FORMAT; + } + + if (hasPrefixKey_) { + LOGE("For relational DB query, prefix key is not supported."); + return -E_NOT_SUPPORT; + } + + sql = AssembleSqlForSuggestIndex(GetSelectAndFromClauseForRDB(tableName_, fieldNames), GetFlagClauseForRDB()); + sql = hasSubQuery ? sql : (sql + GetTimeRangeClauseForRDB()); + + querySql_.clear(); // clear local query sql format + int errCode = ToQuerySyncSql(hasSubQuery, true); + if (errCode != E_OK) { + LOGE("To query sql fail! errCode[%d]", errCode); + return errCode; + } + sql += querySql_; + if (hasSubQuery) { + // The last timestamp in one query will be stored in continue token and used for next query. + // Therefore all query data must be ordered by timestamp. + // When there is limit in SQL, data should be ordered by key in sub query, and timestamp is ordered by outside. + sql = GetOuterQueryClauseForRDB(sql); + } + return errCode; +} + +int SqliteQueryHelper::GetRelationalMissQueryStatement(sqlite3 *dbHandle, uint64_t beginTime, uint64_t endTime, + const std::vector &fieldNames, sqlite3_stmt *&statement) +{ + std::string sql; + int errCode = GetRelationalMissQuerySql(fieldNames, sql); + if (errCode != E_OK) { + LOGE("[Query] Get SQL fail!"); + return -E_INVALID_QUERY_FORMAT; + } + + errCode = SQLiteUtils::GetStatement(dbHandle, sql, statement); + if (errCode != E_OK) { + LOGE("[Query] Get statement fail!"); + return -E_INVALID_QUERY_FORMAT; + } + + int index = 1; // begin with 1. + return BindTimeRange(statement, index, beginTime, endTime); +} + +int SqliteQueryHelper::GetRelationalQueryStatement(sqlite3 *dbHandle, uint64_t beginTime, uint64_t endTime, + const std::vector &fieldNames, sqlite3_stmt *&statement) +{ + bool hasSubQuery = false; + if (hasLimit_ || hasOrderBy_) { + hasSubQuery = true; // Need sub query. + } else { + isNeedOrderbyKey_ = false; // Need order by timestamp. + } + std::string sql; + int errCode = GetRelationalSyncDataQuerySql(sql, hasSubQuery, fieldNames); + if (errCode != E_OK) { + LOGE("[Query] Get SQL fail!"); + return -E_INVALID_QUERY_FORMAT; + } + + errCode = SQLiteUtils::GetStatement(dbHandle, sql, statement); + if (errCode != E_OK) { + LOGE("[Query] Get statement fail!"); + return -E_INVALID_QUERY_FORMAT; + } + + int index = 1; // begin with 1. + if (hasSubQuery) { + /** + * SELECT * FROM ( + * SELECT b.data_key,b.device,b.ori_device,b.timestamp as naturalbase_rdb_timestamp, + * b.wtimestamp,b.flag,b.hash_key,a.* + * FROM tableName AS a INNER JOIN naturalbase_rdb_log AS b + * ON a.rowid=b.data_key + * WHERE (b.flag&0x03=0x02) + * LIMIT ? OFFSET ? ) + * WHERE (naturalbase_rdb_timestamp>=? AND naturalbase_rdb_timestamp=? AND naturalbase_rdb_timestamp &keys, sqlite3_stmt *&statement, int &index) const +{ + if (!keys_.empty()) { + int errCode = E_OK; + for (const auto &key : keys) { + errCode = SQLiteUtils::BindBlobToStatement(statement, index, key); + if (errCode != E_OK) { + LOGE("[Query] Get statement when bind keys failed, errCode = %d", errCode); + return errCode; + } + index++; + } + } + return E_OK; +} +} \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_query_helper.h b/mock/distributeddb/storage/src/sqlite/sqlite_query_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..c2b19a8b440e2fc45be799197550c8aec3147d8e --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_query_helper.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SQLITE_QUERY_HELPER_H +#define SQLITE_QUERY_HELPER_H + +#include +#include +#include +#include + +#include "query_expression.h" +#include "schema_utils.h" +#include "sqlite_import.h" + +namespace DistributedDB { +namespace TriggerMode { +enum class TriggerModeEnum; +} +struct QueryObjInfo { + SchemaObject schema_; + std::list queryObjNodes_; + std::vector prefixKey_; + std::string suggestIndex_; + std::set keys_; + int orderByCounts_ = 0; // Record processing to which orderBy node + bool isValid_ = true; + bool hasOrderBy_ = false; + bool hasLimit_ = false; + bool hasPrefixKey_ = false; + std::string tableName_; + bool isRelationalQuery_ = false; +}; + +enum SymbolType : uint32_t { + INVALID_SYMBOL = 0x0000, + COMPARE_SYMBOL = 0x0100, // relation symbol use to compare + RELATIONAL_SYMBOL = 0x0200, + RANGE_SYMBOL = 0x0300, + PREFIXKEY_SYMBOL = 0x0400, + LOGIC_SYMBOL = 0x0500, + LINK_SYMBOL = 0x0600, // use to link relatonal symbol + SPECIAL_SYMBOL = 0x0700, // need special precess and need at the last + SUGGEST_INDEX_SYMBOL = 0x0800, + IN_KEYS_SYMBOL = 0x0900, +}; + +class SqliteQueryHelper final { +public: + explicit SqliteQueryHelper(const QueryObjInfo &info); + + // forbidden move constructor. + SqliteQueryHelper(SqliteQueryHelper &&) = delete; + SqliteQueryHelper &operator=(SqliteQueryHelper &&) = delete; + // forbidden copy constructor. + SqliteQueryHelper(const SqliteQueryHelper &) = delete; + SqliteQueryHelper &operator=(const SqliteQueryHelper &) = delete; + + ~SqliteQueryHelper() = default; + + int GetQuerySqlStatement(sqlite3 *dbHandle, bool onlyRowid, sqlite3_stmt *&statement); + int GetQuerySqlStatement(sqlite3 *dbHandle, const std::string &sql, sqlite3_stmt *&statement); + int GetCountSqlStatement(sqlite3 *dbHandle, sqlite3_stmt *&countStmt); + + // For query Sync + int GetQuerySyncStatement(sqlite3 *dbHandle, uint64_t beginTime, uint64_t endTime, sqlite3_stmt *&statement); + int GetSyncDataCheckSql(std::string &sql); + int BindSyncDataCheckStmt(sqlite3_stmt *statement, const Key &hashKey) const; + + int GetSubscribeSql(const std::string &subscribeId, TriggerMode::TriggerModeEnum mode, + std::string &subscribeCondition); + + static SymbolType GetSymbolType(const QueryObjType &queryObjType); + + // public for unit test + int GetQuerySql(std::string &sql, bool onlyRowid); + int GetCountQuerySql(std::string &sql); + + const std::string &GetTableName() + { + return tableName_; + } + + int GetRelationalMissQuerySql(const std::vector &fieldNames, std::string &sql); + int GetRelationalMissQueryStatement(sqlite3 *dbHandle, uint64_t beginTime, uint64_t endTime, + const std::vector &fieldNames, sqlite3_stmt *&statement); + int GetRelationalSyncDataQuerySql(std::string &sql, bool hasSubQuery, const std::vector &fieldNames); + int GetRelationalQueryStatement(sqlite3 *dbHandle, uint64_t beginTime, uint64_t endTime, + const std::vector &fieldNames, sqlite3_stmt *&statement); + +private: + int ToQuerySql(); + int ToQuerySyncSql(bool hasSubQuery, bool useTimestampAlias = false); + int ToGetCountSql(); + int ParseQueryExpression(const QueryObjNode &queryNode, std::string &querySql, + const std::string &accessStr = "", bool placeholder = true); + std::string MapRelationalSymbolToSql(const QueryObjNode &queryNode, bool placeholder = false) const; + std::string MapKeywordSymbolToSql(const QueryObjNode &queryNode); + std::string MapLogicSymbolToSql(const QueryObjNode &queryNode) const; + std::string MapValueToSql(const QueryObjNode &queryNode, bool placeholder) const; + std::string MapCastFuncSql(const QueryObjNode &queryNode, const std::string &accessStr = ""); + std::string MapCastTypeSql(const FieldType &type) const; + int BindFieldValue(sqlite3_stmt *statement, const QueryObjNode &queryNode, int &index) const; + bool FilterSymbolToAddBracketLink(std::string &querySql, bool isNeedLink = true) const; + std::string AssembleSqlForSuggestIndex(const std::string &baseSql, const std::string &filter) const; + std::string CheckAndFormatSuggestIndex() const; + int GetSyncDataQuerySql(std::string &sql, bool hasSubQuery); + int ParseQueryObjNodeToSQL(bool isQueryForSync); + int BindTimeRange(sqlite3_stmt *&statement, int &index, uint64_t beginTime, uint64_t endTime) const; + int BindObjNodes(sqlite3_stmt *&statement, int &index) const; + int GetSubscribeCondition(const std::string &accessStr, std::string &conditionStr); + std::string MapKeysInToSql(size_t keysNum) const; + int BindKeysToStmt(const std::set &keys, sqlite3_stmt *&countStmt, int &index) const; + + std::string MapKeysInSubCondition(const std::string &accessStr) const; // For InKeys. + // Return the left string of symbol in compare clause. + std::string GetFieldShape(const QueryObjNode &queryNode, const std::string &accessStr = ""); + + SchemaObject schema_; + std::list queryObjNodes_; + std::vector prefixKey_; + std::string suggestIndex_; + std::string tableName_; + std::set keys_; + + std::string querySql_; + std::string countSql_; + + int orderByCounts_; // Record processing to which orderBy node + bool isValid_; + bool transformed_; + bool hasOrderBy_; + bool hasLimit_; + bool isOrderByAppeared_; + bool hasPrefixKey_; + bool isNeedOrderbyKey_; // The tag field is used for prefix query filtering key sorting + bool isRelationalQuery_; +}; +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_continue_token.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_continue_token.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d585bf6362b47a946432f48b9b0b5e058b51f93f --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_continue_token.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_continue_token.h" + +namespace DistributedDB { +SQLiteSingleVerContinueToken::SQLiteSingleVerContinueToken(Timestamp begin, Timestamp end) + : timeRanges_(MulDevTimeRanges{{"", {begin, end}}}) +{} + +SQLiteSingleVerContinueToken::SQLiteSingleVerContinueToken( + const SyncTimeRange &timeRange, const QueryObject &queryObject) + : queryObject_(std::map{{"", queryObject}}), + timeRanges_(MulDevTimeRanges{{"", {timeRange.beginTime, timeRange.endTime}}}), + deleteTimeRanges_(MulDevTimeRanges{{"", {timeRange.deleteBeginTime, timeRange.deleteEndTime}}}) +{} + +SQLiteSingleVerContinueToken::SQLiteSingleVerContinueToken(MulDevTimeRanges timeRanges) + : timeRanges_(timeRanges) +{} + +SQLiteSingleVerContinueToken::~SQLiteSingleVerContinueToken() +{} + +bool SQLiteSingleVerContinueToken::CheckValid() const +{ + return ((magicBegin_ == MAGIC_BEGIN) && (magicEnd_ == MAGIC_END)); +} + +Timestamp SQLiteSingleVerContinueToken::GetQueryBeginTime() const +{ + return GetBeginTimestamp(timeRanges_); +} + +Timestamp SQLiteSingleVerContinueToken::GetQueryEndTime() const +{ + return GetEndTimestamp(timeRanges_); +} + +Timestamp SQLiteSingleVerContinueToken::GetDeletedBeginTime() const +{ + return GetBeginTimestamp(deleteTimeRanges_); +} + +Timestamp SQLiteSingleVerContinueToken::GetDeletedEndTime() const +{ + return GetEndTimestamp(deleteTimeRanges_); +} + +void SQLiteSingleVerContinueToken::SetNextBeginTime(const DeviceID &deviceID, Timestamp nextBeginTime) +{ + RemovePrevDevAndSetBeginTime(deviceID, nextBeginTime, timeRanges_); +} + +const MulDevTimeRanges& SQLiteSingleVerContinueToken::GetTimeRanges() +{ + return timeRanges_; +} + +void SQLiteSingleVerContinueToken::SetDeletedNextBeginTime(const DeviceID &deviceID, Timestamp nextBeginTime) +{ + RemovePrevDevAndSetBeginTime(deviceID, nextBeginTime, deleteTimeRanges_); +} + +const MulDevTimeRanges& SQLiteSingleVerContinueToken::GetDeletedTimeRanges() const +{ + return deleteTimeRanges_; +} + +void SQLiteSingleVerContinueToken::FinishGetQueryData() +{ + timeRanges_.clear(); +} + +void SQLiteSingleVerContinueToken::FinishGetDeletedData() +{ + deleteTimeRanges_.clear(); +} + +bool SQLiteSingleVerContinueToken::IsGetQueryDataFinished() const +{ + return timeRanges_.empty(); +} + +bool SQLiteSingleVerContinueToken::IsGetDeletedDataFinished() const +{ + return deleteTimeRanges_.empty(); +} + +bool SQLiteSingleVerContinueToken::IsQuerySync() const +{ + return !queryObject_.empty(); +} + +QueryObject SQLiteSingleVerContinueToken::GetQuery() const +{ + if (!queryObject_.empty()) { + return queryObject_.begin()->second; + } + return QueryObject{}; +} + +void SQLiteSingleVerContinueToken::RemovePrevDevAndSetBeginTime(const DeviceID &deviceID, Timestamp nextBeginTime, + MulDevTimeRanges &timeRanges) +{ + auto iter = timeRanges.find(deviceID); + if (iter == timeRanges.end()) { + return; + } + iter = timeRanges.erase(timeRanges.begin(), iter); + iter->second.first = nextBeginTime; +} + +Timestamp SQLiteSingleVerContinueToken::GetBeginTimestamp(const MulDevTimeRanges &timeRanges) const +{ + if (timeRanges.empty()) { + return 0; + } + return timeRanges.begin()->second.first; +} + +Timestamp SQLiteSingleVerContinueToken::GetEndTimestamp(const MulDevTimeRanges &timeRanges) const +{ + if (timeRanges.empty()) { + return static_cast(INT64_MAX); + } + return timeRanges.begin()->second.second; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_continue_token.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_continue_token.h new file mode 100644 index 0000000000000000000000000000000000000000..5350cd34bb058e233e15635744e0fc157d0f831f --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_continue_token.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SQLITE_SINGLE_VER_CONTINUE_TOKEN_H +#define SQLITE_SINGLE_VER_CONTINUE_TOKEN_H + +#include + +#include "db_types.h" +#include "query_object.h" +#include "single_ver_kvdb_sync_interface.h" + +namespace DistributedDB { +class SQLiteSingleVerContinueToken { +public: + // For one device. + SQLiteSingleVerContinueToken(Timestamp begin, Timestamp end); + + // For one device in query sync. + SQLiteSingleVerContinueToken(const SyncTimeRange &timeRange, const QueryObject &queryObject); + + // For multiple device. + explicit SQLiteSingleVerContinueToken(MulDevTimeRanges timeRanges); + + ~SQLiteSingleVerContinueToken(); + + /* + * function: Check the magic number at the beginning and end of the SingleVerContinueToken. + * returnValue: Return true if the begin and end magic number is OK. + * Return false if the begin or end magic number is error. + */ + bool CheckValid() const; + + Timestamp GetQueryBeginTime() const; + Timestamp GetQueryEndTime() const; + Timestamp GetDeletedBeginTime() const; + Timestamp GetDeletedEndTime() const; + + void SetNextBeginTime(const DeviceID &deviceID, Timestamp nextBeginTime); + const MulDevTimeRanges &GetTimeRanges(); + void SetDeletedNextBeginTime(const DeviceID &deviceID, Timestamp nextBeginTime); + const MulDevTimeRanges &GetDeletedTimeRanges() const; + + void FinishGetQueryData(); + void FinishGetDeletedData(); + + bool IsGetQueryDataFinished() const; + bool IsGetDeletedDataFinished() const; + + bool IsQuerySync() const; + QueryObject GetQuery() const; + +private: + void RemovePrevDevAndSetBeginTime(const DeviceID &deviceID, Timestamp nextBeginTime, MulDevTimeRanges &timeRanges); + + Timestamp GetBeginTimestamp(const MulDevTimeRanges &timeRanges) const; + Timestamp GetEndTimestamp(const MulDevTimeRanges &timeRanges) const; + + static const unsigned int MAGIC_BEGIN = 0x600D0AC7; // for token guard + static const unsigned int MAGIC_END = 0x0AC7600D; // for token guard + unsigned int magicBegin_ = MAGIC_BEGIN; + std::map queryObject_; + MulDevTimeRanges timeRanges_; + MulDevTimeRanges deleteTimeRanges_; + unsigned int magicEnd_ = MAGIC_END; +}; +} // namespace DistributedDB +#endif // SQLITE_SINGLE_VER_CONTINUE_TOKEN_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f0ff27eed147d72f26fe62507bd738f525fcdefd --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_database_upgrader.h" +#include "db_errno.h" +#include "log_print.h" +#include "version.h" +#include "db_constant.h" +#include "platform_specific.h" +#include "param_check_utils.h" +#include "runtime_context.h" + +namespace DistributedDB { +namespace { + const std::string CREATE_LOCAL_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS local_data(" \ + "key BLOB PRIMARY KEY," \ + "value BLOB," \ + "timestamp INT," \ + "hash_key BLOB);"; + + const std::string CREATE_SYNC_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS sync_data(" \ + "key BLOB NOT NULL," \ + "value BLOB," \ + "timestamp INT NOT NULL," \ + "flag INT NOT NULL," \ + "device BLOB," \ + "ori_device BLOB," \ + "hash_key BLOB PRIMARY KEY NOT NULL," \ + "w_timestamp INT);"; + + const std::string CREATE_META_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS meta_data(" \ + "key BLOB PRIMARY KEY NOT NULL," \ + "value BLOB);"; + + const std::string CREATE_SINGLE_META_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS meta.meta_data(" \ + "key BLOB PRIMARY KEY NOT NULL," \ + "value BLOB);"; + + const std::string CREATE_SYNC_TABLE_INDEX_SQL = + "CREATE INDEX IF NOT EXISTS key_index ON sync_data (key, flag);" \ + "CREATE INDEX IF NOT EXISTS time_index ON sync_data (timestamp);" \ + "CREATE INDEX IF NOT EXISTS dev_index ON sync_data (device);" \ + "CREATE INDEX IF NOT EXISTS local_hashkey_index ON local_data (hash_key);"; + + const std::string DROP_META_TABLE_SQL = "DROP TABLE IF EXISTS main.meta_data;"; + const std::string COPY_META_TABLE_SQL = "INSERT OR REPLACE INTO meta.meta_data SELECT * FROM meta_data " + "where (SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='main.meta_data') > 0;"; +} + +SQLiteSingleVerDatabaseUpgrader::SQLiteSingleVerDatabaseUpgrader(sqlite3 *db, + const SecurityOption &secopt, bool isMemDb) + : db_(db), + secOpt_(secopt), + isMemDB_(isMemDb), + isMetaUpgrade_(false) +{ +} + +SQLiteSingleVerDatabaseUpgrader::~SQLiteSingleVerDatabaseUpgrader() +{ + db_ = nullptr; +} + +int SQLiteSingleVerDatabaseUpgrader::TransferDatabasePath(const std::string &parentDir, + const OpenDbProperties &option) +{ + std::string dbFilePath = parentDir + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + std::string upgradeLockFile = parentDir + "/" + DBConstant::UPGRADE_POSTFIX; + + if (OS::CheckPathExistence(upgradeLockFile)) { + return MoveDatabaseToNewDir(parentDir, upgradeLockFile); + } + if (OS::CheckPathExistence(dbFilePath)) { + int currentVersion = 0; + int errCode = GetDbVersion(dbFilePath, option, currentVersion); + if (errCode != E_OK) { + LOGE("[SQLiteSinVerUp] Get version of old database failed"); + return errCode; + } + if (currentVersion == 0) { + LOGI("The database file has not been initialized, maybe invalid database"); + if (OS::RemoveFile(dbFilePath) != E_OK) { + LOGE("[SQLiteSinVerUp] Remove the uninitialized database failed, errno[%d]", errno); + return -E_SYSTEM_API_FAIL; + } + } + if (currentVersion >= SINGLE_VER_STORE_VERSION_V1 && currentVersion <= SINGLE_VER_STORE_VERSION_V2) { + LOGI("[SQLiteSinVerUp] Old version[%d] database exists.", currentVersion); + if (OS::CreateFileByFileName(upgradeLockFile) != E_OK) { + return -E_SYSTEM_API_FAIL; + } + return MoveDatabaseToNewDir(parentDir, upgradeLockFile); + } + } + return E_OK; +} + +int SQLiteSingleVerDatabaseUpgrader::BeginUpgrade() +{ + return SQLiteUtils::BeginTransaction(db_, TransactType::IMMEDIATE); +} + +int SQLiteSingleVerDatabaseUpgrader::EndUpgrade(bool isSuccess) +{ + if (isSuccess) { + return SQLiteUtils::CommitTransaction(db_); + } else { + int errCode = SQLiteUtils::RollbackTransaction(db_); + std::string secOptUpgradeFile = subDir_ + "/" + DBConstant::SET_SECOPT_POSTFIX; + if (errCode == E_OK && OS::CheckPathExistence(secOptUpgradeFile) && + (OS::RemoveFile(secOptUpgradeFile) != E_OK)) { + LOGW("[EndUpgrade] Delete secure upgrade file failed"); + return -E_SYSTEM_API_FAIL; + } + return errCode; + } +} + +int SQLiteSingleVerDatabaseUpgrader::GetDatabaseVersion(int &version) const +{ + return SQLiteUtils::GetVersion(db_, version); +} + +int SQLiteSingleVerDatabaseUpgrader::SetDatabaseVersion(int version) +{ + return SQLiteUtils::SetUserVer(db_, version); +} + +void SQLiteSingleVerDatabaseUpgrader::SetUpgradeSqls(int version, std::vector &sqls, + bool &isCreateUpgradeFile) const +{ + if (version == 0) { // no write version. + if ((!isMemDB_) && ParamCheckUtils::IsS3SECEOpt(secOpt_)) { + sqls = { + CREATE_LOCAL_TABLE_SQL, + CREATE_SINGLE_META_TABLE_SQL, + CREATE_SYNC_TABLE_SQL, + CREATE_SYNC_TABLE_INDEX_SQL + }; + } else { + sqls = { + CREATE_LOCAL_TABLE_SQL, + CREATE_META_TABLE_SQL, + CREATE_SYNC_TABLE_SQL, + CREATE_SYNC_TABLE_INDEX_SQL + }; + } + } else { + if (version <= SINGLE_VER_STORE_VERSION_V1) { + sqls = { + "DROP INDEX key_index;", + "CREATE INDEX IF NOT EXISTS key_index ON sync_data (key, flag);", + "ALTER TABLE sync_data ADD w_timestamp INT;", + "UPDATE sync_data SET w_timestamp=timestamp;", + "ALTER TABLE local_data ADD timestamp INT;", + "ALTER TABLE local_data ADD hash_key BLOB;", + "UPDATE local_data SET hash_key=calc_hash_key(key), timestamp=0;", + "CREATE INDEX IF NOT EXISTS local_hashkey_index ON local_data (hash_key);" + }; + } + if ((version <= SINGLE_VER_STORE_VERSION_V2 && ParamCheckUtils::IsS3SECEOpt(secOpt_)) || + (version == SINGLE_VER_STORE_VERSION_CURRENT && isMetaUpgrade_ == true)) { + sqls.push_back(CREATE_SINGLE_META_TABLE_SQL); + sqls.push_back(COPY_META_TABLE_SQL); + sqls.push_back(DROP_META_TABLE_SQL); + isCreateUpgradeFile = true; + } + } +} + +int SQLiteSingleVerDatabaseUpgrader::UpgradeFromDatabaseVersion(int version) +{ + std::vector sqls; + bool isCreateUpgradeFile = false; + LOGI("[SqlSingleUp] metaSplit[%d], secLabel[%d], secFlag[%d]", + isMetaUpgrade_, secOpt_.securityLabel, secOpt_.securityFlag); + SetUpgradeSqls(version, sqls, isCreateUpgradeFile); + for (const auto &item : sqls) { + int errCode = SQLiteUtils::ExecuteRawSQL(db_, item); + if (errCode != E_OK) { + LOGE("[SqlSingleUp][UpFrom] Execute upgrade sql failed:%d", errCode); + return errCode; + } + } + if (isCreateUpgradeFile) { + std::string secOptUpgradeFile = subDir_ + "/" + DBConstant::SET_SECOPT_POSTFIX; + if (!OS::CheckPathExistence(secOptUpgradeFile) && (OS::CreateFileByFileName(secOptUpgradeFile) != E_OK)) { + LOGE("[SqlSingleUp][UpFrom] Create s3sece flag file failed"); + return -E_SYSTEM_API_FAIL; + } + LOGD("[SqlSingleUp][UpFrom] Create s3sece mark file success"); + } + return E_OK; +} + +int SQLiteSingleVerDatabaseUpgrader::GetDbVersion(const std::string &dbPath, const OpenDbProperties &option, + int &version) +{ + OpenDbProperties optionTmp(option); + optionTmp.uri = dbPath; + sqlite3 *db = nullptr; + int errCode = SQLiteUtils::OpenDatabase(optionTmp, db); + if (errCode != E_OK) { + return errCode; + } + errCode = SQLiteUtils::GetVersion(db, version); + (void)sqlite3_close_v2(db); + db = nullptr; + return errCode; +} + +void SQLiteSingleVerDatabaseUpgrader::SetMetaUpgrade(const SecurityOption ¤tOpt, + const SecurityOption &expectOpt, const std::string &subDir) +{ + std::string secOptUpgradeFile = subDir + "/" + DBConstant::SET_SECOPT_POSTFIX; + // the same version should upgrade while user open db with s3sece. + if ((!OS::CheckPathExistence(secOptUpgradeFile)) && currentOpt.securityLabel == SecurityLabel::NOT_SET && + ParamCheckUtils::IsS3SECEOpt(expectOpt)) { + isMetaUpgrade_ = true; + } else { + isMetaUpgrade_ = false; + } +} + +void SQLiteSingleVerDatabaseUpgrader::SetSubdir(const std::string &subDir) +{ + subDir_ = subDir; +} + +int SQLiteSingleVerDatabaseUpgrader::SetPathSecOptWithCheck(const std::string &path, const SecurityOption &secOption, + const std::string &dbStore, bool isWithChecked) +{ + SecurityOption dbOpt; + std::vector dbFilePathVec {DBConstant::SQLITE_DB_EXTENSION}; + std::string dbFilePath = path + "/" + dbStore + DBConstant::SQLITE_DB_EXTENSION; + if (OS::CheckPathExistence(dbFilePath) && isWithChecked) { + int errCode = RuntimeContext::GetInstance()->GetSecurityOption(dbFilePath, dbOpt); + if (errCode != E_OK) { + LOGE("[SetPathSecOptWithCheck] GetSecurityOption failed:%d", errCode); + if (errCode == -E_NOT_SUPPORT) { + dbOpt = SecurityOption(); + } else { + return errCode; + } + } + } + + for (const auto &item : dbFilePathVec) { + std::string dbItemFilePath = path + "/" + dbStore + item; + if (!OS::CheckPathExistence(dbItemFilePath)) { + continue; + } + if (OS::CheckPathExistence(dbItemFilePath) && dbOpt.securityLabel == NOT_SET) { + int errCode = RuntimeContext::GetInstance()->SetSecurityOption(dbItemFilePath, secOption); + if (errCode != E_OK) { + LOGE("[SetPathSecOptWithCheck] SetSecurityOption failed."); + return errCode; + } + } else if (dbOpt == secOption) { + LOGI("[SetPathSecOptWithCheck] already set secoption"); + } else { + LOGE("[SetPathSecOptWithCheck] already set secoption,but different from early option."); + return -E_INVALID_ARGS; + } + } + return E_OK; +} + +int SQLiteSingleVerDatabaseUpgrader::SetSecOption(const std::string &path, const SecurityOption &secOption, + bool isWithChecked) +{ + if (!ParamCheckUtils::CheckSecOption(secOption)) { + return -E_INVALID_ARGS; + } + if (secOption.securityLabel == NOT_SET) { + return E_OK; + } + std::string secOptUpgradeFile = path + "/" + DBConstant::SET_SECOPT_POSTFIX; + if (OS::CheckPathExistence(secOptUpgradeFile) && !ParamCheckUtils::IsS3SECEOpt(secOption)) { + LOGE("[SingleVerUp][SetSec] Security option is invalid"); + return -E_INVALID_ARGS; + } + int errCode = E_OK; + if (secOption.securityLabel != NOT_SET) { + std::string mainDbPath = path + "/" + DBConstant::MAINDB_DIR; + std::string cacheDbPath = path + "/" + DBConstant::CACHEDB_DIR; + std::string metaDbPath = path + "/" + DBConstant::METADB_DIR; + errCode = SetPathSecOptWithCheck(mainDbPath, secOption, DBConstant::SINGLE_VER_DATA_STORE, isWithChecked); + if (errCode != E_OK) { + return errCode; + } + errCode = SetPathSecOptWithCheck(cacheDbPath, secOption, DBConstant::SINGLE_VER_CACHE_STORE, isWithChecked); + if (errCode != E_OK) { + LOGE("[SQLiteSingleVerDatabaseUpgrader] cacheDb SetSecurityOption failed."); + return errCode; + } + SecurityOption metaSecOpt; + metaSecOpt.securityLabel = ((secOption.securityLabel >= SecurityLabel::S2) ? + SecurityLabel::S2 : secOption.securityLabel); + errCode = SetPathSecOptWithCheck(metaDbPath, metaSecOpt, DBConstant::SINGLE_VER_META_STORE, false); + if (errCode != E_OK) { + LOGE("[SQLiteSingleVerDatabaseUpgrader] metaDb SetSecurityOption failed."); + return errCode; + } + } + if (OS::CheckPathExistence(secOptUpgradeFile) && (OS::RemoveFile(secOptUpgradeFile) != E_OK)) { + return -E_SYSTEM_API_FAIL; + } + + return errCode; +} + +int SQLiteSingleVerDatabaseUpgrader::MoveDatabaseToNewDir(const std::string &parentDir, + const std::string &upgradeLockFile) +{ + std::vector dbFilePathVec {DBConstant::SQLITE_DB_EXTENSION, ".db-wal", ".db-shm"}; + for (const auto &item : dbFilePathVec) { + std::string oldDbPath = parentDir + "/" + DBConstant::SINGLE_VER_DATA_STORE + item; + std::string currentDbPath = parentDir + "/" + DBConstant::MAINDB_DIR + "/" + + DBConstant::SINGLE_VER_DATA_STORE + item; + if (OS::CheckPathExistence(oldDbPath)) { + if (OS::RenameFilePath(oldDbPath, currentDbPath) != E_OK) { + LOGE("[SQLiteSinVerUp] Move database file to the new directory failed, errno:%d", errno); + return -E_SYSTEM_API_FAIL; + } + } + } + int errCode = OS::RemoveFile(upgradeLockFile); + if (errCode != E_OK) { + LOGE("[SQLiteSinVerUp] Remove upgrade flag file failed, errno:%d", errno); + } + return errCode; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.h new file mode 100644 index 0000000000000000000000000000000000000000..3d5190e4458590a7654ca821f24497ef6790bb76 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_SINGLE_VER_DATABASE_UPGRADER_H +#define SQLITE_SINGLE_VER_DATABASE_UPGRADER_H + +#include "macro_utils.h" +#include "sqlite_utils.h" +#include "single_ver_database_upgrader.h" + +namespace DistributedDB { +class SQLiteSingleVerDatabaseUpgrader : virtual public SingleVerDatabaseUpgrader { +public: + explicit SQLiteSingleVerDatabaseUpgrader(sqlite3 *db, const SecurityOption &secopt, bool isMemDb); + ~SQLiteSingleVerDatabaseUpgrader() override; + DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleVerDatabaseUpgrader); + + // used for transferring db file to new dir while classifycation feature in SOFTWARE_VERSION_RELEASE_3_0 + static int TransferDatabasePath(const std::string &parentDir, const OpenDbProperties &option); + static int CreateDbDir(); + + void SetMetaUpgrade(const SecurityOption ¤tOpt, const SecurityOption &expectOpt, const std::string &subDir); + void SetSubdir(const std::string &subDir); + static int SetPathSecOptWithCheck(const std::string &path, const SecurityOption &secOption, + const std::string &dbStore, bool isWithChecked = false); + static int SetSecOption(const std::string &path, const SecurityOption &secOption, bool isWithChecked); +protected: + int BeginUpgrade() override; + int EndUpgrade(bool isSuccess) override; + int GetDatabaseVersion(int &version) const override; + int SetDatabaseVersion(int version) override; + int UpgradeFromDatabaseVersion(int version) override; + void SetUpgradeSqls(int version, std::vector &sqls, bool &isCreateUpgradeFile) const; + static int MoveDatabaseToNewDir(const std::string &parentDir, const std::string &upgradeLockFile); + static int GetDbVersion(const std::string &dbPath, const OpenDbProperties &option, int &version); + + sqlite3 *db_ = nullptr; + SecurityOption secOpt_; + bool isMemDB_; + bool isMetaUpgrade_; + std::string subDir_; +}; +} // namespace DistributedDB +#endif // SQLITE_SINGLE_VER_DATABASE_UPGRADER_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc282384a5689c48eadffa0ceaba24654b3534af --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_forward_cursor.h" + +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +SQLiteSingleVerForwardCursor::SQLiteSingleVerForwardCursor(SQLiteSingleVerNaturalStore *kvDB, const Key &keyPrefix) + : kvDB_(kvDB), + keyPrefix_(keyPrefix), + handle_(nullptr), + count_(0), + isOpen_(false), + isQueryMode_(false) +{} + +SQLiteSingleVerForwardCursor::SQLiteSingleVerForwardCursor(SQLiteSingleVerNaturalStore *kvDB, + const QueryObject &queryObj) + : kvDB_(kvDB), + queryObj_(queryObj), + handle_(nullptr), + count_(0), + isOpen_(false), + isQueryMode_(true) +{} + +SQLiteSingleVerForwardCursor::~SQLiteSingleVerForwardCursor() +{ + kvDB_ = nullptr; + keyPrefix_.clear(); + handle_ = nullptr; + count_ = 0; +} + +int SQLiteSingleVerForwardCursor::Open() +{ + std::lock_guard lock(isOpenMutex_); + if (isOpen_) { + return E_OK; + } + int errCode = E_OK; + handle_ = kvDB_->GetHandle(false, errCode); + if (handle_ == nullptr) { + LOGE("Get handle failed."); + return errCode; + } + + if (isQueryMode_) { + errCode = handle_->OpenResultSet(queryObj_, count_); + } else { + errCode = handle_->OpenResultSet(keyPrefix_, count_); + } + if (errCode == E_OK) { + if (count_ == 0) { + handle_->CloseResultSet(); + kvDB_->ReleaseHandle(handle_); + } + isOpen_ = true; + } else { + handle_->CloseResultSet(); + kvDB_->ReleaseHandle(handle_); + LOGE("Handle open result set failed, errCode: %d", errCode); + } + + return errCode; +} + +void SQLiteSingleVerForwardCursor::Close() +{ + std::lock_guard lock(isOpenMutex_); + if (!isOpen_) { + return; + } + if (handle_ != nullptr) { + handle_->CloseResultSet(); + kvDB_->ReleaseHandle(handle_); + } + count_ = 0; + isOpen_ = false; +} + +int SQLiteSingleVerForwardCursor::Reload() +{ + std::lock_guard lock(isOpenMutex_); + if (!isOpen_) { + return -E_RESULT_SET_STATUS_INVALID; + } + if (count_ == 0) { + return E_OK; + } + int errCode = E_OK; + if (isQueryMode_) { + errCode = handle_->ReloadResultSet(queryObj_); + } else { + errCode = handle_->ReloadResultSet(keyPrefix_); + } + if (errCode != E_OK) { + handle_->CloseResultSet(); + kvDB_->ReleaseHandle(handle_); + isOpen_ = false; + } + return errCode; +} + +int SQLiteSingleVerForwardCursor::GetCount() const +{ + std::lock_guard lock(isOpenMutex_); + if (!isOpen_) { + return 0; + } + return count_; +} + +int SQLiteSingleVerForwardCursor::GetNext(Entry &entry, bool isCopy) const +{ + std::lock_guard lock(isOpenMutex_); + if (!isOpen_) { + return -E_RESULT_SET_STATUS_INVALID; + } + if (count_ == 0) { + return -E_RESULT_SET_EMPTY; + } + int errCode = handle_->GetNextEntryFromResultSet(entry.key, entry.value, isCopy); + if (errCode != E_OK && errCode != -E_FINISHED) { + handle_->CloseResultSet(); + kvDB_->ReleaseHandle(handle_); + isOpen_ = false; + } + return errCode; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.h new file mode 100644 index 0000000000000000000000000000000000000000..49ccdaf1d4db3443e04b8517f033a4ed87d50c24 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_SINGLE_VER_FORWARD_CURSOR_H +#define SQLITE_SINGLE_VER_FORWARD_CURSOR_H + +#include + +#include "macro_utils.h" +#include "ikvdb_raw_cursor.h" +#include "sqlite_single_ver_storage_executor.h" +#include "sqlite_single_ver_natural_store.h" + +namespace DistributedDB { +class SQLiteSingleVerForwardCursor : public IKvDBRawCursor { +public: + SQLiteSingleVerForwardCursor(SQLiteSingleVerNaturalStore *kvDB, const Key &keyPrefix); + SQLiteSingleVerForwardCursor(SQLiteSingleVerNaturalStore *kvDB, const QueryObject &queryObj); + ~SQLiteSingleVerForwardCursor() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleVerForwardCursor); + + // Open the raw cursor. + int Open() override; + + // Close the raw cursor before invoking operator delete. + void Close() override; + + // Reload all data. + int Reload() override; + + // Get total entries count. + int GetCount() const override; + + // Get next entry, return errCode if it fails. + int GetNext(Entry &entry, bool isCopy) const override; + +private: + SQLiteSingleVerNaturalStore *kvDB_; + Key keyPrefix_; + QueryObject queryObj_; + mutable SQLiteSingleVerStorageExecutor *handle_; + int count_; + mutable bool isOpen_; + bool isQueryMode_; + mutable std::mutex isOpenMutex_; +}; +} // namespace DistributedDB + +#endif // SQLITE_SINGLE_VER_FORWARD_CURSOR_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e01e97021dc2d2cbaf22975210d55d5e1a5aab4 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.cpp @@ -0,0 +1,2345 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_natural_store.h" + +#include +#include +#include + +#include "data_compression.h" +#include "db_common.h" +#include "db_constant.h" +#include "db_dump_helper.h" +#include "db_dfx_adapter.h" +#include "db_errno.h" +#include "generic_single_ver_kv_entry.h" +#include "intercepted_data_impl.h" +#include "kvdb_utils.h" +#include "log_print.h" +#include "platform_specific.h" +#include "schema_object.h" +#include "single_ver_database_oper.h" +#include "storage_engine_manager.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "value_hash_calc.h" + +namespace DistributedDB { +namespace { + constexpr int WAIT_DELEGATE_CALLBACK_TIME = 100; + + const std::string CREATE_DB_TIME = "createDBTime"; + + // Called when get multiple dev data. + // deviceID is the device which currently being getting. When getting one dev data, deviceID is "". + // dataItems is the DataItems which already be get from DB sorted by timestamp. + // token must not be null. + void ProcessContinueToken(const DeviceID &deviceID, const std::vector &dataItems, int &errCode, + SQLiteSingleVerContinueToken *&token) + { + if (errCode != -E_UNFINISHED) { // Error happened or get data finished. Token should be cleared. + delete token; + token = nullptr; + return; + } + + if (dataItems.empty()) { + errCode = -E_INTERNAL_ERROR; + LOGE("Get data unfinished but dataitems is empty."); + delete token; + token = nullptr; + return; + } + + Timestamp nextBeginTime = dataItems.back().timestamp + 1; + if (nextBeginTime > INT64_MAX) { + nextBeginTime = INT64_MAX; + } + token->SetNextBeginTime(deviceID, nextBeginTime); + return; + } + + // Called when get one dev data. + void ProcessContinueToken(const std::vector &dataItems, int &errCode, + SQLiteSingleVerContinueToken *&token) + { + ProcessContinueToken("", dataItems, errCode, token); + } + + // Called when get query sync data. + // dataItems is the DataItems which already be get from DB sorted by timestamp. + // token must not be null. + void ProcessContinueTokenForQuerySync(const std::vector &dataItems, int &errCode, + SQLiteSingleVerContinueToken *&token) + { + if (errCode != -E_UNFINISHED) { // Error happened or get data finished. Token should be cleared. + delete token; + token = nullptr; + return; + } + + if (dataItems.empty()) { + errCode = -E_INTERNAL_ERROR; + LOGE("Get data unfinished but dataitems is empty."); + delete token; + token = nullptr; + return; + } + + Timestamp nextBeginTime = dataItems.back().timestamp + 1; + if (nextBeginTime > INT64_MAX) { + nextBeginTime = INT64_MAX; + } + bool getDeleteData = ((dataItems.back().flag & DataItem::DELETE_FLAG) != 0); + if (getDeleteData) { + token->FinishGetQueryData(); + token->SetDeletedNextBeginTime("", nextBeginTime); + } else { + token->SetNextBeginTime("", nextBeginTime); + } + return; + } + + void UpdateSecProperties(KvDBProperties &properties, bool isReadOnly, const SchemaObject &savedSchemaObj, + const SQLiteSingleVerStorageEngine *engine) + { + if (isReadOnly) { + properties.SetSchema(savedSchemaObj); + properties.SetBoolProp(KvDBProperties::FIRST_OPEN_IS_READ_ONLY, true); + } + // Update the security option from the storage engine for that + // we will not update the security label and flag for the existed database. + // So the security label and flag are from the existed database. + if (engine == nullptr) { + return; + } + properties.SetIntProp(KvDBProperties::SECURITY_LABEL, engine->GetSecurityOption().securityLabel); + properties.SetIntProp(KvDBProperties::SECURITY_FLAG, engine->GetSecurityOption().securityFlag); + } + + int GetKvEntriesByDataItems(std::vector &entries, std::vector &dataItems) + { + int errCode = E_OK; + for (auto &item : dataItems) { + auto entry = new (std::nothrow) GenericSingleVerKvEntry(); + if (entry == nullptr) { + errCode = -E_OUT_OF_MEMORY; + LOGE("GetKvEntries failed, errCode:%d", errCode); + SingleVerKvEntry::Release(entries); + break; + } + entry->SetEntryData(std::move(item)); + entries.push_back(entry); + } + return errCode; + } + + bool CanHoldDeletedData(const std::vector &dataItems, const DataSizeSpecInfo &dataSizeInfo, + size_t appendLen) + { + bool reachThreshold = false; + size_t blockSize = 0; + for (size_t i = 0; !reachThreshold && i < dataItems.size(); i++) { + blockSize += SQLiteSingleVerStorageExecutor::GetDataItemSerialSize(dataItems[i], appendLen); + reachThreshold = (blockSize >= dataSizeInfo.blockSize * DBConstant::QUERY_SYNC_THRESHOLD); + } + return !reachThreshold; + } +} + +SQLiteSingleVerNaturalStore::SQLiteSingleVerNaturalStore() + : currentMaxTimestamp_(0), + storageEngine_(nullptr), + notificationEventsRegistered_(false), + notificationConflictEventsRegistered_(false), + isInitialized_(false), + isReadOnly_(false), + lifeCycleNotifier_(nullptr), + lifeTimerId_(0), + autoLifeTime_(DBConstant::DEF_LIFE_CYCLE_TIME), + createDBTime_(0), + dataInterceptor_(nullptr), + maxLogSize_(DBConstant::MAX_LOG_SIZE_DEFAULT) +{} + +SQLiteSingleVerNaturalStore::~SQLiteSingleVerNaturalStore() +{ + ReleaseResources(); +} + +std::string SQLiteSingleVerNaturalStore::GetDatabasePath(const KvDBProperties &kvDBProp) +{ + std::string filePath = GetSubDirPath(kvDBProp) + "/" + + DBConstant::MAINDB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + return filePath; +} + +std::string SQLiteSingleVerNaturalStore::GetSubDirPath(const KvDBProperties &kvDBProp) +{ + std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifierDir = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + std::string dirPath = dataDir + "/" + identifierDir + "/" + DBConstant::SINGLE_SUB_DIR; + return dirPath; +} + +int SQLiteSingleVerNaturalStore::SetUserVer(const KvDBProperties &kvDBProp, int version) +{ + OpenDbProperties properties; + properties.uri = GetDatabasePath(kvDBProp); + bool isEncryptedDb = kvDBProp.GetBoolProp(KvDBProperties::ENCRYPTED_MODE, false); + if (isEncryptedDb) { + kvDBProp.GetPassword(properties.cipherType, properties.passwd); + } + + int errCode = SQLiteUtils::SetUserVer(properties, version); + if (errCode != E_OK) { + LOGE("Recover for open db failed in single version:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::InitDatabaseContext(const KvDBProperties &kvDBProp, bool isNeedUpdateSecOpt) +{ + int errCode = InitStorageEngine(kvDBProp, isNeedUpdateSecOpt); + if (errCode != E_OK) { + return errCode; + } + InitCurrentMaxStamp(); + return errCode; +} + +int SQLiteSingleVerNaturalStore::RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) +{ + std::lock_guard lock(lifeCycleMutex_); + int errCode; + if (!notifier) { + if (lifeTimerId_ == 0) { + return E_OK; + } + errCode = StopLifeCycleTimer(); + if (errCode != E_OK) { + LOGE("Stop the life cycle timer failed:%d", errCode); + } + return E_OK; + } + + if (lifeTimerId_ != 0) { + errCode = StopLifeCycleTimer(); + if (errCode != E_OK) { + LOGE("Stop the life cycle timer failed:%d", errCode); + } + } + errCode = StartLifeCycleTimer(notifier); + if (errCode != E_OK) { + LOGE("Register life cycle timer failed:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::SetAutoLifeCycleTime(uint32_t time) +{ + std::lock_guard lock(lifeCycleMutex_); + if (lifeTimerId_ == 0) { + autoLifeTime_ = time; + } else { + auto runtimeCxt = RuntimeContext::GetInstance(); + if (runtimeCxt == nullptr) { + return -E_INVALID_ARGS; + } + LOGI("[SingleVer] Set life cycle to %u", time); + int errCode = runtimeCxt->ModifyTimer(lifeTimerId_, time); + if (errCode != E_OK) { + return errCode; + } + autoLifeTime_ = time; + } + return E_OK; +} + +int SQLiteSingleVerNaturalStore::GetSecurityOption(SecurityOption &option) const +{ + bool isMemDb = GetDbProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false); + if (isMemDb) { + LOGI("[GetSecurityOption] MemDb, no need to get security option"); + option = SecurityOption(); + return E_OK; + } + + option.securityLabel = GetDbProperties().GetSecLabel(); + option.securityFlag = GetDbProperties().GetSecFlag(); + + return E_OK; +} + +namespace { +inline bool OriValueCanBeUse(int errCode) +{ + return (errCode == -E_VALUE_MATCH); +} + +inline bool AmendValueShouldBeUse(int errCode) +{ + return (errCode == -E_VALUE_MATCH_AMENDED); +} + +inline bool IsValueMismatched(int errCode) +{ + return (errCode == -E_VALUE_MISMATCH_FEILD_COUNT || + errCode == -E_VALUE_MISMATCH_FEILD_TYPE || + errCode == -E_VALUE_MISMATCH_CONSTRAINT); +} +} + +int SQLiteSingleVerNaturalStore::CheckValueAndAmendIfNeed(ValueSource sourceType, const Value &oriValue, + Value &amendValue, bool &useAmendValue) const +{ + // oriValue size may already be checked previously, but check here const little + if (oriValue.size() > DBConstant::MAX_VALUE_SIZE) { + return -E_INVALID_ARGS; + } + const SchemaObject &schemaObjRef = MyProp().GetSchemaConstRef(); + if (!schemaObjRef.IsSchemaValid()) { + // Not a schema database, do not need to check more + return E_OK; + } + if (schemaObjRef.GetSchemaType() == SchemaType::JSON) { + ValueObject valueObj; + int errCode = valueObj.Parse(oriValue.data(), oriValue.data() + oriValue.size(), schemaObjRef.GetSkipSize()); + if (errCode != E_OK) { + return -E_INVALID_FORMAT; + } + errCode = schemaObjRef.CheckValueAndAmendIfNeed(sourceType, valueObj); + if (OriValueCanBeUse(errCode)) { + useAmendValue = false; + return E_OK; + } + if (AmendValueShouldBeUse(errCode)) { + std::string amended = valueObj.ToString(); + if (amended.size() > DBConstant::MAX_VALUE_SIZE) { + LOGE("[SqlSinStore][CheckAmendValue] ValueSize=%zu exceed limit after amend.", amended.size()); + return -E_INVALID_FORMAT; + } + amendValue.clear(); + amendValue.assign(amended.begin(), amended.end()); + useAmendValue = true; + return E_OK; + } + if (IsValueMismatched(errCode)) { + return errCode; + } + } else { + int errCode = schemaObjRef.VerifyValue(sourceType, oriValue); + if (errCode == E_OK) { + useAmendValue = false; + return E_OK; + } + } + // Any unexpected wrong + return -E_INVALID_FORMAT; +} + +int SQLiteSingleVerNaturalStore::ClearIncompleteDatabase(const KvDBProperties &kvDBPro) const +{ + std::string dbSubDir = SQLiteSingleVerNaturalStore::GetSubDirPath(kvDBPro); + if (OS::CheckPathExistence(dbSubDir + DBConstant::PATH_POSTFIX_DB_INCOMPLETE)) { + int errCode = DBCommon::RemoveAllFilesOfDirectory(dbSubDir); + if (errCode != E_OK) { + LOGE("Remove the incomplete database dir failed!"); + return -E_REMOVE_FILE; + } + } + return E_OK; +} + +int SQLiteSingleVerNaturalStore::CheckDatabaseRecovery(const KvDBProperties &kvDBProp) +{ + if (kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false)) { // memory status not need recovery + return E_OK; + } + std::unique_ptr operation = std::make_unique(this, nullptr); + (void)operation->ClearExportedTempFiles(kvDBProp); + int errCode = operation->RekeyRecover(kvDBProp); + if (errCode != E_OK) { + LOGE("Recover from rekey failed in single version:%d", errCode); + return errCode; + } + + errCode = operation->ClearImportTempFile(kvDBProp); + if (errCode != E_OK) { + LOGE("Clear imported temp db failed in single version:%d", errCode); + return errCode; + } + + // Currently, Design for the consistency of directory and file setting secOption + errCode = ClearIncompleteDatabase(kvDBProp); + if (errCode != E_OK) { + LOGE("Clear incomplete database failed in single version:%d", errCode); + return errCode; + } + const std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, ""); + const std::string identifierDir = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + bool isCreate = kvDBProp.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + bool isMemoryDb = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + if (!isMemoryDb) { + errCode = DBCommon::CreateStoreDirectory(dataDir, identifierDir, DBConstant::SINGLE_SUB_DIR, isCreate); + if (errCode != E_OK) { + LOGE("Create single version natural store directory failed:%d", errCode); + } + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetAndInitStorageEngine(const KvDBProperties &kvDBProp) +{ + int errCode = E_OK; + storageEngine_ = + static_cast(StorageEngineManager::GetStorageEngine(kvDBProp, errCode)); + if (storageEngine_ == nullptr) { + return errCode; + } + + if (storageEngine_->IsEngineCorrupted()) { + LOGE("[SqlSinStore][GetAndInitStorageEngine] database engine is corrupted, not need continue to open!"); + return -E_INVALID_PASSWD_OR_CORRUPTED_DB; + } + + errCode = InitDatabaseContext(kvDBProp); + if (errCode != E_OK) { + LOGE("[SqlSinStore][Open] Init database context fail! errCode = [%d]", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::Open(const KvDBProperties &kvDBProp) +{ + std::lock_guard lock(initialMutex_); + if (isInitialized_) { + return E_OK; // avoid the reopen operation. + } + + int errCode = CheckDatabaseRecovery(kvDBProp); + if (errCode != E_OK) { + return errCode; + } + + bool isReadOnly = false; + SchemaObject savedSchemaObj; + + errCode = GetAndInitStorageEngine(kvDBProp); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = RegisterNotification(); + if (errCode != E_OK) { + LOGE("Register notification failed:%d", errCode); + goto ERROR; + } + + errCode = RemoveAllSubscribe(); + if (errCode != E_OK) { + LOGE("[SqlSinStore][Open] remove subscribe fail! errCode = [%d]", errCode); + goto ERROR; + } + + // Here, the dbfile is created or opened, and upgrade of table structure has done. + // More, Upgrade of schema is also done in upgrader call in InitDatabaseContext, schema in dbfile updated if need. + // If inputSchema is empty, upgrader do nothing of schema, isReadOnly will be true if dbfile contain schema before. + // In this case, we should load the savedSchema for checking value from sync which not restricted by readOnly. + // If inputSchema not empty, isReadOnly will not be true, we should do nothing more. + errCode = DecideReadOnlyBaseOnSchema(kvDBProp, isReadOnly, savedSchemaObj); + if (errCode != E_OK) { + LOGE("[SqlSinStore][Open] DecideReadOnlyBaseOnSchema failed=%d", errCode); + goto ERROR; + } + // Set KvDBProperties and set Schema + MyProp() = kvDBProp; + UpdateSecProperties(MyProp(), isReadOnly, savedSchemaObj, storageEngine_); + + StartSyncer(); + OnKill([this]() { ReleaseResources(); }); + + errCode = SaveCreateDBTimeIfNotExisted(); + if (errCode != E_OK) { + goto ERROR; + } + + InitialLocalDataTimestamp(); + isInitialized_ = true; + isReadOnly_ = isReadOnly; + return E_OK; +ERROR: + ReleaseResources(); + return errCode; +} + +void SQLiteSingleVerNaturalStore::Close() +{ + ReleaseResources(); +} + +GenericKvDBConnection *SQLiteSingleVerNaturalStore::NewConnection(int &errCode) +{ + SQLiteSingleVerNaturalStoreConnection *connection = new (std::nothrow) SQLiteSingleVerNaturalStoreConnection(this); + if (connection == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + errCode = E_OK; + return connection; +} + +// Get interface type of this kvdb. +int SQLiteSingleVerNaturalStore::GetInterfaceType() const +{ + return SYNC_SVD; +} + +// Get the interface ref-count, in order to access asynchronously. +void SQLiteSingleVerNaturalStore::IncRefCount() +{ + IncObjRef(this); +} + +// Drop the interface ref-count. +void SQLiteSingleVerNaturalStore::DecRefCount() +{ + DecObjRef(this); +} + +// Get the identifier of this kvdb. +std::vector SQLiteSingleVerNaturalStore::GetIdentifier() const +{ + std::string identifier = MyProp().GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + std::vector identifierVect(identifier.begin(), identifier.end()); + return identifierVect; +} + +std::vector SQLiteSingleVerNaturalStore::GetDualTupleIdentifier() const +{ + std::string identifier = MyProp().GetStringProp(KvDBProperties::DUAL_TUPLE_IDENTIFIER_DATA, ""); + std::vector identifierVect(identifier.begin(), identifier.end()); + return identifierVect; +} + +// Get interface for syncer. +IKvDBSyncInterface *SQLiteSingleVerNaturalStore::GetSyncInterface() +{ + return this; +} + +int SQLiteSingleVerNaturalStore::GetMetaData(const Key &key, Value &value) const +{ + if (storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + if (key.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + Timestamp timestamp; + errCode = handle->GetKvData(SingleVerDataType::META_TYPE, key, value, timestamp); + ReleaseHandle(handle); + HeartBeatForLifeCycle(); + return errCode; +} + +int SQLiteSingleVerNaturalStore::PutMetaData(const Key &key, const Value &value) +{ + int errCode = SQLiteSingleVerNaturalStore::CheckDataStatus(key, value, false); + if (errCode != E_OK) { + return errCode; + } + + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->PutKvData(SingleVerDataType::META_TYPE, key, value, 0, nullptr); // meta doesn't need time. + if (errCode != E_OK) { + LOGE("Put kv data err:%d", errCode); + } + + HeartBeatForLifeCycle(); + ReleaseHandle(handle); + return errCode; +} + +// Delete multiple meta data records in a transaction. +int SQLiteSingleVerNaturalStore::DeleteMetaData(const std::vector &keys) +{ + for (const auto &key : keys) { + if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + } + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + handle->StartTransaction(TransactType::IMMEDIATE); + errCode = handle->DeleteMetaData(keys); + if (errCode != E_OK) { + handle->Rollback(); + LOGE("[SinStore] DeleteMetaData failed, errCode = %d", errCode); + } else { + handle->Commit(); + } + + ReleaseHandle(handle); + HeartBeatForLifeCycle(); + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetAllMetaKeys(std::vector &keys) const +{ + if (storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->GetAllMetaKeys(keys); + ReleaseHandle(handle); + return errCode; +} + +void SQLiteSingleVerNaturalStore::CommitAndReleaseNotifyData(SingleVerNaturalStoreCommitNotifyData *&committedData, + bool isNeedCommit, int eventType) +{ + if (isNeedCommit) { + if (committedData != nullptr) { + if (!committedData->IsChangedDataEmpty()) { + CommitNotify(eventType, committedData); + } + if (!committedData->IsConflictedDataEmpty()) { + CommitNotify(SQLITE_GENERAL_CONFLICT_EVENT, committedData); + } + } + } + + if (committedData != nullptr) { + committedData->DecObjRef(committedData); + committedData = nullptr; + } +} + +int SQLiteSingleVerNaturalStore::GetSyncData(Timestamp begin, Timestamp end, std::vector &entries, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const +{ + int errCode = CheckReadDataControlled(); + if (errCode != E_OK) { + LOGE("[GetSyncData] Existed cache database can not read data, errCode = [%d]!", errCode); + return errCode; + } + + std::vector dataItems; + errCode = GetSyncData(begin, end, dataItems, continueStmtToken, dataSizeInfo); + if (errCode != E_OK && errCode != -E_UNFINISHED) { + LOGE("GetSyncData errCode:%d", errCode); + goto ERROR; + } + + for (auto &item : dataItems) { + GenericSingleVerKvEntry *entry = new (std::nothrow) GenericSingleVerKvEntry(); + if (entry == nullptr) { + errCode = -E_OUT_OF_MEMORY; + LOGE("GetSyncData errCode:%d", errCode); + goto ERROR; + } + entry->SetEntryData(std::move(item)); + entries.push_back(entry); + } + +ERROR: + if (errCode != E_OK && errCode != -E_UNFINISHED) { + SingleVerKvEntry::Release(entries); + } + HeartBeatForLifeCycle(); + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetSyncData(Timestamp begin, Timestamp end, std::vector &dataItems, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const +{ + if (begin >= end || dataSizeInfo.blockSize > DBConstant::MAX_SYNC_BLOCK_SIZE) { + return -E_INVALID_ARGS; + } + + auto token = new (std::nothrow) SQLiteSingleVerContinueToken(begin, end); + if (token == nullptr) { + LOGE("[SQLiteSingleVerNaturalStore][NewToken] Bad alloc."); + return -E_OUT_OF_MEMORY; + } + + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(false, errCode); + if (handle == nullptr) { + goto ERROR; + } + + errCode = handle->GetSyncDataByTimestamp(dataItems, GetAppendedLen(), begin, end, dataSizeInfo); + if (errCode == -E_FINISHED) { + errCode = E_OK; + } + +ERROR: + if (errCode != -E_UNFINISHED && errCode != E_OK) { + dataItems.clear(); + } + ProcessContinueToken(dataItems, errCode, token); + continueStmtToken = static_cast(token); + + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetSyncData(QueryObject &query, const SyncTimeRange &timeRange, + const DataSizeSpecInfo &dataSizeInfo, ContinueToken &continueStmtToken, + std::vector &entries) const +{ + if (!timeRange.IsValid()) { + return -E_INVALID_ARGS; + } + int errCode = CheckReadDataControlled(); + if (errCode != E_OK) { + LOGE("[GetEntries] Existed cache prevents the reading from query sync[%d]!", errCode); + return errCode; + } + + query.SetSchema(GetSchemaObject()); + auto token = new (std::nothrow) SQLiteSingleVerContinueToken(timeRange, query); + if (token == nullptr) { + LOGE("[SingleVerNStore] Allocate continue token failed."); + return -E_OUT_OF_MEMORY; + } + + int innerCode; + std::vector dataItems; + errCode = GetSyncDataForQuerySync(dataItems, token, dataSizeInfo); + if (errCode != E_OK && errCode != -E_UNFINISHED) { // The code need be sent to outside except new error happened. + goto ERROR; + } + + innerCode = GetKvEntriesByDataItems(entries, dataItems); + if (innerCode != E_OK) { + errCode = innerCode; + delete token; + token = nullptr; + } + +ERROR: + continueStmtToken = static_cast(token); + return errCode; +} + +/** + * Caller must ensure that parameter continueStmtToken is valid. + * If error happened, token will be deleted here. + */ +int SQLiteSingleVerNaturalStore::GetSyncDataForQuerySync(std::vector &dataItems, + SQLiteSingleVerContinueToken *&continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(false, errCode); + if (handle == nullptr) { + goto ERROR; + } + + // Get query data. + if (!continueStmtToken->IsGetQueryDataFinished()) { + LOGD("[SingleVerNStore] Get query data between %" PRIu64 " and %" PRIu64 ".", + continueStmtToken->GetQueryBeginTime(), continueStmtToken->GetQueryEndTime()); + errCode = handle->GetSyncDataWithQuery(continueStmtToken->GetQuery(), GetAppendedLen(), dataSizeInfo, + std::make_pair(continueStmtToken->GetQueryBeginTime(), continueStmtToken->GetQueryEndTime()), dataItems); + } + + // Get query data finished. + if (errCode == E_OK || errCode == -E_FINISHED) { + // Clear query timeRange. + continueStmtToken->FinishGetQueryData(); + if (!continueStmtToken->IsGetDeletedDataFinished()) { + errCode = -E_UNFINISHED; + // Get delete time next. + if (CanHoldDeletedData(dataItems, dataSizeInfo, GetAppendedLen())) { + LOGD("[SingleVerNStore] Get deleted data between %" PRIu64 " and %" PRIu64 ".", + continueStmtToken->GetDeletedBeginTime(), continueStmtToken->GetDeletedEndTime()); + errCode = handle->GetDeletedSyncDataByTimestamp(dataItems, GetAppendedLen(), + continueStmtToken->GetDeletedBeginTime(), continueStmtToken->GetDeletedEndTime(), dataSizeInfo); + } + } + } + + if (errCode == -E_FINISHED) { + errCode = E_OK; + } + +ERROR: + if (errCode != -E_UNFINISHED && errCode != E_OK) { // Error happened. + dataItems.clear(); + } + ProcessContinueTokenForQuerySync(dataItems, errCode, continueStmtToken); + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetSyncDataNext(std::vector &entries, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const +{ + int errCode = CheckReadDataControlled(); + if (errCode != E_OK) { + LOGE("[GetSyncDataNext] Existed cache database can not read data, errCode = [%d]!", errCode); + return errCode; + } + + std::vector dataItems; + auto token = static_cast(continueStmtToken); + if (token->IsQuerySync()) { + errCode = GetSyncDataForQuerySync(dataItems, token, dataSizeInfo); + continueStmtToken = static_cast(token); + } else { + errCode = GetSyncDataNext(dataItems, continueStmtToken, dataSizeInfo); + } + + if (errCode != E_OK && errCode != -E_UNFINISHED) { + LOGE("GetSyncDataNext errCode:%d", errCode); + return errCode; + } + + int innerErrCode = GetKvEntriesByDataItems(entries, dataItems); + if (innerErrCode != E_OK) { + errCode = innerErrCode; + ReleaseContinueToken(continueStmtToken); + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetSyncDataNext(std::vector &dataItems, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const +{ + if (dataSizeInfo.blockSize > DBConstant::MAX_SYNC_BLOCK_SIZE) { + return -E_INVALID_ARGS; + } + + auto token = static_cast(continueStmtToken); + if (token == nullptr || !(token->CheckValid())) { + LOGE("[SingleVerNaturalStore][GetSyncDataNext] invalid continue token."); + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(false, errCode); + if (handle == nullptr) { + ReleaseContinueToken(continueStmtToken); + return errCode; + } + + errCode = handle->GetSyncDataByTimestamp(dataItems, GetAppendedLen(), token->GetQueryBeginTime(), + token->GetQueryEndTime(), dataSizeInfo); + if (errCode == -E_FINISHED) { + errCode = E_OK; + } + + ProcessContinueToken(dataItems, errCode, token); + continueStmtToken = static_cast(token); + + ReleaseHandle(handle); + return errCode; +} + +void SQLiteSingleVerNaturalStore::ReleaseContinueToken(ContinueToken &continueStmtToken) const +{ + auto token = static_cast(continueStmtToken); + if (token == nullptr || !(token->CheckValid())) { + LOGE("[SQLiteSingleVerNaturalStore][ReleaseContinueToken] Input is not a continue token."); + return; + } + delete token; + continueStmtToken = nullptr; +} + +int SQLiteSingleVerNaturalStore::PutSyncDataWithQuery(const QueryObject &query, + const std::vector &entries, const std::string &deviceName) +{ + if (deviceName.length() > DBConstant::MAX_DEV_LENGTH) { + LOGW("Device length is invalid for sync put"); + return -E_INVALID_ARGS; + } + HeartBeatForLifeCycle(); + DeviceInfo deviceInfo = {false, deviceName}; + if (deviceName.empty()) { + deviceInfo.deviceName = "Unknown"; + } + + std::vector dataItems; + for (const auto itemEntry : entries) { + auto *entry = static_cast(itemEntry); + if (entry != nullptr) { + DataItem item; + item.origDev = entry->GetOrigDevice(); + item.flag = entry->GetFlag(); + item.timestamp = entry->GetTimestamp(); + item.writeTimestamp = entry->GetWriteTimestamp(); + entry->GetKey(item.key); + entry->GetValue(item.value); + dataItems.push_back(item); + } + } + + int errCode = SaveSyncDataItems(query, dataItems, deviceInfo, true); // Current is true to check value content + if (errCode != E_OK) { + LOGE("PutSyncData failed:%d", errCode); + } + + return errCode; +} + +void SQLiteSingleVerNaturalStore::GetMaxTimestamp(Timestamp &stamp) const +{ + std::lock_guard lock(maxTimestampMutex_); + stamp = currentMaxTimestamp_; +} + +int SQLiteSingleVerNaturalStore::SetMaxTimestamp(Timestamp timestamp) +{ + std::lock_guard lock(maxTimestampMutex_); + if (timestamp > currentMaxTimestamp_) { + currentMaxTimestamp_ = timestamp; + } + return E_OK; +} + +// In sync procedure, call this function +int SQLiteSingleVerNaturalStore::RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) +{ + LOGI("[RemoveDeviceData] %s{private} rebuild, clear historydata", deviceName.c_str()); + return RemoveDeviceData(deviceName, isNeedNotify, true); +} + +// In local procedure, call this function +int SQLiteSingleVerNaturalStore::RemoveDeviceData(const std::string &deviceName, bool isNeedNotify, bool isInSync) +{ + if (deviceName.empty() || deviceName.length() > DBConstant::MAX_DEV_LENGTH) { + return -E_INVALID_ARGS; + } + if (!isInSync && !CheckWritePermission()) { + return -E_NOT_PERMIT; + } + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + LOGE("[SingleVerNStore] RemoveDeviceData get handle failed:%d", errCode); + return errCode; + } + uint64_t logFileSize = handle->GetLogFileSize(); + ReleaseHandle(handle); + if (logFileSize > GetMaxLogSize()) { + LOGW("[SingleVerNStore] RmDevData log size[%" PRIu64 "] over the limit", logFileSize); + return -E_LOG_OVER_LIMITS; + } + + // Call the syncer module to erase the water mark. + errCode = EraseDeviceWaterMark(deviceName, true); + if (errCode != E_OK) { + LOGE("[SingleVerNStore] erase water mark failed:%d", errCode); + return errCode; + } + + if (IsExtendedCacheDBMode()) { + errCode = RemoveDeviceDataInCacheMode(deviceName, isNeedNotify); + } else { + errCode = RemoveDeviceDataNormally(deviceName, isNeedNotify); + } + if (errCode != E_OK) { + LOGE("[SingleVerNStore] RemoveDeviceData failed:%d", errCode); + } + + return errCode; +} + +int SQLiteSingleVerNaturalStore::RemoveDeviceDataInCacheMode(const std::string &deviceName, bool isNeedNotify) +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + LOGE("[SingleVerNStore] RemoveDeviceData get handle failed:%d", errCode); + return errCode; + } + uint64_t recordVersion = GetAndIncreaseCacheRecordVersion(); + LOGI("Remove device data in cache mode isNeedNotify:%d, recordVersion:%" PRIu64, isNeedNotify, recordVersion); + errCode = handle->RemoveDeviceDataInCacheMode(deviceName, isNeedNotify, recordVersion); + if (errCode != E_OK) { + LOGE("[SingleVerNStore] RemoveDeviceDataInCacheMode failed:%d", errCode); + } + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::RemoveDeviceDataNormally(const std::string &deviceName, bool isNeedNotify) +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + LOGE("[SingleVerNStore] RemoveDeviceData get handle failed:%d", errCode); + return errCode; + } + + std::vector entries; + if (isNeedNotify) { + handle->GetAllSyncedEntries(deviceName, entries); + } + + LOGI("Remove device data:%d", isNeedNotify); + errCode = handle->RemoveDeviceData(deviceName); + if (errCode == E_OK && isNeedNotify) { + NotifyRemovedData(entries); + } + ReleaseHandle(handle); + return errCode; +} + +void SQLiteSingleVerNaturalStore::NotifyRemovedData(std::vector &entries) +{ + if (entries.empty() || entries.size() > MAX_TOTAL_NOTIFY_ITEM_SIZE) { + return; + } + + size_t index = 0; + size_t totalSize = 0; + SingleVerNaturalStoreCommitNotifyData *notifyData = nullptr; + while (index < entries.size()) { + if (notifyData == nullptr) { + notifyData = new (std::nothrow) SingleVerNaturalStoreCommitNotifyData; + if (notifyData == nullptr) { + LOGE("Failed to do commit sync removing because of OOM"); + break; + } + } + + // ignore the invalid key. + if (entries[index].key.size() > DBConstant::MAX_KEY_SIZE || + entries[index].value.size() > DBConstant::MAX_VALUE_SIZE) { + index++; + continue; + } + + if ((entries[index].key.size() + entries[index].value.size() + totalSize) > MAX_TOTAL_NOTIFY_DATA_SIZE) { + CommitAndReleaseNotifyData(notifyData, true, SQLITE_GENERAL_NS_SYNC_EVENT); + totalSize = 0; + notifyData = nullptr; + continue; + } + + totalSize += (entries[index].key.size() + entries[index].value.size()); + notifyData->InsertCommittedData(std::move(entries[index]), DataType::DELETE, false); + index++; + } + if (notifyData != nullptr) { + CommitAndReleaseNotifyData(notifyData, true, SQLITE_GENERAL_NS_SYNC_EVENT); + } +} + +SQLiteSingleVerStorageExecutor *SQLiteSingleVerNaturalStore::GetHandle(bool isWrite, int &errCode, + OperatePerm perm) const +{ + if (storageEngine_ == nullptr) { + errCode = -E_INVALID_DB; + return nullptr; + } + // Use for check database corrupted in Asynchronous task, like cache data migrate to main database + if (storageEngine_->IsEngineCorrupted()) { + CorruptNotify(); + errCode = -E_INVALID_PASSWD_OR_CORRUPTED_DB; + LOGI("Handle is corrupted can not to get! errCode = [%d]", errCode); + return nullptr; + } + return static_cast(storageEngine_->FindExecutor(isWrite, perm, errCode)); +} + +void SQLiteSingleVerNaturalStore::ReleaseHandle(SQLiteSingleVerStorageExecutor *&handle) const +{ + if (handle == nullptr) { + return; + } + + if (storageEngine_ != nullptr) { + bool isCorrupted = handle->GetCorruptedStatus(); + StorageExecutor *databaseHandle = handle; + storageEngine_->Recycle(databaseHandle); + handle = nullptr; + if (isCorrupted) { + CorruptNotify(); + } + } +} + +int SQLiteSingleVerNaturalStore::RegisterNotification() +{ + static const std::vector events { + static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT), + static_cast(SQLITE_GENERAL_NS_PUT_EVENT), + static_cast(SQLITE_GENERAL_NS_SYNC_EVENT), + static_cast(SQLITE_GENERAL_CONFLICT_EVENT), + }; + + for (auto event = events.begin(); event != events.end(); ++event) { + int errCode = RegisterNotificationEventType(*event); + if (errCode == E_OK) { + continue; + } + LOGE("Register single version event %d failed:%d!", *event, errCode); + for (auto iter = events.begin(); iter != event; ++iter) { + UnRegisterNotificationEventType(*iter); + } + return errCode; + } + + notificationEventsRegistered_ = true; + notificationConflictEventsRegistered_ = true; + return E_OK; +} + +void SQLiteSingleVerNaturalStore::ReleaseResources() +{ + SyncAbleKvDB::Close(); + if (notificationEventsRegistered_) { + UnRegisterNotificationEventType(static_cast(SQLITE_GENERAL_NS_SYNC_EVENT)); + UnRegisterNotificationEventType(static_cast(SQLITE_GENERAL_NS_PUT_EVENT)); + UnRegisterNotificationEventType(static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT)); + notificationEventsRegistered_ = false; + } + + if (notificationConflictEventsRegistered_) { + UnRegisterNotificationEventType(static_cast(SQLITE_GENERAL_CONFLICT_EVENT)); + notificationConflictEventsRegistered_ = false; + } + { + std::lock_guard lock(syncerMutex_); + if (storageEngine_ != nullptr) { + storageEngine_->ClearEnginePasswd(); + (void)StorageEngineManager::ReleaseStorageEngine(storageEngine_); + storageEngine_ = nullptr; + } + } + + isInitialized_ = false; +} + +void SQLiteSingleVerNaturalStore::InitCurrentMaxStamp() +{ + if (storageEngine_ == nullptr) { + return; + } + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return; + } + + handle->InitCurrentMaxStamp(currentMaxTimestamp_); + LOGD("Init max timestamp:%" PRIu64, currentMaxTimestamp_); + ReleaseHandle(handle); +} + +void SQLiteSingleVerNaturalStore::InitConflictNotifiedFlag(SingleVerNaturalStoreCommitNotifyData *committedData) +{ + unsigned int conflictFlag = 0; + if (GetRegisterFunctionCount(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ONLY) != 0) { + conflictFlag |= static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ONLY); + } + if (GetRegisterFunctionCount(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ORIG) != 0) { + conflictFlag |= static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ORIG); + } + if (GetRegisterFunctionCount(CONFLICT_SINGLE_VERSION_NS_NATIVE_ALL) != 0) { + conflictFlag |= static_cast(SQLITE_GENERAL_NS_NATIVE_ALL); + } + committedData->SetConflictedNotifiedFlag(static_cast(conflictFlag)); +} + +// Currently this function only suitable to be call from sync in insert_record_from_sync procedure +// Take attention if future coder attempt to call it in other situation procedure +int SQLiteSingleVerNaturalStore::SaveSyncDataItems(const QueryObject &query, std::vector &dataItems, + const DeviceInfo &deviceInfo, bool checkValueContent) +{ + // Sync procedure does not care readOnly Flag + if (storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = E_OK; + for (const auto &item : dataItems) { + // Check only the key and value size + errCode = CheckDataStatus(item.key, item.value, (item.flag & DataItem::DELETE_FLAG) != 0); + if (errCode != E_OK) { + return errCode; + } + } + if (checkValueContent) { + CheckAmendValueContentForSyncProcedure(dataItems); + } + QueryObject queryInner = query; + queryInner.SetSchema(GetSchemaObjectConstRef()); + if (IsExtendedCacheDBMode()) { + errCode = SaveSyncDataToCacheDB(queryInner, dataItems, deviceInfo); + } else { + errCode = SaveSyncDataToMain(queryInner, dataItems, deviceInfo); + } + if (errCode != E_OK) { + LOGE("[SingleVerNStore] SaveSyncDataItems failed:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::SaveSyncDataToMain(const QueryObject &query, std::vector &dataItems, + const DeviceInfo &deviceInfo) +{ + auto *committedData = new (std::nothrow) SingleVerNaturalStoreCommitNotifyData; + if (committedData == nullptr) { + LOGE("[SingleVerNStore] Failed to alloc single version notify data"); + return -E_OUT_OF_MEMORY; + } + InitConflictNotifiedFlag(committedData); + Timestamp maxTimestamp = 0; + bool isNeedCommit = false; + int errCode = SaveSyncItems(query, dataItems, deviceInfo, maxTimestamp, committedData); + if (errCode == E_OK) { + isNeedCommit = true; + (void)SetMaxTimestamp(maxTimestamp); + } + + CommitAndReleaseNotifyData(committedData, isNeedCommit, SQLITE_GENERAL_NS_SYNC_EVENT); + return errCode; +} + +// Currently, this function only suitable to be call from sync in insert_record_from_sync procedure +// Take attention if future coder attempt to call it in other situation procedure +int SQLiteSingleVerNaturalStore::SaveSyncItems(const QueryObject &query, std::vector &dataItems, + const DeviceInfo &deviceInfo, Timestamp &maxTimestamp, SingleVerNaturalStoreCommitNotifyData *commitData) const +{ + int errCode = E_OK; + int innerCode = E_OK; + LOGD("[SQLiteSingleVerNaturalStore::SaveSyncData] Get write handle."); + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + DBDfxAdapter::StartTraceSQL(); + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + ReleaseHandle(handle); + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + bool isPermitForceWrite = !(GetDbProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false)); + errCode = handle->CheckDataWithQuery(query, dataItems, deviceInfo); + if (errCode != E_OK) { + goto END; + } + errCode = handle->PrepareForSavingData(SingleVerDataType::SYNC_TYPE); + if (errCode != E_OK) { + goto END; + } + for (auto &item: dataItems) { + if (item.neglect) { // Do not save this record if it is neglected + continue; + } + errCode = handle->SaveSyncDataItem(item, deviceInfo, maxTimestamp, commitData, isPermitForceWrite); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + break; + } + } + if (errCode == -E_NOT_FOUND) { + errCode = E_OK; + } + innerCode = handle->ResetForSavingData(SingleVerDataType::SYNC_TYPE); + if (innerCode != E_OK) { + errCode = innerCode; + } +END: + if (errCode == E_OK) { + errCode = handle->Commit(); + } else { + (void)handle->Rollback(); // Keep the error code of the first scene + } + DBDfxAdapter::FinishTraceSQL(); + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::SaveSyncDataToCacheDB(const QueryObject &query, std::vector &dataItems, + const DeviceInfo &deviceInfo) +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + Timestamp maxTimestamp = 0; + DBDfxAdapter::StartTraceSQL(); + errCode = SaveSyncItemsInCacheMode(handle, query, dataItems, deviceInfo, maxTimestamp); + if (errCode != E_OK) { + LOGE("[SingleVerNStore] Failed to save sync data in cache mode, err : %d", errCode); + } else { + (void)SetMaxTimestamp(maxTimestamp); + } + DBDfxAdapter::FinishTraceSQL(); + ReleaseHandle(handle); + return errCode; +} + +Timestamp SQLiteSingleVerNaturalStore::GetCurrentTimestamp() +{ + return GetTimestamp(); +} + +int SQLiteSingleVerNaturalStore::InitStorageEngine(const KvDBProperties &kvDBProp, bool isNeedUpdateSecOpt) +{ + OpenDbProperties option; + InitDataBaseOption(kvDBProp, option); + + bool isMemoryMode = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + StorageEngineAttr poolSize = {1, 1, 1, 16}; // at most 1 write 16 read. + if (isMemoryMode) { + poolSize.minWriteNum = 1; // keep at least one connection. + } + + storageEngine_->SetNotifiedCallback( + [&](int eventType, KvDBCommitNotifyFilterAbleData *committedData) { + if (eventType == SQLITE_GENERAL_FINISH_MIGRATE_EVENT) { + return this->TriggerSync(eventType); + } + auto commitData = static_cast(committedData); + this->CommitAndReleaseNotifyData(commitData, true, eventType); + } + ); + + std::string identifier = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + storageEngine_->SetNeedUpdateSecOption(isNeedUpdateSecOpt); + int errCode = storageEngine_->InitSQLiteStorageEngine(poolSize, option, identifier); + if (errCode != E_OK) { + LOGE("Init the sqlite storage engine failed:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::Rekey(const CipherPassword &passwd) +{ + // Check the storage engine and try to disable the engine. + if (storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + + std::unique_ptr operation; + + // stop the syncer + int errCode = storageEngine_->TryToDisable(false, OperatePerm::REKEY_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + LOGI("Stop the syncer for rekey"); + StopSyncer(true); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); // wait for 5 ms + errCode = storageEngine_->TryToDisable(true, OperatePerm::REKEY_MONOPOLIZE_PERM); + if (errCode != E_OK) { + LOGE("[Rekey] Failed to disable the database: %d", errCode); + goto END; + } + + if (storageEngine_->GetEngineState() != EngineState::MAINDB) { + LOGE("Rekey is not supported while cache exists! state = [%d]", storageEngine_->GetEngineState()); + errCode = (storageEngine_->GetEngineState() == EngineState::CACHEDB) ? -E_NOT_SUPPORT : -E_BUSY; + goto END; + } + + operation = std::make_unique(this, storageEngine_); + LOGI("Operation rekey"); + errCode = operation->Rekey(passwd); +END: + // Only maindb state have existed handle, if rekey fail other state will create error cache db + // Abort can forbid get new handle, requesting handle will return BUSY and nullptr handle + if (errCode != -E_FORBID_CACHEDB) { + storageEngine_->Enable(OperatePerm::REKEY_MONOPOLIZE_PERM); + } else { + storageEngine_->Abort(OperatePerm::REKEY_MONOPOLIZE_PERM); + errCode = E_OK; + } + StartSyncer(); + return errCode; +} + +int SQLiteSingleVerNaturalStore::Export(const std::string &filePath, const CipherPassword &passwd) +{ + if (storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + if (MyProp().GetBoolProp(KvDBProperties::MEMORY_MODE, false)) { + return -E_NOT_SUPPORT; + } + + // Exclusively write resources + std::string localDev; + int errCode = GetLocalIdentity(localDev); + if (errCode != E_OK) { + LOGE("Get local dev id err:%d", errCode); + localDev.resize(0); + } + + // The write handle is applied to prevent writing data during the export process. + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode, OperatePerm::NORMAL_PERM); + if (handle == nullptr) { + return errCode; + } + + // forbid migrate by hold write handle not release + if (storageEngine_->GetEngineState() != EngineState::MAINDB) { + LOGE("Not support export when cacheDB existed! state = [%d]", storageEngine_->GetEngineState()); + errCode = (storageEngine_->GetEngineState() == EngineState::CACHEDB) ? -E_NOT_SUPPORT : -E_BUSY; + ReleaseHandle(handle); + return errCode; + } + + std::unique_ptr operation = std::make_unique(this, storageEngine_); + operation->SetLocalDevId(localDev); + LOGI("Begin export the kv store"); + errCode = operation->Export(filePath, passwd); + + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (storageEngine_ == nullptr) { + return -E_INVALID_DB; + } + if (MyProp().GetBoolProp(KvDBProperties::MEMORY_MODE, false)) { + return -E_NOT_SUPPORT; + } + + std::string localDev; + int errCode = GetLocalIdentity(localDev); + if (errCode != E_OK) { + LOGE("Failed to GetLocalIdentity!"); + localDev.resize(0); + } + + // stop the syncer + errCode = storageEngine_->TryToDisable(false, OperatePerm::IMPORT_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + StopSyncer(true); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); // wait for 5 ms + std::unique_ptr operation; + + errCode = storageEngine_->TryToDisable(true, OperatePerm::IMPORT_MONOPOLIZE_PERM); + if (errCode != E_OK) { + LOGE("[Import] Failed to disable the database: %d", errCode); + goto END; + } + + if (storageEngine_->GetEngineState() != EngineState::MAINDB) { + LOGE("Not support import when cacheDB existed! state = [%d]", storageEngine_->GetEngineState()); + errCode = (storageEngine_->GetEngineState() == EngineState::CACHEDB) ? -E_NOT_SUPPORT : -E_BUSY; + goto END; + } + + operation = std::make_unique(this, storageEngine_); + operation->SetLocalDevId(localDev); + errCode = operation->Import(filePath, passwd); + if (errCode != E_OK) { + goto END; + } + + // Save create db time. + storageEngine_->Enable(OperatePerm::IMPORT_MONOPOLIZE_PERM); + errCode = SaveCreateDBTime(); + +END: + // restore the storage engine and the syncer. + storageEngine_->Enable(OperatePerm::IMPORT_MONOPOLIZE_PERM); + StartSyncer(); + return errCode; +} + +bool SQLiteSingleVerNaturalStore::CheckWritePermission() const +{ + return !isReadOnly_; +} + +SchemaObject SQLiteSingleVerNaturalStore::GetSchemaInfo() const +{ + return MyProp().GetSchemaConstRef(); +} + +SchemaObject SQLiteSingleVerNaturalStore::GetSchemaObject() const +{ + return MyProp().GetSchema(); +} + +const SchemaObject &SQLiteSingleVerNaturalStore::GetSchemaObjectConstRef() const +{ + return MyProp().GetSchemaConstRef(); +} + +bool SQLiteSingleVerNaturalStore::CheckCompatible(const std::string &schema, uint8_t type) const +{ + const SchemaObject &localSchema = MyProp().GetSchemaConstRef(); + if (!localSchema.IsSchemaValid() || schema.empty() || ReadSchemaType(type) == SchemaType::NONE) { + // If at least one of local or remote is normal-kvdb, then allow sync + LOGI("IsLocalSchemaDb=%d, IsRemoteSchemaDb=%d.", localSchema.IsSchemaValid(), !schema.empty()); + return true; + } + // Here both are schema-db, check their compatibility mutually + SchemaObject remoteSchema; + int errCode = remoteSchema.ParseFromSchemaString(schema); + if (errCode != E_OK) { + // Consider: if the parse errCode is SchemaVersionNotSupport, we can consider allow sync if schemaType equal. + LOGE("Parse remote schema fail, errCode=%d.", errCode); + return false; + } + // First, Compare remoteSchema based on localSchema + errCode = localSchema.CompareAgainstSchemaObject(remoteSchema); + if (errCode != -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + LOGI("Remote(Maybe newer) compatible based on local, result=%d.", errCode); + return true; + } + // Second, Compare localSchema based on remoteSchema + errCode = remoteSchema.CompareAgainstSchemaObject(localSchema); + if (errCode != -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + LOGI("Local(Newer) compatible based on remote, result=%d.", errCode); + return true; + } + LOGE("Local incompatible with remote mutually."); + return false; +} + +void SQLiteSingleVerNaturalStore::InitDataBaseOption(const KvDBProperties &kvDBProp, OpenDbProperties &option) +{ + std::string uri = GetDatabasePath(kvDBProp); + bool isMemoryDb = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + if (isMemoryDb) { + std::string identifierDir = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + uri = identifierDir + DBConstant::SQLITE_MEMDB_IDENTIFY; + LOGD("Begin create memory natural store database"); + } + std::string subDir = GetSubDirPath(kvDBProp); + CipherType cipherType; + CipherPassword passwd; + kvDBProp.GetPassword(cipherType, passwd); + std::string schemaStr = kvDBProp.GetSchema().ToSchemaString(); + + bool isCreateNecessary = kvDBProp.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + std::vector createTableSqls; + + SecurityOption securityOpt; + if (RuntimeContext::GetInstance()->IsProcessSystemApiAdapterValid()) { + securityOpt.securityLabel = kvDBProp.GetSecLabel(); + securityOpt.securityFlag = kvDBProp.GetSecFlag(); + } + + option = {uri, isCreateNecessary, isMemoryDb, createTableSqls, cipherType, passwd, schemaStr, subDir, securityOpt}; + option.conflictReslovePolicy = kvDBProp.GetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, DEFAULT_LAST_WIN); + option.createDirByStoreIdOnly = kvDBProp.GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false); +} + +int SQLiteSingleVerNaturalStore::TransObserverTypeToRegisterFunctionType( + int observerType, RegisterFuncType &type) const +{ + static constexpr TransPair transMap[] = { + { static_cast(SQLITE_GENERAL_NS_PUT_EVENT), OBSERVER_SINGLE_VERSION_NS_PUT_EVENT }, + { static_cast(SQLITE_GENERAL_NS_SYNC_EVENT), OBSERVER_SINGLE_VERSION_NS_SYNC_EVENT }, + { static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT), OBSERVER_SINGLE_VERSION_NS_LOCAL_EVENT }, + { static_cast(SQLITE_GENERAL_CONFLICT_EVENT), OBSERVER_SINGLE_VERSION_NS_CONFLICT_EVENT }, + }; + auto funcType = GetFuncType(observerType, transMap, sizeof(transMap) / sizeof(TransPair)); + if (funcType == REGISTER_FUNC_TYPE_MAX) { + return -E_NOT_SUPPORT; + } + type = funcType; + return E_OK; +} + +int SQLiteSingleVerNaturalStore::TransConflictTypeToRegisterFunctionType( + int conflictType, RegisterFuncType &type) const +{ + static constexpr TransPair transMap[] = { + { static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ONLY), CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ONLY }, + { static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ORIG), CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ORIG }, + { static_cast(SQLITE_GENERAL_NS_NATIVE_ALL), CONFLICT_SINGLE_VERSION_NS_NATIVE_ALL }, + }; + auto funcType = GetFuncType(conflictType, transMap, sizeof(transMap) / sizeof(TransPair)); + if (funcType == REGISTER_FUNC_TYPE_MAX) { + return -E_NOT_SUPPORT; + } + type = funcType; + return E_OK; +} + +RegisterFuncType SQLiteSingleVerNaturalStore::GetFuncType(int index, const TransPair *transMap, int32_t len) +{ + int32_t head = 0; + int32_t end = len - 1; + while (head <= end) { + int32_t mid = (head + end) / 2; + if (transMap[mid].index < index) { + head = mid + 1; + continue; + } + if (transMap[mid].index > index) { + end = mid - 1; + continue; + } + return transMap[mid].funcType; + } + return REGISTER_FUNC_TYPE_MAX; +} + +int SQLiteSingleVerNaturalStore::GetSchema(SchemaObject &schema) const +{ + int errCode = E_OK; + auto handle = GetHandle(true, errCode); // Only open kvdb use, no competition for write handle + if (handle == nullptr) { + return errCode; + } + + Timestamp timestamp; + std::string schemaKey = DBConstant::SCHEMA_KEY; + Key key(schemaKey.begin(), schemaKey.end()); + Value value; + errCode = handle->GetKvData(SingleVerDataType::META_TYPE, key, value, timestamp); + if (errCode == E_OK) { + std::string schemaValue(value.begin(), value.end()); + errCode = schema.ParseFromSchemaString(schemaValue); + } else { + LOGI("[SqlSinStore] Get schema error:%d.", errCode); + } + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::DecideReadOnlyBaseOnSchema(const KvDBProperties &kvDBProp, bool &isReadOnly, + SchemaObject &savedSchemaObj) const +{ + // Check whether it is a memory db + if (kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false)) { + isReadOnly = false; + return E_OK; + } + SchemaObject inputSchemaObj = kvDBProp.GetSchema(); + if (!inputSchemaObj.IsSchemaValid()) { + int errCode = GetSchema(savedSchemaObj); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + LOGE("[SqlSinStore][DecideReadOnly] GetSchema fail=%d.", errCode); + return errCode; + } + if (savedSchemaObj.IsSchemaValid()) { + isReadOnly = true; + return E_OK; + } + } + // An valid schema will not lead to readonly + isReadOnly = false; + return E_OK; +} + +void SQLiteSingleVerNaturalStore::InitialLocalDataTimestamp() +{ + Timestamp timestamp = GetCurrentTimestamp(); + + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return; + } + + errCode = handle->UpdateLocalDataTimestamp(timestamp); + if (errCode != E_OK) { + LOGE("Update the timestamp for local data failed:%d", errCode); + } + ReleaseHandle(handle); +} + +const KvDBProperties &SQLiteSingleVerNaturalStore::GetDbProperties() const +{ + return GetMyProperties(); +} + +int SQLiteSingleVerNaturalStore::RemoveKvDB(const KvDBProperties &properties) +{ + // To avoid leakage, the engine resources are forced to be released + const std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + (void)StorageEngineManager::ForceReleaseStorageEngine(identifier); + + // Only care the data directory and the db name. + std::string storeOnlyDir; + std::string storeDir; + GenericKvDB::GetStoreDirectory(properties, KvDBProperties::SINGLE_VER_TYPE, storeDir, storeOnlyDir); + + const std::vector> dbDir { + {DBConstant::MAINDB_DIR, DBConstant::SINGLE_VER_DATA_STORE}, + {DBConstant::METADB_DIR, DBConstant::SINGLE_VER_META_STORE}, + {DBConstant::CACHEDB_DIR, DBConstant::SINGLE_VER_CACHE_STORE}}; + + bool isAllNotFound = true; + for (const auto &item : dbDir) { + std::string currentDir = storeDir + item.first + "/"; + std::string currentOnlyDir = storeOnlyDir + item.first + "/"; + int errCode = KvDBUtils::RemoveKvDB(currentDir, currentOnlyDir, item.second); + if (errCode != -E_NOT_FOUND) { + if (errCode != E_OK) { + return errCode; + } + isAllNotFound = false; + } + }; + if (isAllNotFound) { + return -E_NOT_FOUND; + } + + int errCode = DBCommon::RemoveAllFilesOfDirectory(storeDir, true); + if (errCode != E_OK) { + return errCode; + } + errCode = DBCommon::RemoveAllFilesOfDirectory(storeOnlyDir, true); + if (errCode != E_OK) { + return errCode; + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetKvDBSize(const KvDBProperties &properties, uint64_t &size) const +{ + std::string storeOnlyIdentDir; + std::string storeIdentDir; + GenericKvDB::GetStoreDirectory(properties, KvDBProperties::SINGLE_VER_TYPE, storeIdentDir, storeOnlyIdentDir); + const std::vector> dbDir { + {DBConstant::MAINDB_DIR, DBConstant::SINGLE_VER_DATA_STORE}, + {DBConstant::METADB_DIR, DBConstant::SINGLE_VER_META_STORE}, + {DBConstant::CACHEDB_DIR, DBConstant::SINGLE_VER_CACHE_STORE}}; + int errCode = -E_NOT_FOUND; + for (const auto &item : dbDir) { + std::string storeDir = storeIdentDir + item.first; + std::string storeOnlyDir = storeOnlyIdentDir + item.first; + int err = KvDBUtils::GetKvDbSize(storeDir, storeOnlyDir, item.second, size); + if (err != -E_NOT_FOUND && err != E_OK) { + return err; + } + if (err == E_OK) { + errCode = E_OK; + } + } + return errCode; +} + +KvDBProperties &SQLiteSingleVerNaturalStore::GetDbPropertyForUpdate() +{ + return MyProp(); +} + +void SQLiteSingleVerNaturalStore::HeartBeatForLifeCycle() const +{ + std::lock_guard lock(lifeCycleMutex_); + int errCode = ResetLifeCycleTimer(); + if (errCode != E_OK) { + LOGE("Heart beat for life cycle failed:%d", errCode); + } +} + +int SQLiteSingleVerNaturalStore::StartLifeCycleTimer(const DatabaseLifeCycleNotifier ¬ifier) const +{ + auto runtimeCxt = RuntimeContext::GetInstance(); + if (runtimeCxt == nullptr) { + return -E_INVALID_ARGS; + } + RefObject::IncObjRef(this); + TimerId timerId = 0; + int errCode = runtimeCxt->SetTimer(autoLifeTime_, + [this](TimerId id) -> int { + std::lock_guard lock(lifeCycleMutex_); + if (lifeCycleNotifier_) { + std::string identifier; + if (GetMyProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false)) { + identifier = GetMyProperties().GetStringProp(KvDBProperties::DUAL_TUPLE_IDENTIFIER_DATA, ""); + } else { + identifier = GetMyProperties().GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + } + auto userId = GetMyProperties().GetStringProp(DBProperties::USER_ID, ""); + lifeCycleNotifier_(identifier, userId); + } + return 0; + }, + [this]() { + int ret = RuntimeContext::GetInstance()->ScheduleTask([this]() { + RefObject::DecObjRef(this); + }); + if (ret != E_OK) { + LOGE("SQLiteSingleVerNaturalStore timer finalizer ScheduleTask, errCode %d", ret); + } + }, + timerId); + if (errCode != E_OK) { + lifeTimerId_ = 0; + LOGE("SetTimer failed:%d", errCode); + RefObject::DecObjRef(this); + return errCode; + } + + lifeCycleNotifier_ = notifier; + lifeTimerId_ = timerId; + return E_OK; +} + +int SQLiteSingleVerNaturalStore::ResetLifeCycleTimer() const +{ + if (lifeTimerId_ == 0) { + return E_OK; + } + auto lifeNotifier = lifeCycleNotifier_; + lifeCycleNotifier_ = nullptr; + int errCode = StopLifeCycleTimer(); + if (errCode != E_OK) { + LOGE("[Reset timer]Stop the life cycle timer failed:%d", errCode); + } + return StartLifeCycleTimer(lifeNotifier); +} + +int SQLiteSingleVerNaturalStore::StopLifeCycleTimer() const +{ + auto runtimeCxt = RuntimeContext::GetInstance(); + if (runtimeCxt == nullptr) { + return -E_INVALID_ARGS; + } + if (lifeTimerId_ != 0) { + TimerId timerId = lifeTimerId_; + lifeTimerId_ = 0; + runtimeCxt->RemoveTimer(timerId, false); + } + return E_OK; +} + +bool SQLiteSingleVerNaturalStore::IsDataMigrating() const +{ + if (storageEngine_ == nullptr) { + return false; + } + + if (storageEngine_->IsMigrating()) { + LOGD("Migrating now."); + return true; + } + return false; +} + +void SQLiteSingleVerNaturalStore::SetConnectionFlag(bool isExisted) const +{ + if (storageEngine_ != nullptr) { + storageEngine_->SetConnectionFlag(isExisted); + } +} + +int SQLiteSingleVerNaturalStore::TriggerToMigrateData() const +{ + RefObject::IncObjRef(this); + int errCode = RuntimeContext::GetInstance()->ScheduleTask( + std::bind(&SQLiteSingleVerNaturalStore::AsyncDataMigration, this)); + if (errCode != E_OK) { + RefObject::DecObjRef(this); + LOGE("[SingleVerNStore] Trigger to migrate data failed : %d.", errCode); + } + return errCode; +} + +bool SQLiteSingleVerNaturalStore::IsCacheDBMode() const +{ + if (storageEngine_ == nullptr) { + LOGE("[SingleVerNStore] IsCacheDBMode storage engine is invalid."); + return false; + } + EngineState engineState = storageEngine_->GetEngineState(); + return (engineState == CACHEDB); +} + +bool SQLiteSingleVerNaturalStore::IsExtendedCacheDBMode() const +{ + if (storageEngine_ == nullptr) { + LOGE("[SingleVerNStore] storage engine is invalid."); + return false; + } + EngineState engineState = storageEngine_->GetEngineState(); + return (engineState == CACHEDB || engineState == MIGRATING || engineState == ATTACHING); +} + +int SQLiteSingleVerNaturalStore::CheckReadDataControlled() const +{ + if (IsExtendedCacheDBMode()) { + int err = IsCacheDBMode() ? -E_EKEYREVOKED : -E_BUSY; + LOGE("Existed cache database can not read data, errCode = [%d]!", err); + return err; + } + return E_OK; +} + +void SQLiteSingleVerNaturalStore::IncreaseCacheRecordVersion() const +{ + if (storageEngine_ == nullptr) { + LOGE("[SingleVerNStore] Increase cache version storage engine is invalid."); + return; + } + storageEngine_->IncreaseCacheRecordVersion(); +} + +uint64_t SQLiteSingleVerNaturalStore::GetCacheRecordVersion() const +{ + if (storageEngine_ == nullptr) { + LOGE("[SingleVerNStore] Get cache version storage engine is invalid."); + return 0; + } + return storageEngine_->GetCacheRecordVersion(); +} + +uint64_t SQLiteSingleVerNaturalStore::GetAndIncreaseCacheRecordVersion() const +{ + if (storageEngine_ == nullptr) { + LOGE("[SingleVerNStore] Get cache version storage engine is invalid."); + return 0; + } + return storageEngine_->GetAndIncreaseCacheRecordVersion(); +} + +void SQLiteSingleVerNaturalStore::AsyncDataMigration() const +{ + // Delay a little time to ensure the completion of the delegate callback + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_DELEGATE_CALLBACK_TIME)); + bool isLocked = RuntimeContext::GetInstance()->IsAccessControlled(); + if (!isLocked) { + LOGI("Begin to migrate cache data to manDb asynchronously!"); + (void)StorageEngineManager::ExecuteMigration(storageEngine_); + } + + RefObject::DecObjRef(this); +} + +void SQLiteSingleVerNaturalStore::CheckAmendValueContentForSyncProcedure(std::vector &dataItems) const +{ + const SchemaObject &schemaObjRef = MyProp().GetSchemaConstRef(); + if (!schemaObjRef.IsSchemaValid()) { + // Not a schema database, do not need to check more + return; + } + uint32_t deleteCount = 0; + uint32_t amendCount = 0; + uint32_t neglectCount = 0; + for (auto &eachItem : dataItems) { + if ((eachItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG || + (eachItem.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) == DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) { + // Delete record not concerned + deleteCount++; + continue; + } + bool useAmendValue = false; + int errCode = CheckValueAndAmendIfNeed(ValueSource::FROM_SYNC, eachItem.value, eachItem.value, useAmendValue); + if (errCode != E_OK) { + eachItem.neglect = true; + neglectCount++; + continue; + } + if (useAmendValue) { + amendCount++; + } + } + LOGI("[SqlSinStore][CheckAmendForSync] OriCount=%zu, DeleteCount=%u, AmendCount=%u, NeglectCount=%u", + dataItems.size(), deleteCount, amendCount, neglectCount); +} + +int SQLiteSingleVerNaturalStore::SaveSyncItemsInCacheMode(SQLiteSingleVerStorageExecutor *handle, + const QueryObject &query, std::vector &dataItems, const DeviceInfo &deviceInfo, + Timestamp &maxTimestamp) const +{ + int errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + return errCode; + } + + int innerCode; + const uint64_t recordVersion = GetCacheRecordVersion(); + errCode = handle->PrepareForSavingCacheData(SingleVerDataType::SYNC_TYPE); + if (errCode != E_OK) { + goto END; + } + + for (auto &item : dataItems) { + errCode = handle->SaveSyncDataItemInCacheMode(item, deviceInfo, maxTimestamp, recordVersion, query); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + break; + } + } + + if (errCode == -E_NOT_FOUND) { + errCode = E_OK; + } + + innerCode = handle->ResetForSavingCacheData(SingleVerDataType::SYNC_TYPE); + if (innerCode != E_OK) { + errCode = innerCode; + } +END: + if (errCode == E_OK) { + errCode = handle->Commit(); + storageEngine_->IncreaseCacheRecordVersion(); + } else { + (void)handle->Rollback(); // Keep the error code of the first scene + } + return errCode; +} + +void SQLiteSingleVerNaturalStore::NotifyRemotePushFinished(const std::string &targetId) const +{ + std::string identifier = DBCommon::VectorToHexString(GetIdentifier()); + LOGI("label:%s sourceTarget: %s{private} push finished", identifier.c_str(), targetId.c_str()); + NotifyRemotePushFinishedInner(targetId); +} + +int SQLiteSingleVerNaturalStore::GetDatabaseCreateTimestamp(Timestamp &outTime) const +{ + // Found in memory. + { + std::lock_guard autoLock(createDBTimeMutex_); + if (createDBTime_ != 0) { + outTime = createDBTime_; + return E_OK; + } + } + + const Key key(CREATE_DB_TIME.begin(), CREATE_DB_TIME.end()); + Value value; + int errCode = GetMetaData(key, value); + if (errCode != E_OK) { + LOGD("GetDatabaseCreateTimestamp failed, errCode = %d.", errCode); + return errCode; + } + + Timestamp createDBTime = 0; + Parcel parcel(value.data(), value.size()); + (void)parcel.ReadUInt64(createDBTime); + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + outTime = createDBTime; + std::lock_guard autoLock(createDBTimeMutex_); + createDBTime_ = createDBTime; + return E_OK; +} + +int SQLiteSingleVerNaturalStore::CheckIntegrity() const +{ + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->CheckIntegrity(); + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::SaveCreateDBTime() +{ + Timestamp createDBTime = GetCurrentTimestamp(); + const Key key(CREATE_DB_TIME.begin(), CREATE_DB_TIME.end()); + Value value(Parcel::GetUInt64Len()); + Parcel parcel(value.data(), Parcel::GetUInt64Len()); + (void)parcel.WriteUInt64(createDBTime); + if (parcel.IsError()) { + LOGE("SaveCreateDBTime failed, something wrong in parcel."); + return -E_PARSE_FAIL; + } + + int errCode = PutMetaData(key, value); + if (errCode != E_OK) { + LOGE("SaveCreateDBTime failed, errCode = %d", errCode); + return errCode; + } + + // save in memory. + std::lock_guard autoLock(createDBTimeMutex_); + createDBTime_ = createDBTime; + return errCode; +} + +int SQLiteSingleVerNaturalStore::SaveCreateDBTimeIfNotExisted() +{ + Timestamp createDBTime = 0; + int errCode = GetDatabaseCreateTimestamp(createDBTime); + if (errCode == -E_NOT_FOUND) { + errCode = SaveCreateDBTime(); + } + if (errCode != E_OK) { + LOGE("SaveCreateDBTimeIfNotExisted failed, errCode=%d.", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::DeleteMetaDataByPrefixKey(const Key &keyPrefix) const +{ + if (keyPrefix.empty() || keyPrefix.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + auto handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->DeleteMetaDataByPrefixKey(keyPrefix); + if (errCode != E_OK) { + LOGE("[SinStore] DeleteMetaData by prefix key failed, errCode = %d", errCode); + } + + ReleaseHandle(handle); + HeartBeatForLifeCycle(); + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetCompressionOption(bool &needCompressOnSync, uint8_t &compressionRate) const +{ + needCompressOnSync = GetDbProperties().GetBoolProp(KvDBProperties::COMPRESS_ON_SYNC, false); + compressionRate = GetDbProperties().GetIntProp(KvDBProperties::COMPRESSION_RATE, + DBConstant::DEFAULT_COMPTRESS_RATE); + return E_OK; +} + +int SQLiteSingleVerNaturalStore::GetCompressionAlgo(std::set &algorithmSet) const +{ + algorithmSet.clear(); + DataCompression::GetCompressionAlgo(algorithmSet); + return E_OK; +} + +int SQLiteSingleVerNaturalStore::CheckAndInitQueryCondition(QueryObject &query) const +{ + const SchemaObject &localSchema = MyProp().GetSchemaConstRef(); + if (localSchema.GetSchemaType() != SchemaType::NONE && localSchema.GetSchemaType() != SchemaType::JSON) { + // Flatbuffer schema is not support subscribe + return -E_NOT_SUPPORT; + } + query.SetSchema(localSchema); + + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(false, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->CheckQueryObjectLegal(query); + if (errCode != E_OK) { + LOGE("Check query condition failed [%d]!", errCode); + } + ReleaseHandle(handle); + return errCode; +} + +void SQLiteSingleVerNaturalStore::SetDataInterceptor(const PushDataInterceptor &interceptor) +{ + std::unique_lock lock(dataInterceptorMutex_); + dataInterceptor_ = interceptor; +} + +int SQLiteSingleVerNaturalStore::InterceptData(std::vector &entries, const std::string &sourceID, + const std::string &targetID) const +{ + PushDataInterceptor interceptor = nullptr; + { + std::shared_lock lock(dataInterceptorMutex_); + if (dataInterceptor_ == nullptr) { + return E_OK; + } + interceptor = dataInterceptor_; + } + + InterceptedDataImpl data(entries, [this](const Value &newValue) -> int { + bool useAmendValue = false; + Value amendValue = newValue; + return this->CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, newValue, amendValue, useAmendValue); + } + ); + + int errCode = interceptor(data, sourceID, targetID); + if (data.IsError()) { + SingleVerKvEntry::Release(entries); + LOGE("Intercept data failed:%d.", errCode); + return -E_INTERCEPT_DATA_FAIL; + } + return E_OK; +} + +int SQLiteSingleVerNaturalStore::AddSubscribe(const std::string &subscribeId, const QueryObject &query, + bool needCacheSubscribe) +{ + const SchemaObject &localSchema = MyProp().GetSchemaConstRef(); + if (localSchema.GetSchemaType() != SchemaType::NONE && localSchema.GetSchemaType() != SchemaType::JSON) { + // Flatbuffer schema is not support subscribe + return -E_NOT_SUPPORT; + } + QueryObject queryInner = query; + queryInner.SetSchema(localSchema); + if (IsExtendedCacheDBMode() && needCacheSubscribe) { // cache auto subscribe when engine state is in CACHEDB mode + LOGI("Cache subscribe query and return ok when in cacheDB."); + storageEngine_->CacheSubscribe(subscribeId, queryInner); + return E_OK; + } + + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + ReleaseHandle(handle); + return errCode; + } + + errCode = handle->AddSubscribeTrigger(queryInner, subscribeId); + if (errCode != E_OK) { + LOGE("Add subscribe trigger failed: %d", errCode); + (void)handle->Rollback(); + } else { + errCode = handle->Commit(); + } + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::RemoveSubscribe(const std::vector &subscribeIds) +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + ReleaseHandle(handle); + return errCode; + } + errCode = handle->RemoveSubscribeTrigger(subscribeIds); + if (errCode != E_OK) { + LOGE("Remove subscribe trigger failed: %d", errCode); + goto ERR; + } + errCode = handle->RemoveSubscribeTriggerWaterMark(subscribeIds); + if (errCode != E_OK) { + LOGE("Remove subscribe data water mark failed: %d", errCode); + } +ERR: + if (errCode == E_OK) { + errCode = handle->Commit(); + } else { + (void)handle->Rollback(); + } + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::RemoveSubscribe(const std::string &subscribeId) +{ + return RemoveSubscribe(std::vector {subscribeId}); +} + +int SQLiteSingleVerNaturalStore::SetMaxLogSize(uint64_t limit) +{ + LOGI("Set the max log size to %" PRIu64, limit); + maxLogSize_.store(limit); + return E_OK; +} +uint64_t SQLiteSingleVerNaturalStore::GetMaxLogSize() const +{ + return maxLogSize_.load(); +} + +int SQLiteSingleVerNaturalStore::RemoveAllSubscribe() +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + std::vector triggers; + errCode = handle->GetTriggers(DBConstant::SUBSCRIBE_QUERY_PREFIX, triggers); + if (errCode != E_OK) { + LOGE("Get all subscribe triggers failed. %d", errCode); + ReleaseHandle(handle); + return errCode; + } + + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + ReleaseHandle(handle); + return errCode; + } + + Key prefixKey; + errCode = handle->RemoveTrigger(triggers); + if (errCode != E_OK) { + LOGE("remove all subscribe triggers failed. %d", errCode); + goto END; + } + + DBCommon::StringToVector(DBConstant::SUBSCRIBE_QUERY_PREFIX, prefixKey); + errCode = handle->DeleteMetaDataByPrefixKey(prefixKey); + if (errCode != E_OK) { + LOGE("remove all subscribe water mark failed. %d", errCode); + } +END: + if (errCode == E_OK) { + errCode = handle->Commit(); + } else { + (void)handle->Rollback(); + } + ReleaseHandle(handle); + return errCode; +} + +void SQLiteSingleVerNaturalStore::Dump(int fd) +{ + std::string userId = MyProp().GetStringProp(DBProperties::USER_ID, ""); + std::string appId = MyProp().GetStringProp(DBProperties::APP_ID, ""); + std::string storeId = MyProp().GetStringProp(DBProperties::STORE_ID, ""); + std::string label = MyProp().GetStringProp(DBProperties::IDENTIFIER_DATA, ""); + label = DBCommon::TransferStringToHex(label); + DBDumpHelper::Dump(fd, "\tdb userId = %s, appId = %s, storeId = %s, label = %s\n", + userId.c_str(), appId.c_str(), storeId.c_str(), label.c_str()); + SyncAbleKvDB::Dump(fd); +} + +DEFINE_OBJECT_TAG_FACILITIES(SQLiteSingleVerNaturalStore) +} diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.h new file mode 100644 index 0000000000000000000000000000000000000000..03ecf2012f9c928ee1e80a75b357469af6ad3edb --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.h @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SQLITE_SINGLE_VER_NATURAL_STORE_H +#define SQLITE_SINGLE_VER_NATURAL_STORE_H +#include + +#include "sync_able_kvdb.h" +#include "sqlite_single_ver_storage_engine.h" +#include "sqlite_utils.h" +#include "isyncer.h" +#include "single_ver_natural_store_commit_notify_data.h" +#include "single_ver_kvdb_sync_interface.h" +#include "kv_store_nb_conflict_data_impl.h" +#include "runtime_context.h" +#include "sqlite_single_ver_continue_token.h" + +namespace DistributedDB { +class SQLiteSingleVerNaturalStore : public SyncAbleKvDB, public SingleVerKvDBSyncInterface { +public: + SQLiteSingleVerNaturalStore(); + ~SQLiteSingleVerNaturalStore() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleVerNaturalStore); + + // Open the database + int Open(const KvDBProperties &kvDBProp) override; + + // Invoked automatically when connection count is zero + void Close() override; + + // Create a connection object. + GenericKvDBConnection *NewConnection(int &errCode) override; + + // Get interface type of this kvdb. + int GetInterfaceType() const override; + + // Get the interface ref-count, in order to access asynchronously. + void IncRefCount() override; + + // Drop the interface ref-count. + void DecRefCount() override; + + // Get the identifier of this kvdb. + std::vector GetIdentifier() const override; + // Get the dual tuple identifier of this kvdb. + std::vector GetDualTupleIdentifier() const override; + + // Get interface for syncer. + IKvDBSyncInterface *GetSyncInterface() override; + + int GetMetaData(const Key &key, Value &value) const override; + + int PutMetaData(const Key &key, const Value &value) override; + + // Delete multiple meta data records in a transaction. + int DeleteMetaData(const std::vector &keys) override; + // Delete multiple meta data records with key prefix in a transaction. + int DeleteMetaDataByPrefixKey(const Key &keyPrefix) const override; + + int GetAllMetaKeys(std::vector &keys) const override; + + int GetSyncData(Timestamp begin, Timestamp end, std::vector &dataItems, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const override; + + int GetSyncData(Timestamp begin, Timestamp end, std::vector &entries, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const override; + + int GetSyncData(QueryObject &query, const SyncTimeRange &timeRange, const DataSizeSpecInfo &dataSizeInfo, + ContinueToken &continueStmtToken, std::vector &entries) const override; + + int GetSyncDataNext(std::vector &dataItems, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const override; + + int GetSyncDataNext(std::vector &entries, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const override; + + void ReleaseContinueToken(ContinueToken &continueStmtToken) const override; + + int PutSyncDataWithQuery(const QueryObject &query, const std::vector &entries, + const std::string &deviceName) override; + + void GetMaxTimestamp(Timestamp &stamp) const override; + + int SetMaxTimestamp(Timestamp timestamp); + + int Rekey(const CipherPassword &passwd) override; + + int Export(const std::string &filePath, const CipherPassword &passwd) override; + + int Import(const std::string &filePath, const CipherPassword &passwd) override; + + // In sync procedure, call this function + int RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) override; + + // In local procedure, call this function + int RemoveDeviceData(const std::string &deviceName, bool isNeedNotify, bool isInSync); + + SQLiteSingleVerStorageExecutor *GetHandle(bool isWrite, int &errCode, + OperatePerm perm = OperatePerm::NORMAL_PERM) const; + + void ReleaseHandle(SQLiteSingleVerStorageExecutor *&handle) const; + + int TransObserverTypeToRegisterFunctionType(int observerType, RegisterFuncType &type) const override; + + int TransConflictTypeToRegisterFunctionType(int conflictType, RegisterFuncType &type) const override; + + bool CheckWritePermission() const override; + + SchemaObject GetSchemaInfo() const override; + + bool CheckCompatible(const std::string &schema, uint8_t type) const override; + + Timestamp GetCurrentTimestamp(); + + SchemaObject GetSchemaObject() const; + + const SchemaObject &GetSchemaObjectConstRef() const; + + const KvDBProperties &GetDbProperties() const override; + + int RemoveKvDB(const KvDBProperties &properties) override; + + int GetKvDBSize(const KvDBProperties &properties, uint64_t &size) const override; + KvDBProperties &GetDbPropertyForUpdate(); + + int InitDatabaseContext(const KvDBProperties &kvDBProp, bool isNeedUpdateSecOpt = false); + + int RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier); + + int SetAutoLifeCycleTime(uint32_t time); + + int GetSecurityOption(SecurityOption &option) const override; + + bool IsDataMigrating() const override; + + void SetConnectionFlag(bool isExisted) const override; + + int TriggerToMigrateData() const; + + int CheckValueAndAmendIfNeed(ValueSource sourceType, const Value &oriValue, Value &amendValue, + bool &useAmendValue) const; + + int CheckReadDataControlled() const; + bool IsCacheDBMode() const; + bool IsExtendedCacheDBMode() const; + + void IncreaseCacheRecordVersion() const; + uint64_t GetCacheRecordVersion() const; + uint64_t GetAndIncreaseCacheRecordVersion() const; + + void NotifyRemotePushFinished(const std::string &targetId) const override; + + int GetDatabaseCreateTimestamp(Timestamp &outTime) const override; + + int CheckIntegrity() const override; + + int GetCompressionOption(bool &needCompressOnSync, uint8_t &compressionRate) const override; + int GetCompressionAlgo(std::set &algorithmSet) const override; + + // Check and init query object for query sync and subscribe, flatbuffer schema will always return E_NOT_SUPPORT. + // return E_OK if subscribe is legal, ERROR on exception. + int CheckAndInitQueryCondition(QueryObject &query) const override; + + int InterceptData(std::vector &entries, const std::string &sourceID, + const std::string &targetID) const override; + + void SetDataInterceptor(const PushDataInterceptor &interceptor) override; + + int AddSubscribe(const std::string &subscribeId, const QueryObject &query, bool needCacheSubscribe) override; + + int RemoveSubscribe(const std::string &subscribeId) override; + + int RemoveSubscribe(const std::vector &subscribeIds) override; + + int SetMaxLogSize(uint64_t limit); + + uint64_t GetMaxLogSize() const; + + void Dump(int fd) override; + +private: + struct TransPair { + int index; + RegisterFuncType funcType; + }; + static RegisterFuncType GetFuncType(int index, const TransPair *transMap, int32_t len); + int CheckDatabaseRecovery(const KvDBProperties &kvDBProp); + + void CommitAndReleaseNotifyData(SingleVerNaturalStoreCommitNotifyData *&committedData, + bool isNeedCommit, int eventType); + + int RegisterNotification(); + + void ReleaseResources(); + + void InitCurrentMaxStamp(); + + int SaveSyncDataItems(const QueryObject &query, std::vector &dataItems, const DeviceInfo &deviceInfo, + bool checkValueContent); + + int InitStorageEngine(const KvDBProperties &kvDBProp, bool isNeedUpdateSecOpt); + + void InitialLocalDataTimestamp(); + + int GetSchema(SchemaObject &schema) const; + + static void InitDataBaseOption(const KvDBProperties &kvDBProp, OpenDbProperties &option); + + static int SetUserVer(const KvDBProperties &kvDBProp, int version); + + static std::string GetDatabasePath(const KvDBProperties &kvDBProp); + static std::string GetSubDirPath(const KvDBProperties &kvDBProp); + void NotifyRemovedData(std::vector &entries); + + // Decide read only based on schema situation + int DecideReadOnlyBaseOnSchema(const KvDBProperties &kvDBProp, bool &isReadOnly, + SchemaObject &savedSchemaObj) const; + + void HeartBeatForLifeCycle() const; + + int StartLifeCycleTimer(const DatabaseLifeCycleNotifier ¬ifier) const; + + int ResetLifeCycleTimer() const; + + int StopLifeCycleTimer() const; + void InitConflictNotifiedFlag(SingleVerNaturalStoreCommitNotifyData *committedData); + + void AsyncDataMigration() const; + + // Change value that should be amended, and neglect value that is incompatible + void CheckAmendValueContentForSyncProcedure(std::vector &dataItems) const; + + int RemoveDeviceDataInCacheMode(const std::string &deviceName, bool isNeedNotify); + + int RemoveDeviceDataNormally(const std::string &deviceName, bool isNeedNotify); + + int SaveSyncDataToMain(const QueryObject &query, std::vector &dataItems, const DeviceInfo &deviceInfo); + + // Currently, this function only suitable to be call from sync in insert_record_from_sync procedure + // Take attention if future coder attempt to call it in other situation procedure + int SaveSyncItems(const QueryObject& query, std::vector &dataItems, const DeviceInfo &deviceInfo, + Timestamp &maxTimestamp, SingleVerNaturalStoreCommitNotifyData *commitData) const; + + int SaveSyncDataToCacheDB(const QueryObject &query, std::vector &dataItems, + const DeviceInfo &deviceInfo); + + int SaveSyncItemsInCacheMode(SQLiteSingleVerStorageExecutor *handle, const QueryObject &query, + std::vector &dataItems, const DeviceInfo &deviceInfo, Timestamp &maxTimestamp) const; + + int ClearIncompleteDatabase(const KvDBProperties &kvDBPro) const; + + int GetSyncDataForQuerySync(std::vector &dataItems, SQLiteSingleVerContinueToken *&continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const; + + int SaveCreateDBTime(); + int SaveCreateDBTimeIfNotExisted(); + + int GetAndInitStorageEngine(const KvDBProperties &kvDBProp); + + int RemoveAllSubscribe(); + + DECLARE_OBJECT_TAG(SQLiteSingleVerNaturalStore); + + Timestamp currentMaxTimestamp_ = 0; + SQLiteSingleVerStorageEngine *storageEngine_; + bool notificationEventsRegistered_; + bool notificationConflictEventsRegistered_; + bool isInitialized_; + bool isReadOnly_; + mutable std::mutex syncerMutex_; + mutable std::mutex initialMutex_; + mutable std::mutex maxTimestampMutex_; + mutable std::mutex lifeCycleMutex_; + mutable DatabaseLifeCycleNotifier lifeCycleNotifier_; + mutable TimerId lifeTimerId_; + uint32_t autoLifeTime_; + mutable Timestamp createDBTime_; + mutable std::mutex createDBTimeMutex_; + + mutable std::shared_mutex dataInterceptorMutex_; + PushDataInterceptor dataInterceptor_; + std::atomic maxLogSize_; +}; +} +#endif diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..703ac71a54961a0d293e72b9a33ad08528193cf7 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.cpp @@ -0,0 +1,1796 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_natural_store_connection.h" + +#include "db_constant.h" +#include "db_dfx_adapter.h" +#include "db_errno.h" +#include "log_print.h" +#include "kvdb_pragma.h" +#include "sqlite_single_ver_natural_store.h" +#include "kvdb_observer_handle.h" +#include "store_types.h" +#include "db_common.h" +#include "sqlite_single_ver_result_set.h" +#include "sqlite_single_ver_forward_cursor.h" + +namespace DistributedDB { +namespace { + enum { + LOCAL_OPR_NONE = 0, + LOCAL_OPR_DEL = 1, + LOCAL_OPR_PUT = 2 + }; + const uint32_t MAX_AUTO_LIFE_CYCLE = 1800000; // half an hour. + const uint32_t MIN_AUTO_LIFE_CYCLE = 5000; // 5s. +} + +SQLiteSingleVerNaturalStoreConnection::SQLiteSingleVerNaturalStoreConnection(SQLiteSingleVerNaturalStore *kvDB) + : SyncAbleKvDBConnection(kvDB), + cacheMaxSizeForNewResultSet_(DEFAULT_RESULT_SET_CACHE_MAX_SIZE), + conflictType_(0), + transactionEntrySize_(0), + currentMaxTimestamp_(0), + committedData_(nullptr), + localCommittedData_(nullptr), + transactionExeFlag_(false), + conflictListener_(nullptr), + writeHandle_(nullptr) +{} + +SQLiteSingleVerNaturalStoreConnection::~SQLiteSingleVerNaturalStoreConnection() +{ + if (conflictListener_ != nullptr) { + conflictListener_->Drop(true); + conflictListener_ = nullptr; + } +} + +inline bool SQLiteSingleVerNaturalStoreConnection::IsFileAccessControlled() const +{ + return RuntimeContext::GetInstance()->IsAccessControlled() && + kvDB_->GetMyProperties().GetSecLabel() > SecurityLabel::S2; +} + +int SQLiteSingleVerNaturalStoreConnection::CheckReadDataControlled() const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + LOGE("[SingleVerConnection] natural store is nullptr in CheckReadDataControlled."); + return E_OK; + } + return naturalStore->CheckReadDataControlled(); +} + +int SQLiteSingleVerNaturalStoreConnection::Get(const IOption &option, const Key &key, Value &value) const +{ + if (key.size() > DBConstant::MAX_KEY_SIZE || key.empty()) { + return -E_INVALID_ARGS; + } + + SingleVerDataType dataType; + if (option.dataType == IOption::LOCAL_DATA) { + dataType = SingleVerDataType::LOCAL_TYPE; + } else if (option.dataType == IOption::SYNC_DATA) { + dataType = SingleVerDataType::SYNC_TYPE; + } else { + return -E_NOT_SUPPORT; + } + + int errCode = CheckReadDataControlled(); + if (errCode != E_OK) { + LOGE("[Get] Existed cache database can not read data, errCode = [%d]!", errCode); + return errCode; + } + + DBDfxAdapter::StartTraceSQL(); + { + // need to check if the transaction started + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + Timestamp recordTimestamp; + errCode = writeHandle_->GetKvData(dataType, key, value, recordTimestamp); + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + } + + SQLiteSingleVerStorageExecutor *handle = GetExecutor(false, errCode); + if (handle == nullptr) { + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + + Timestamp timestamp; + errCode = handle->GetKvData(dataType, key, value, timestamp); + ReleaseExecutor(handle); + DBDfxAdapter::FinishTraceSQL(); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::Put(const IOption &option, const Key &key, const Value &value) +{ + std::vector entries; + Entry entry{key, value}; + entries.emplace_back(std::move(entry)); + + return PutBatch(option, entries); +} + +int SQLiteSingleVerNaturalStoreConnection::Delete(const IOption &option, const Key &key) +{ + std::vector keys; + keys.push_back(key); + + return DeleteBatch(option, keys); +} + +int SQLiteSingleVerNaturalStoreConnection::Clear(const IOption &option) +{ + return -E_NOT_SUPPORT; +} + +int SQLiteSingleVerNaturalStoreConnection::GetEntries(const IOption &option, const Key &keyPrefix, + std::vector &entries) const +{ + if (keyPrefix.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + + SingleVerDataType type; + if (option.dataType == IOption::LOCAL_DATA) { + type = SingleVerDataType::LOCAL_TYPE; + } else if (option.dataType == IOption::SYNC_DATA) { + type = SingleVerDataType::SYNC_TYPE; + } else { + return -E_INVALID_ARGS; + } + + int errCode = CheckReadDataControlled(); + if (errCode != E_OK) { + LOGE("[GetEntries] Existed cache database can not read data, errCode = [%d]!", errCode); + return errCode; + } + + DBDfxAdapter::StartTraceSQL(); + { + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + errCode = writeHandle_->GetEntries(type, keyPrefix, entries); + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + } + + SQLiteSingleVerStorageExecutor *handle = GetExecutor(false, errCode); + if (handle == nullptr) { + LOGE("[Connection]::[GetEntries] Get executor failed, errCode = [%d]", errCode); + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + + errCode = handle->GetEntries(type, keyPrefix, entries); + ReleaseExecutor(handle); + DBDfxAdapter::FinishTraceSQL(); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::GetEntries(const IOption &option, const Query &query, + std::vector &entries) const +{ + if (option.dataType != IOption::SYNC_DATA) { + return -E_NOT_SUPPORT; + } + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + int errCode = CheckReadDataControlled(); + if (errCode != E_OK) { + LOGE("[GetEntries] Existed cache database can not read data, errCode = [%d]!", errCode); + return errCode; + } + QueryObject queryObj(query); + // In readOnly mode, forbidden all schema related query + if (CheckWritePermission() == E_OK) { + const SchemaObject &schemaObjRef = naturalStore->GetSchemaObjectConstRef(); + queryObj.SetSchema(schemaObjRef); + } + DBDfxAdapter::StartTraceSQL(); + { + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + errCode = writeHandle_->GetEntries(queryObj, entries); + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + } + + SQLiteSingleVerStorageExecutor *handle = GetExecutor(false, errCode); + if (handle == nullptr) { + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + + errCode = handle->GetEntries(queryObj, entries); + ReleaseExecutor(handle); + DBDfxAdapter::FinishTraceSQL(); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::GetCount(const IOption &option, const Query &query, int &count) const +{ + if (option.dataType != IOption::SYNC_DATA) { + return -E_NOT_SUPPORT; + } + int errCode = CheckReadDataControlled(); + if (errCode != E_OK) { + LOGE("[GetCount] Existed cache database can not read data, errCode = [%d]!", errCode); + return errCode; + } + + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + QueryObject queryObj(query); + // In readOnly mode, forbidden all schema related query + if (CheckWritePermission() == E_OK) { + const SchemaObject &schemaObjRef = naturalStore->GetSchemaObjectConstRef(); + queryObj.SetSchema(schemaObjRef); + } + DBDfxAdapter::StartTraceSQL(); + { + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + errCode = writeHandle_->GetCount(queryObj, count); + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + } + + SQLiteSingleVerStorageExecutor *handle = GetExecutor(false, errCode); + if (handle == nullptr) { + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + errCode = handle->GetCount(queryObj, count); + ReleaseExecutor(handle); + DBDfxAdapter::FinishTraceSQL(); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::PutBatch(const IOption &option, const std::vector &entries) +{ + LOGD("[PutBatch] entries size is : %zu, dataType : %d", entries.size(), option.dataType); + if (option.dataType == IOption::LOCAL_DATA) { + int retCode = CheckLocalEntriesValid(entries); + if (retCode != E_OK) { + return retCode; + } + return PutBatchInner(option, entries); + } + + if (option.dataType == IOption::SYNC_DATA) { + int errCode = CheckSyncEntriesValid(entries); + if (errCode != E_OK) { + return errCode; + } + return PutBatchInner(option, entries); + } + + return -E_NOT_SUPPORT; +} + +int SQLiteSingleVerNaturalStoreConnection::DeleteBatch(const IOption &option, const std::vector &keys) +{ + LOGD("[DeleteBatch] keys size is : %zu, dataType : %d", keys.size(), option.dataType); + if (option.dataType == IOption::LOCAL_DATA) { + int retCode = CheckLocalKeysValid(keys); + if (retCode != E_OK) { + return retCode; + } + return DeleteBatchInner(option, keys); + } + + if (option.dataType == IOption::SYNC_DATA) { + int errCode = CheckSyncKeysValid(keys); + if (errCode != E_OK) { + return errCode; + } + return DeleteBatchInner(option, keys); + } + + return -E_NOT_SUPPORT; +} + +int SQLiteSingleVerNaturalStoreConnection::GetSnapshot(IKvDBSnapshot *&snapshot) const +{ + return -E_NOT_SUPPORT; +} + +void SQLiteSingleVerNaturalStoreConnection::ReleaseSnapshot(IKvDBSnapshot *&snapshot) +{} + +int SQLiteSingleVerNaturalStoreConnection::StartTransaction() +{ + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + return -E_TRANSACT_STATE; + } + + int errCode = StartTransactionInner(); + if (errCode == E_OK) { + transactionExeFlag_.store(true); + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::Commit() +{ + std::lock_guard lock(transactionMutex_); + if (writeHandle_ == nullptr) { + LOGE("single version database is null or the transaction has not been started"); + return -E_INVALID_DB; + } + + int errCode = CommitInner(); + if (errCode == E_OK) { + transactionExeFlag_.store(false); + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::RollBack() +{ + std::lock_guard lock(transactionMutex_); + if (writeHandle_ == nullptr) { + LOGE("Invalid handle for rollback or the transaction has not been started."); + return -E_INVALID_DB; + } + + int errCode = RollbackInner(); + if (errCode == E_OK) { + transactionExeFlag_.store(false); + } + return errCode; +} + +bool SQLiteSingleVerNaturalStoreConnection::IsTransactionStarted() const +{ + return transactionExeFlag_.load(); +} + +int SQLiteSingleVerNaturalStoreConnection::Pragma(int cmd, void *parameter) +{ + int errCode = E_OK; + switch (cmd) { + case PRAGMA_RM_DEVICE_DATA: { + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + auto deviceName = static_cast(parameter); + errCode = naturalStore->RemoveDeviceData(*deviceName, false, false); + break; + } + case PRAGMA_GET_IDENTIFIER_OF_DEVICE: { + if (parameter == nullptr) { + return -E_INVALID_ARGS; + } + return CalcHashDevID(*(static_cast(parameter))); + } + case PRAGMA_GET_DEVICE_IDENTIFIER_OF_ENTRY: + return GetDeviceIdentifier(static_cast(parameter)); + case PRAGMA_PUBLISH_LOCAL: + return PragmaPublish(parameter); + case PRAGMA_UNPUBLISH_SYNC: + errCode = PragmaUnpublish(parameter); + break; + case PRAGMA_SET_AUTO_LIFE_CYCLE: + return PragmaSetAutoLifeCycle(static_cast(parameter)); + case PRAGMA_RESULT_SET_CACHE_MODE: + return PragmaResultSetCacheMode(parameter); + case PRAGMA_RESULT_SET_CACHE_MAX_SIZE: + return PragmaResultSetCacheMaxSize(parameter); + case PRAGMA_TRIGGER_TO_MIGRATE_DATA: + return PragmaTriggerToMigrateData(*static_cast(parameter)); + case PRAGMA_SET_MAX_LOG_LIMIT: + return PragmaSetMaxLogSize(static_cast(parameter)); + case PRAGMA_EXEC_CHECKPOINT: + return ForceCheckPoint(); + default: + // Call Pragma() of super class. + errCode = SyncAbleKvDBConnection::Pragma(cmd, parameter); + break; + } + + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::TranslateObserverModeToEventTypes(unsigned mode, + std::list &eventTypes) const +{ + int errCode = E_OK; + switch (mode) { + case static_cast(SQLITE_GENERAL_NS_PUT_EVENT): + eventTypes.push_back(SQLITE_GENERAL_NS_PUT_EVENT); + break; + case static_cast(SQLITE_GENERAL_NS_SYNC_EVENT): + eventTypes.push_back(SQLITE_GENERAL_NS_SYNC_EVENT); + break; + case (static_cast(SQLITE_GENERAL_NS_PUT_EVENT) + | static_cast(SQLITE_GENERAL_NS_SYNC_EVENT)): + eventTypes.push_back(SQLITE_GENERAL_NS_PUT_EVENT); + eventTypes.push_back(SQLITE_GENERAL_NS_SYNC_EVENT); + break; + case static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT): + eventTypes.push_back(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT); + break; + default: + errCode = -E_NOT_SUPPORT; + break; + } + return errCode; +} + +void SQLiteSingleVerNaturalStoreConnection::ClearConflictNotifierCount() +{ + uint32_t conflictType = static_cast(conflictType_); + if ((conflictType & static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ONLY)) != 0) { + (void)kvDB_->UnregisterFunction(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ONLY); + } + if ((conflictType & static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ORIG)) != 0) { + (void)kvDB_->UnregisterFunction(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ORIG); + } + if ((conflictType & static_cast(SQLITE_GENERAL_NS_NATIVE_ALL)) != 0) { + (void)kvDB_->UnregisterFunction(CONFLICT_SINGLE_VERSION_NS_NATIVE_ALL); + } + return; +} + +void SQLiteSingleVerNaturalStoreConnection::ResetConflictNotifierCount(int target) +{ + // Clear the old conflict type function. + ClearConflictNotifierCount(); + + LOGD("Conflict type:%d to %d", conflictType_, target); + // Add the new conflict type function. + AddConflictNotifierCount(target); + conflictType_ = target; +} + +void SQLiteSingleVerNaturalStoreConnection::AddConflictNotifierCount(int target) +{ + LOGD("Conflict type:%u vs %u", conflictType_, target); + // Add the new conflict type function. + uint32_t targetTemp = static_cast(target); + if ((targetTemp & static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ONLY)) != 0) { + (void)kvDB_->RegisterFunction(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ONLY); + } + if ((targetTemp & static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ORIG)) != 0) { + (void)kvDB_->RegisterFunction(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ORIG); + } + if ((targetTemp & static_cast(SQLITE_GENERAL_NS_NATIVE_ALL)) != 0) { + (void)kvDB_->RegisterFunction(CONFLICT_SINGLE_VERSION_NS_NATIVE_ALL); + } +} + +int SQLiteSingleVerNaturalStoreConnection::SetConflictNotifier(int conflictType, + const KvDBConflictAction &action) +{ + std::lock_guard lock(conflictMutex_); + if (!action && conflictListener_ == nullptr) { + return -E_INVALID_ARGS; + } + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + + // prevent the rekey operation. + if (isExclusive_.load()) { + return -E_BUSY; + } + + int targetType = 0; + NotificationChain::Listener *listener = nullptr; + if (action) { + int errCode = E_OK; + Key key; + listener = RegisterSpecialListener(SQLITE_GENERAL_CONFLICT_EVENT, key, action, true, errCode); + if (listener == nullptr) { + LOGE("Register Conflict listener failed:'%d'.", errCode); + return errCode; + } + targetType = conflictType; + } + + ResetConflictNotifierCount(targetType); + // drop the old listener. + if (conflictListener_ != nullptr) { + conflictListener_->Drop(true); + } + conflictListener_ = listener; + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::Rekey(const CipherPassword &passwd) +{ + if (IsFileAccessControlled()) { + LOGE("Forbid Rekey when screen locked and security label [%d]!", kvDB_->GetMyProperties().GetSecLabel()); + return -E_NOT_SUPPORT; + } + std::lock_guard lock(rekeyMutex_); + int errCode = CheckMonoStatus(OperatePerm::REKEY_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + LOGI("Begin rekey operation"); + errCode = kvDB_->Rekey(passwd); + GenericKvDBConnection::ResetExclusiveStatus(); + kvDB_->ReEnableConnection(OperatePerm::REKEY_MONOPOLIZE_PERM); + EnableManualSync(); + LOGI("End rekey operation errCode = [%d]", errCode); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::Export(const std::string &filePath, const CipherPassword &passwd) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + + if (IsFileAccessControlled()) { + LOGE("Forbid Export when screen locked and security label [%d] file lock state [%d]", + kvDB_->GetMyProperties().GetSecLabel(), RuntimeContext::GetInstance()->IsAccessControlled()); + return -E_NOT_SUPPORT; + } // Avoid abnormal branch handling without affecting the business + return kvDB_->Export(filePath, passwd); +} + +int SQLiteSingleVerNaturalStoreConnection::Import(const std::string &filePath, const CipherPassword &passwd) +{ + if (IsFileAccessControlled()) { + LOGE("Forbid Import when screen locked and security label [%d]!", kvDB_->GetMyProperties().GetSecLabel()); + return -E_NOT_SUPPORT; + } + + std::lock_guard lock(importMutex_); + int errCode = CheckMonoStatus(OperatePerm::IMPORT_MONOPOLIZE_PERM); + if (errCode != E_OK) { + return errCode; + } + errCode = kvDB_->Import(filePath, passwd); + GenericKvDBConnection::ResetExclusiveStatus(); + kvDB_->ReEnableConnection(OperatePerm::IMPORT_MONOPOLIZE_PERM); + EnableManualSync(); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::GetResultSet(const IOption &option, const Key &keyPrefix, + IKvDBResultSet *&resultSet) const +{ + // need to check if the transaction started + if (transactionExeFlag_.load()) { + LOGD("Transaction started already."); + return -E_BUSY; + } + + // maximum of result set size is 4 + std::lock_guard lock(kvDbResultSetsMutex_); + if (kvDbResultSets_.size() >= MAX_RESULT_SET_SIZE) { + LOGE("Over max result set size"); + return -E_MAX_LIMITS; + } + + int errCode = CheckReadDataControlled(); + if (errCode != E_OK) { + LOGE("[GetResultSet][keyPrefix] Existed cache database can not read data, errCode = [%d]!", errCode); + return errCode; + } + + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + bool isMemDb = naturalStore->GetMyProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false); + resultSet = new (std::nothrow) SQLiteSingleVerResultSet(naturalStore, keyPrefix, + SQLiteSingleVerResultSet::Option{cacheModeForNewResultSet_.load(), cacheMaxSizeForNewResultSet_.load()}); + if (resultSet == nullptr) { + LOGE("Create single version result set failed."); + return -E_OUT_OF_MEMORY; + } + errCode = resultSet->Open(isMemDb); + if (errCode != E_OK) { + delete resultSet; + resultSet = nullptr; + LOGE("Open result set failed."); + return errCode; + } + kvDbResultSets_.insert(resultSet); + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::GetResultSet(const IOption &option, const Query &query, + IKvDBResultSet *&resultSet) const +{ + // need to check if the transaction started + if (transactionExeFlag_.load()) { + LOGD("Transaction started already."); + return -E_BUSY; + } + + // maximum of result set size is 4 + std::lock_guard lock(kvDbResultSetsMutex_); + if (kvDbResultSets_.size() >= MAX_RESULT_SET_SIZE) { + LOGE("Over max result set size"); + return -E_MAX_LIMITS; + } + + int errCode = CheckReadDataControlled(); + if (errCode != E_OK) { + LOGE("[GetResultSet][query] Existed cache database can not read data, errCode = [%d]!", errCode); + return errCode; + } + + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); // Guarantee not nullptr + QueryObject queryObj(query); + // In readOnly mode, forbidden all schema related query + if (CheckWritePermission() == E_OK) { + const SchemaObject &schemaObjRef = naturalStore->GetSchemaObjectConstRef(); + queryObj.SetSchema(schemaObjRef); + } + bool isMemDb = naturalStore->GetMyProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false); + resultSet = new (std::nothrow) SQLiteSingleVerResultSet(naturalStore, queryObj, + SQLiteSingleVerResultSet::Option{cacheModeForNewResultSet_.load(), cacheMaxSizeForNewResultSet_.load()}); + if (resultSet == nullptr) { + LOGE("Create single version result set failed."); + return -E_OUT_OF_MEMORY; + } + errCode = resultSet->Open(isMemDb); + if (errCode != E_OK) { + delete resultSet; + resultSet = nullptr; + LOGE("Open result set failed."); + return errCode; + } + kvDbResultSets_.insert(resultSet); + return E_OK; +} + +void SQLiteSingleVerNaturalStoreConnection::ReleaseResultSet(IKvDBResultSet *&resultSet) +{ + std::lock_guard lock(kvDbResultSetsMutex_); + if (resultSet == nullptr) { + return; + } + resultSet->Close(); + kvDbResultSets_.erase(resultSet); + delete resultSet; + resultSet = nullptr; + return; +} + +int SQLiteSingleVerNaturalStoreConnection::RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) +{ + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + return static_cast(kvDB_)->RegisterLifeCycleCallback(notifier); +} + +int SQLiteSingleVerNaturalStoreConnection::PreClose() +{ + // check if result set closed + { + std::lock_guard kvDbResultLock(kvDbResultSetsMutex_); + if (kvDbResultSets_.size() > 0) { + LOGE("The connection have [%zu] active result set, can not close.", kvDbResultSets_.size()); + return -E_BUSY; + } + } + + // check if transaction closed + { + std::lock_guard transactionLock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGW("Transaction started, need to rollback before close."); + int errCode = RollbackInner(); + if (errCode != E_OK) { + LOGE("Rollback transaction failed, %d.", errCode); + } + ReleaseExecutor(writeHandle_); + } + } + + // Clear the conflict type function. + { + std::lock_guard lock(conflictMutex_); + ClearConflictNotifierCount(); + conflictType_ = 0; + } + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::CheckIntegrity() const +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetExecutor(true, errCode); + if (handle == nullptr) { + LOGW("Failed to get the executor for the integrity check."); + return errCode; + } + + errCode = handle->CheckIntegrity(); + ReleaseExecutor(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::PragmaSetMaxLogSize(uint64_t *limit) +{ + if (limit == nullptr) { + return -E_INVALID_ARGS; + } + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + LOGE("[SingleVerConnection] db is nullptr for max log limit set."); + return -E_INVALID_DB; + } + if (*limit > DBConstant::MAX_LOG_SIZE_HIGH || *limit < DBConstant::MAX_LOG_SIZE_LOW) { + return -E_INVALID_ARGS; + } + return naturalStore->SetMaxLogSize(*limit); +} + +int SQLiteSingleVerNaturalStoreConnection::ForceCheckPoint() const +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetExecutor(true, errCode); + if (handle == nullptr) { + LOGW("Failed to get the executor for the checkpoint."); + return errCode; + } + + errCode = handle->ForceCheckPoint(); + ReleaseExecutor(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::CheckMonoStatus(OperatePerm perm) +{ + // 1. Get the connection number + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = DisableManualSync(); + if (errCode != E_OK) { + LOGE("In manual sync"); + return -E_BUSY; + } + + // 2. check the result set number + { + std::lock_guard kvDbResultLock(kvDbResultSetsMutex_); + if (kvDbResultSets_.size() > 0) { + LOGE("Active result set exist."); + EnableManualSync(); + return -E_BUSY; + } + } + // 1. Get the connection number, and get the right to do the rekey operation. + errCode = kvDB_->TryToDisableConnection(perm); + if (errCode != E_OK) { + // If precheck failed, it means that there are more than one connection. + // No need reset the condition for the scene. + LOGE("More than one connection"); + EnableManualSync(); + return errCode; + } + // 2. Check the observer list. + errCode = GenericKvDBConnection::PreCheckExclusiveStatus(); + if (errCode != E_OK) { + kvDB_->ReEnableConnection(perm); + LOGE("Observer prevents."); + EnableManualSync(); + return errCode; + } + + // 3. Check the conflict notifier. + { + std::lock_guard conflictLock(conflictMutex_); + if (conflictListener_ != nullptr) { + errCode = -E_BUSY; + GenericKvDBConnection::ResetExclusiveStatus(); + kvDB_->ReEnableConnection(perm); + LOGE("Conflict notifier prevents"); + EnableManualSync(); + return errCode; + } + } + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::GetDeviceIdentifier(PragmaEntryDeviceIdentifier *identifier) +{ + if (identifier == nullptr) { + return -E_INVALID_ARGS; + } + + if (identifier->key.empty() || identifier->key.size() > DBConstant::MAX_VALUE_SIZE) { + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetExecutor(false, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->GetDeviceIdentifier(identifier); + ReleaseExecutor(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::PutBatchInner(const IOption &option, const std::vector &entries) +{ + DBDfxAdapter::StartTraceSQL(); + std::lock_guard lock(transactionMutex_); + bool isAuto = false; + int errCode = E_OK; + if (writeHandle_ == nullptr) { + isAuto = true; + errCode = StartTransactionInner(); + if (errCode != E_OK) { + return errCode; + DBDfxAdapter::FinishTraceSQL(); + } + } + + if ((transactionEntrySize_ + entries.size()) > DBConstant::MAX_TRANSACTION_ENTRY_SIZE) { + DBDfxAdapter::FinishTraceSQL(); + return -E_MAX_LIMITS; + } + + if (option.dataType == IOption::SYNC_DATA) { + errCode = SaveSyncEntries(entries); + } else { + errCode = SaveLocalEntries(entries); + } + if (errCode == E_OK) { + transactionEntrySize_ += entries.size(); + } + + if (isAuto) { + if (errCode == E_OK) { + errCode = CommitInner(); + } else { + int innerCode = RollbackInner(); + errCode = (innerCode != E_OK) ? innerCode : errCode; + } + } + DBDfxAdapter::FinishTraceSQL(); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::DeleteBatchInner(const IOption &option, const std::vector &keys) +{ + DBDfxAdapter::StartTraceSQL(); + std::lock_guard lock(transactionMutex_); + bool isAuto = false; + int errCode = E_OK; + + if (writeHandle_ == nullptr) { + isAuto = true; + errCode = StartTransactionInner(); + if (errCode != E_OK) { + DBDfxAdapter::FinishTraceSQL(); + return errCode; + } + } + + if ((transactionEntrySize_ + keys.size()) > DBConstant::MAX_TRANSACTION_ENTRY_SIZE) { + DBDfxAdapter::FinishTraceSQL(); + return -E_MAX_LIMITS; + } + + if (option.dataType == IOption::SYNC_DATA) { + errCode = DeleteSyncEntries(keys); + } else { + errCode = DeleteLocalEntries(keys); + } + if (errCode == E_OK) { + transactionEntrySize_ += keys.size(); + } + + if (isAuto) { + if (errCode == E_OK) { + errCode = CommitInner(); + } else { + int innerCode = RollbackInner(); + errCode = (innerCode != E_OK) ? innerCode : errCode; + } + } + DBDfxAdapter::FinishTraceSQL(); + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::SaveSyncEntries(const std::vector &entries) +{ + int errCode = E_OK; + for (const auto &entry : entries) { + errCode = SaveEntry(entry, false); + if (errCode != E_OK) { + break; + } + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::SaveLocalEntries(const std::vector &entries) +{ + int errCode = E_OK; + for (const auto &entry : entries) { + errCode = SaveLocalEntry(entry, false); + if (errCode != E_OK) { + break; + } + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::DeleteSyncEntries(const std::vector &keys) +{ + int errCode = E_OK; + for (const auto &key : keys) { + Entry entry; + DBCommon::CalcValueHash(key, entry.key); + errCode = SaveEntry(entry, true); + if ((errCode != E_OK) && (errCode != -E_NOT_FOUND)) { + LOGE("[DeleteSyncEntries] Delete data err:%d", errCode); + break; + } + } + return (errCode == -E_NOT_FOUND) ? E_OK : errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::DeleteLocalEntries(const std::vector &keys) +{ + int errCode = E_OK; + for (const auto &key : keys) { + Entry entry = {key, Value()}; + errCode = SaveLocalEntry(entry, true); + if ((errCode != E_OK) && (errCode != -E_NOT_FOUND)) { + LOGE("[DeleteLocalEntries] Delete data err:%d", errCode); + break; + } + } + return (errCode == -E_NOT_FOUND) ? E_OK : errCode; +} + +// This function currently only be called in local procedure to change sync_data table, do not use in sync procedure. +// It will check and amend value when need if it is a schema database. return error if some value disagree with the +// schema. But in sync procedure, we just neglect the value that disagree with schema. +int SQLiteSingleVerNaturalStoreConnection::SaveEntry(const Entry &entry, bool isDelete, Timestamp timestamp) +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + DataItem dataItem; + dataItem.key = entry.key; + dataItem.value = entry.value; + dataItem.flag = DataItem::LOCAL_FLAG; + if (isDelete) { + dataItem.flag |= DataItem::DELETE_FLAG; + } else { + int errCode = CheckAmendValueContentForLocalProcedure(dataItem.value, dataItem.value); + if (errCode != E_OK) { + LOGE("[SqlSinCon][SaveEntry] CheckAmendValue fail, errCode=%d.", errCode); + return errCode; + } + } + + dataItem.timestamp = naturalStore->GetCurrentTimestamp(); + if (currentMaxTimestamp_ > dataItem.timestamp) { + dataItem.timestamp = currentMaxTimestamp_; + } + + if (timestamp != 0) { + dataItem.writeTimestamp = timestamp; + } else { + dataItem.writeTimestamp = dataItem.timestamp; + } + + if (IsExtendedCacheDBMode()) { + uint64_t recordVersion = naturalStore->GetCacheRecordVersion(); + return SaveEntryInCacheMode(dataItem, recordVersion); + } else { + return SaveEntryNormally(dataItem); + } +} + +int SQLiteSingleVerNaturalStoreConnection::SaveLocalEntry(const Entry &entry, bool isDelete) +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + LocalDataItem dataItem; + dataItem.key = entry.key; + dataItem.value = entry.value; + (void)DBCommon::CalcValueHash(entry.key, dataItem.hashKey); + if (isDelete) { + dataItem.flag = DataItem::DELETE_FLAG; + } + dataItem.timestamp = naturalStore->GetCurrentTimestamp(); + LOGD("Timestamp is %" PRIu64, dataItem.timestamp); + + if (IsCacheDBMode()) { + return SaveLocalItemInCacheMode(dataItem); + } else { + return SaveLocalItem(dataItem); + } +} + +int SQLiteSingleVerNaturalStoreConnection::SaveLocalItem(const LocalDataItem &dataItem) const +{ + int errCode = E_OK; + if ((dataItem.flag & DataItem::DELETE_FLAG) == 0) { + errCode = writeHandle_->PutKvData(SingleVerDataType::LOCAL_TYPE, dataItem.key, dataItem.value, + dataItem.timestamp, localCommittedData_); + } else { + Value value; + Timestamp localTimestamp = 0; + errCode = writeHandle_->DeleteLocalKvData(dataItem.key, localCommittedData_, value, localTimestamp); + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::SaveLocalItemInCacheMode(const LocalDataItem &dataItem) const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + int errCode = writeHandle_->PutLocalDataToCacheDB(dataItem); + if (errCode != E_OK) { + LOGE("[PutLocalEntries] Put local data to cacheDB err:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::SaveEntryNormally(DataItem &dataItem) +{ + int errCode = writeHandle_->PrepareForSavingData(SingleVerDataType::SYNC_TYPE); + if (errCode != E_OK) { + LOGE("Prepare the saving sync data failed:%d", errCode); + return errCode; + } + + Timestamp maxTimestamp = 0; + DeviceInfo deviceInfo = {true, ""}; + errCode = writeHandle_->SaveSyncDataItem(dataItem, deviceInfo, maxTimestamp, committedData_); + if (errCode == E_OK) { + if (maxTimestamp > currentMaxTimestamp_) { + currentMaxTimestamp_ = maxTimestamp; + } + } else { + LOGE("Save entry failed, err:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::SaveEntryInCacheMode(DataItem &dataItem, uint64_t recordVersion) +{ + int errCode = writeHandle_->PrepareForSavingCacheData(SingleVerDataType::SYNC_TYPE); + if (errCode != E_OK) { + LOGE("Prepare the saving sync data failed:%d", errCode); + return errCode; + } + + Timestamp maxTimestamp = 0; + DeviceInfo deviceInfo = {true, ""}; + QueryObject query(Query::Select()); + errCode = writeHandle_->SaveSyncDataItemInCacheMode(dataItem, deviceInfo, maxTimestamp, recordVersion, query); + if (errCode == E_OK) { + if (maxTimestamp > currentMaxTimestamp_) { + currentMaxTimestamp_ = maxTimestamp; + } + } else { + LOGE("Save entry failed, err:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::CheckDataStatus(const Key &key, const Value &value, bool isDelete) const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + return naturalStore->CheckDataStatus(key, value, isDelete); +} + +int SQLiteSingleVerNaturalStoreConnection::CheckWritePermission() const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + if (!naturalStore->CheckWritePermission()) { + return -E_READ_ONLY; + } + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::CheckSyncEntriesValid(const std::vector &entries) const +{ + if (entries.size() > DBConstant::MAX_BATCH_SIZE) { + return -E_INVALID_ARGS; + } + + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + if (!naturalStore->CheckWritePermission()) { + return -E_READ_ONLY; + } + + for (const auto &entry : entries) { + int errCode = naturalStore->CheckDataStatus(entry.key, entry.value, false); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::CheckSyncKeysValid(const std::vector &keys) const +{ + if (keys.size() > DBConstant::MAX_BATCH_SIZE) { + return -E_INVALID_ARGS; + } + + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + if (!naturalStore->CheckWritePermission()) { + return -E_READ_ONLY; + } + + for (const auto &key : keys) { + int errCode = naturalStore->CheckDataStatus(key, {}, true); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::CheckLocalEntriesValid(const std::vector &entries) const +{ + if (entries.size() > DBConstant::MAX_BATCH_SIZE) { + return -E_INVALID_ARGS; + } + + GenericKvDB *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + if (!naturalStore->GenericKvDB::CheckWritePermission()) { + return -E_READ_ONLY; + } + + for (const auto &entry : entries) { + int errCode = naturalStore->GenericKvDB::CheckDataStatus(entry.key, entry.value, false); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::CheckLocalKeysValid(const std::vector &keys) const +{ + if (keys.size() > DBConstant::MAX_BATCH_SIZE) { + return -E_INVALID_ARGS; + } + + GenericKvDB *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + if (!naturalStore->GenericKvDB::CheckWritePermission()) { + return -E_READ_ONLY; + } + + for (const auto &key : keys) { + int errCode = naturalStore->GenericKvDB::CheckDataStatus(key, {}, true); + if (errCode != E_OK) { + return errCode; + } + } + return E_OK; +} + +void SQLiteSingleVerNaturalStoreConnection::CommitAndReleaseNotifyData( + SingleVerNaturalStoreCommitNotifyData *&committedData, bool isNeedCommit, int eventType) +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if ((naturalStore != nullptr) && (committedData != nullptr)) { + if (isNeedCommit) { + if (!committedData->IsChangedDataEmpty()) { + naturalStore->CommitNotify(eventType, committedData); + } + if (!committedData->IsConflictedDataEmpty()) { + naturalStore->CommitNotify(SQLITE_GENERAL_CONFLICT_EVENT, committedData); + } + } + } + ReleaseCommitData(committedData); +} + +int SQLiteSingleVerNaturalStoreConnection::StartTransactionInner() +{ + if (IsExtendedCacheDBMode()) { + return StartTransactionInCacheMode(); + } else { + return StartTransactionNormally(); + } +} + +int SQLiteSingleVerNaturalStoreConnection::StartTransactionInCacheMode() +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetExecutor(true, errCode); + if (handle == nullptr) { + return errCode; + } + if (CheckLogOverLimit(handle)) { + LOGW("Over the log limit"); + ReleaseExecutor(handle); + return -E_LOG_OVER_LIMITS; + } + errCode = handle->StartTransaction(TransactType::DEFERRED); + if (errCode != E_OK) { + ReleaseExecutor(handle); + return errCode; + } + + writeHandle_ = handle; + transactionEntrySize_ = 0; + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::StartTransactionNormally() +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetExecutor(true, errCode); + if (handle == nullptr) { + return errCode; + } + if (CheckLogOverLimit(handle)) { + LOGW("Over the log limit"); + ReleaseExecutor(handle); + return -E_LOG_OVER_LIMITS; + } + + if (committedData_ == nullptr) { + committedData_ = new (std::nothrow) SingleVerNaturalStoreCommitNotifyData; + if (committedData_ == nullptr) { + ReleaseExecutor(handle); + return -E_OUT_OF_MEMORY; + } + InitConflictNotifiedFlag(); + } + if (localCommittedData_ == nullptr) { + localCommittedData_ = new (std::nothrow) SingleVerNaturalStoreCommitNotifyData; + if (localCommittedData_ == nullptr) { + ReleaseExecutor(handle); + ReleaseCommitData(committedData_); + return -E_OUT_OF_MEMORY; + } + } + errCode = handle->StartTransaction(TransactType::DEFERRED); + if (errCode != E_OK) { + ReleaseExecutor(handle); + ReleaseCommitData(committedData_); + ReleaseCommitData(localCommittedData_); + return errCode; + } + + writeHandle_ = handle; + transactionEntrySize_ = 0; + return E_OK; +} + +void SQLiteSingleVerNaturalStoreConnection::InitConflictNotifiedFlag() +{ + unsigned int conflictFlag = 0; + if (kvDB_->GetRegisterFunctionCount(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ONLY) != 0) { + conflictFlag |= static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ONLY); + } + if (kvDB_->GetRegisterFunctionCount(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ORIG) != 0) { + conflictFlag |= static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ORIG); + } + if (kvDB_->GetRegisterFunctionCount(CONFLICT_SINGLE_VERSION_NS_NATIVE_ALL) != 0) { + conflictFlag |= static_cast(SQLITE_GENERAL_NS_NATIVE_ALL); + } + + committedData_->SetConflictedNotifiedFlag(static_cast(conflictFlag)); +} + +int SQLiteSingleVerNaturalStoreConnection::CommitInner() +{ + bool isCacheOrMigrating = IsExtendedCacheDBMode(); + + int errCode = writeHandle_->Commit(); + ReleaseExecutor(writeHandle_); + transactionEntrySize_ = 0; + + if (!isCacheOrMigrating) { + CommitAndReleaseNotifyData(committedData_, true, SQLITE_GENERAL_NS_PUT_EVENT); + CommitAndReleaseNotifyData(localCommittedData_, true, SQLITE_GENERAL_NS_LOCAL_PUT_EVENT); + } + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + naturalStore->SetMaxTimestamp(currentMaxTimestamp_); + + if (isCacheOrMigrating) { + naturalStore->IncreaseCacheRecordVersion(); + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::RollbackInner() +{ + int errCode = writeHandle_->Rollback(); + transactionEntrySize_ = 0; + currentMaxTimestamp_ = 0; + if (!IsExtendedCacheDBMode()) { + ReleaseCommitData(committedData_); + ReleaseCommitData(localCommittedData_); + } + ReleaseExecutor(writeHandle_); + return errCode; +} + +SQLiteSingleVerStorageExecutor *SQLiteSingleVerNaturalStoreConnection::GetExecutor(bool isWrite, int &errCode) const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + errCode = -E_NOT_INIT; + LOGE("[SingleVerConnection] the store is null"); + return nullptr; + } + return naturalStore->GetHandle(isWrite, errCode); +} + +bool SQLiteSingleVerNaturalStoreConnection::IsCacheDBMode() const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + LOGE("[SingleVerConnection] the store is null"); + return false; + } + return naturalStore->IsCacheDBMode(); +} + +bool SQLiteSingleVerNaturalStoreConnection::IsExtendedCacheDBMode() const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + LOGE("[SingleVerConnection] the store is null"); + return false; + } + return naturalStore->IsExtendedCacheDBMode(); +} + +void SQLiteSingleVerNaturalStoreConnection::ReleaseExecutor(SQLiteSingleVerStorageExecutor *&executor) const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore != nullptr) { + naturalStore->ReleaseHandle(executor); + } +} + +int SQLiteSingleVerNaturalStoreConnection::PublishLocal(const Key &key, bool deleteLocal, bool updateTimestamp, + const KvStoreNbPublishAction &onConflict) +{ + int errCode = CheckWritePermission(); + if (errCode != E_OK) { + return errCode; + } + + bool isNeedCallback = (onConflict != nullptr); + SingleVerRecord localRecord; + localRecord.key = key; + SingleVerRecord syncRecord; + { + if (IsTransactionStarted()) { + return -E_NOT_SUPPORT; + } + std::lock_guard lock(transactionMutex_); + errCode = StartTransactionInner(); + if (errCode != E_OK) { + return errCode; + } + + SingleVerNaturalStoreCommitNotifyData *localCommittedData = nullptr; + if (deleteLocal) { + localCommittedData = new (std::nothrow) SingleVerNaturalStoreCommitNotifyData; + if (localCommittedData == nullptr) { + errCode = -E_OUT_OF_MEMORY; + } + } + if (errCode == E_OK) { + errCode = PublishInner(localCommittedData, updateTimestamp, localRecord, syncRecord, isNeedCallback); + } + + if (errCode != E_OK || isNeedCallback) { + int innerCode = RollbackInner(); + errCode = (innerCode != E_OK) ? innerCode : errCode; + } else { + errCode = CommitInner(); + if (errCode == E_OK) { + CommitAndReleaseNotifyData(localCommittedData, true, SQLITE_GENERAL_NS_LOCAL_PUT_EVENT); + } + } + ReleaseCommitData(localCommittedData); + } + + // need to release the handle lock before callback invoked + if (isNeedCallback) { + return PublishLocalCallback(updateTimestamp, localRecord, syncRecord, onConflict); + } + + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::PublishLocalCallback(bool updateTimestamp, + const SingleVerRecord &localRecord, const SingleVerRecord &syncRecord, const KvStoreNbPublishAction &onConflict) +{ + bool isLocalLastest = updateTimestamp ? true : (localRecord.timestamp > syncRecord.writeTimestamp); + if ((syncRecord.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG) { + onConflict({localRecord.key, localRecord.value}, nullptr, isLocalLastest); + } else { + Entry syncEntry = {syncRecord.key, syncRecord.value}; + onConflict({localRecord.key, localRecord.value}, &syncEntry, isLocalLastest); + } + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::PublishInner(SingleVerNaturalStoreCommitNotifyData *committedData, + bool updateTimestamp, SingleVerRecord &localRecord, SingleVerRecord &syncRecord, bool &isNeedCallback) +{ + Key hashKey; + int errCode = DBCommon::CalcValueHash(localRecord.key, hashKey); + if (errCode != E_OK) { + return errCode; + } + + if (committedData != nullptr) { + errCode = writeHandle_->DeleteLocalKvData(localRecord.key, committedData, localRecord.value, + localRecord.timestamp); + if (errCode != E_OK) { + LOGE("Delete local kv data err:%d", errCode); + return errCode; + } + } else { + if (!writeHandle_->CheckIfKeyExisted(localRecord.key, true, localRecord.value, localRecord.timestamp)) { + LOGE("Record not found."); + return -E_NOT_FOUND; + } + } + + // begin to insert entry to sync table + errCode = CheckDataStatus(localRecord.key, localRecord.value, false); + if (errCode != E_OK) { + return errCode; + } + + errCode = writeHandle_->GetKvDataByHashKey(hashKey, syncRecord); + if (errCode == E_OK) { // has conflict record + if (isNeedCallback) { + return errCode; + } + // fix conflict with LAST_WIN policy + if (updateTimestamp) { // local win + errCode = SaveEntry({localRecord.key, localRecord.value}, false); + } else { + if (localRecord.timestamp <= syncRecord.writeTimestamp) { // sync win + errCode = -E_STALE; + } else { + errCode = SaveEntry({localRecord.key, localRecord.value}, false, localRecord.timestamp); + } + } + } else { + isNeedCallback = false; + if (errCode == -E_NOT_FOUND) { + errCode = SaveEntry({localRecord.key, localRecord.value}, false, localRecord.timestamp); + } + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::UnpublishToLocal(const Key &key, bool deletePublic, bool updateTimestamp) +{ + int errCode = CheckWritePermission(); + if (errCode != E_OK) { + return errCode; + } + + if (IsTransactionStarted()) { + return -E_NOT_SUPPORT; + } + + std::lock_guard lock(transactionMutex_); + + errCode = StartTransactionInner(); + if (errCode != E_OK) { + return errCode; + } + + Key hashKey; + int innerErrCode = E_OK; + SingleVerRecord syncRecord; + SingleVerNaturalStoreCommitNotifyData *localCommittedData = nullptr; + errCode = DBCommon::CalcValueHash(key, hashKey); + if (errCode != E_OK) { + goto END; + } + + errCode = writeHandle_->GetKvDataByHashKey(hashKey, syncRecord); + if (errCode != E_OK) { + goto END; + } + + syncRecord.key = key; + errCode = UnpublishInner(localCommittedData, syncRecord, updateTimestamp, innerErrCode); + if (errCode != E_OK) { + goto END; + } + + if (deletePublic && (syncRecord.flag & DataItem::DELETE_FLAG) != DataItem::DELETE_FLAG) { + errCode = SaveEntry({hashKey, {}}, true); + if (errCode != E_OK) { + goto END; + } + } + +END: + // finalize + if (errCode != E_OK) { + int rollbackRet = RollbackInner(); + errCode = (rollbackRet != E_OK) ? rollbackRet : errCode; + } else { + errCode = CommitInner(); + if (errCode == E_OK) { + CommitAndReleaseNotifyData(localCommittedData, true, SQLITE_GENERAL_NS_LOCAL_PUT_EVENT); + } + } + ReleaseCommitData(localCommittedData); + + return (errCode == E_OK) ? innerErrCode : errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::UnpublishInner(SingleVerNaturalStoreCommitNotifyData *&committedData, + const SingleVerRecord &syncRecord, bool updateTimestamp, int &innerErrCode) +{ + int errCode = E_OK; + int localOperation = LOCAL_OPR_NONE; + SingleVerRecord localRecord; + + innerErrCode = -E_LOCAL_DEFEAT; + if (writeHandle_->CheckIfKeyExisted(syncRecord.key, true, localRecord.value, localRecord.timestamp)) { + if ((syncRecord.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG) { + if (updateTimestamp || localRecord.timestamp <= syncRecord.writeTimestamp) { // sync win + innerErrCode = -E_LOCAL_DELETED; + localOperation = LOCAL_OPR_DEL; + } + } else if (updateTimestamp || localRecord.timestamp <= syncRecord.writeTimestamp) { // sync win + innerErrCode = -E_LOCAL_COVERED; + localOperation = LOCAL_OPR_PUT; + } + } else { // no conflict entry in local + innerErrCode = E_OK; + if ((syncRecord.flag & DataItem::DELETE_FLAG) != DataItem::DELETE_FLAG) { + localOperation = LOCAL_OPR_PUT; + } + } + + if (localOperation != LOCAL_OPR_NONE) { + errCode = UnpublishOper(committedData, syncRecord, updateTimestamp, localOperation); + } + + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::UnpublishOper(SingleVerNaturalStoreCommitNotifyData *&committedData, + const SingleVerRecord &syncRecord, bool updateTimestamp, int operType) +{ + committedData = new (std::nothrow) SingleVerNaturalStoreCommitNotifyData; + if (committedData == nullptr) { + return -E_OUT_OF_MEMORY; + } + + int errCode = E_OK; + if (operType == LOCAL_OPR_PUT) { + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_DB; + } + + errCode = CheckDataStatus(syncRecord.key, syncRecord.value, false); + if (errCode != E_OK) { + return errCode; + } + + Timestamp time = updateTimestamp ? naturalStore->GetCurrentTimestamp() : syncRecord.writeTimestamp; + errCode = writeHandle_->PutKvData(SingleVerDataType::LOCAL_TYPE, syncRecord.key, syncRecord.value, time, + committedData); + } else if (operType == LOCAL_OPR_DEL) { + Timestamp localTimestamp = 0; + Value value; + errCode = writeHandle_->DeleteLocalKvData(syncRecord.key, committedData, value, localTimestamp); + } + + return errCode; +} + +void SQLiteSingleVerNaturalStoreConnection::ReleaseCommitData(SingleVerNaturalStoreCommitNotifyData *&committedData) +{ + if (committedData != nullptr) { + committedData->DecObjRef(committedData); + committedData = nullptr; + } +} + +int SQLiteSingleVerNaturalStoreConnection::PragmaPublish(void *parameter) +{ + PragmaPublishInfo *info = static_cast(parameter); + if (info == nullptr) { + return -E_INVALID_ARGS; + } + if (IsExtendedCacheDBMode()) { + int err = IsCacheDBMode() ? -E_EKEYREVOKED : -E_BUSY; + LOGE("[PragmaPublish]Existed cache database can not read data, errCode = [%d]!", err); + return err; + } + return PublishLocal(info->key, info->deleteLocal, info->updateTimestamp, info->action); +} + +int SQLiteSingleVerNaturalStoreConnection::PragmaUnpublish(void *parameter) +{ + PragmaUnpublishInfo *info = static_cast(parameter); + if (info == nullptr) { + return -E_INVALID_ARGS; + } + if (IsExtendedCacheDBMode()) { + int err = IsCacheDBMode() ? -E_EKEYREVOKED : -E_BUSY; + LOGE("[PragmaUnpublish]Existed cache database can not read data, errCode = [%d]!", err); + return err; + } + return UnpublishToLocal(info->key, info->isDeleteSync, info->isUpdateTime); +} + +int SQLiteSingleVerNaturalStoreConnection::PragmaSetAutoLifeCycle(const uint32_t *lifeTime) +{ + if (lifeTime == nullptr || *lifeTime > MAX_AUTO_LIFE_CYCLE || *lifeTime < MIN_AUTO_LIFE_CYCLE) { + return -E_INVALID_ARGS; + } + if (kvDB_ == nullptr) { + return -E_INVALID_DB; + } + return static_cast(kvDB_)->SetAutoLifeCycleTime(*lifeTime); +} + +int SQLiteSingleVerNaturalStoreConnection::PragmaResultSetCacheMode(PragmaData inMode) +{ + if (inMode == nullptr) { + return -E_INVALID_ARGS; + } + auto mode = *(static_cast(inMode)); + if (mode != ResultSetCacheMode::CACHE_FULL_ENTRY && mode != ResultSetCacheMode::CACHE_ENTRY_ID_ONLY) { + return -E_INVALID_ARGS; + } + cacheModeForNewResultSet_.store(mode); + return E_OK; +} + +int SQLiteSingleVerNaturalStoreConnection::PragmaResultSetCacheMaxSize(PragmaData inSize) +{ + if (inSize == nullptr) { + return -E_INVALID_ARGS; + } + int size = *(static_cast(inSize)); + if (size < RESULT_SET_CACHE_MAX_SIZE_MIN || size > RESULT_SET_CACHE_MAX_SIZE_MAX) { + return -E_INVALID_ARGS; + } + cacheMaxSizeForNewResultSet_.store(size); + return E_OK; +} + +// use for getkvstore migrating cache data +int SQLiteSingleVerNaturalStoreConnection::PragmaTriggerToMigrateData(const SecurityOption &secOption) const +{ + if ((secOption.securityLabel != S3) || (secOption.securityFlag != SECE)) { + LOGD("Only S3 SECE data need migrate data!"); + return E_OK; + } + + LOGI("Begin trigger the migration data while open the database!"); + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + return -E_INVALID_CONNECTION; + } + return naturalStore->TriggerToMigrateData(); +} + +int SQLiteSingleVerNaturalStoreConnection::CheckAmendValueContentForLocalProcedure(const Value &oriValue, + Value &amendValue) const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { // Not Likely + return -E_INVALID_DB; + } + bool useAmendValue = false; + return naturalStore->CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, oriValue, amendValue, useAmendValue); +} + +bool SQLiteSingleVerNaturalStoreConnection::CheckLogOverLimit(SQLiteSingleVerStorageExecutor *executor) const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr || executor == nullptr) { // Not Likely + return false; + } + uint64_t logFileSize = executor->GetLogFileSize(); + bool result = logFileSize > naturalStore->GetMaxLogSize(); + if (result) { + LOGW("Log size[%" PRIu64 "] over the limit", logFileSize); + } + return result; +} + +int SQLiteSingleVerNaturalStoreConnection::CalcHashDevID(PragmaDeviceIdentifier &pragmaDev) +{ + if (pragmaDev.deviceID.empty()) { + return -E_INVALID_ARGS; + } + pragmaDev.deviceIdentifier = DBCommon::TransferHashString(pragmaDev.deviceID); + return E_OK; +} + +DEFINE_OBJECT_TAG_FACILITIES(SQLiteSingleVerNaturalStoreConnection) +} \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..dfaf1d681b61a054e1069eb454cef1622527d30d --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_SINGLE_VER_NATURAL_STORE_CONNECTION_H +#define SQLITE_SINGLE_VER_NATURAL_STORE_CONNECTION_H + +#include "sync_able_kvdb_connection.h" +#include "sqlite_single_ver_storage_executor.h" +#include "db_types.h" +#include "runtime_context.h" + +namespace DistributedDB { +class SQLiteSingleVerNaturalStore; + +class SQLiteSingleVerNaturalStoreConnection : public SyncAbleKvDBConnection { +public: + explicit SQLiteSingleVerNaturalStoreConnection(SQLiteSingleVerNaturalStore *kvDB); + ~SQLiteSingleVerNaturalStoreConnection() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleVerNaturalStoreConnection); + + // Get the value from the database + int Get(const IOption &option, const Key &key, Value &value) const override; + + // Put the value to the database + int Put(const IOption &option, const Key &key, const Value &value) override; + + // Delete the value from the database + int Delete(const IOption &option, const Key &key) override; + + // Clear all the data from the database + int Clear(const IOption &option) override; + + // Get all the data from the database + int GetEntries(const IOption &option, const Key &keyPrefix, std::vector &entries) const override; + + int GetEntries(const IOption &option, const Query &query, std::vector &entries) const override; + + int GetCount(const IOption &option, const Query &query, int &count) const override; + + // Put the batch values to the database. + int PutBatch(const IOption &option, const std::vector &entries) override; + + // Delete the batch values from the database. + int DeleteBatch(const IOption &option, const std::vector &keys) override; + + // Get the snapshot + int GetSnapshot(IKvDBSnapshot *&snapshot) const override; + + // Release the created snapshot + void ReleaseSnapshot(IKvDBSnapshot *&snapshot) override; + + // Start the transaction + int StartTransaction() override; + + // Commit the transaction + int Commit() override; + + // Roll back the transaction + int RollBack() override; + + // Check if the transaction already started manually + bool IsTransactionStarted() const override; + + // Pragma interface. + int Pragma(int cmd, void *parameter) override; + + // Parse event types(from observer mode). + int TranslateObserverModeToEventTypes(unsigned mode, std::list &eventTypes) const override; + + // Register a conflict notifier. + int SetConflictNotifier(int conflictType, const KvDBConflictAction &action) override; + + int Rekey(const CipherPassword &passwd) override; + + int Export(const std::string &filePath, const CipherPassword &passwd) override; + + int Import(const std::string &filePath, const CipherPassword &passwd) override; + + // Get the result set + int GetResultSet(const IOption &option, const Key &keyPrefix, IKvDBResultSet *&resultSet) const override; + + int GetResultSet(const IOption &option, const Query &query, IKvDBResultSet *&resultSet) const override; + + // Release the result set + void ReleaseResultSet(IKvDBResultSet *&resultSet) override; + + int RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) override; + + // Called when Close and delete the connection. + int PreClose() override; + + int CheckIntegrity() const override; + +private: + int CheckMonoStatus(OperatePerm perm); + + int GetDeviceIdentifier(PragmaEntryDeviceIdentifier *identifier); + + void ClearConflictNotifierCount(); + + int PutBatchInner(const IOption &option, const std::vector &entries); + int DeleteBatchInner(const IOption &option, const std::vector &keys); + + int SaveSyncEntries(const std::vector &entries); + int SaveLocalEntries(const std::vector &entries); + int DeleteSyncEntries(const std::vector &keys); + int DeleteLocalEntries(const std::vector &keys); + + int SaveEntry(const Entry &entry, bool isDelete, Timestamp timestamp = 0); + + int CheckDataStatus(const Key &key, const Value &value, bool isDelete) const; + + int CheckWritePermission() const; + + int CheckSyncEntriesValid(const std::vector &entries) const; + + int CheckSyncKeysValid(const std::vector &keys) const; + + int CheckLocalEntriesValid(const std::vector &entries) const; + + int CheckLocalKeysValid(const std::vector &keys) const; + + void CommitAndReleaseNotifyData(SingleVerNaturalStoreCommitNotifyData *&committedData, + bool isNeedCommit, int eventType); + + int StartTransactionInner(); + + int CommitInner(); + + int RollbackInner(); + + int PublishLocal(const Key &key, bool deleteLocal, bool updateTimestamp, + const KvStoreNbPublishAction &onConflict); + + int PublishLocalCallback(bool updateTimestamp, const SingleVerRecord &localRecord, + const SingleVerRecord &syncRecord, const KvStoreNbPublishAction &onConflict); + + int PublishInner(SingleVerNaturalStoreCommitNotifyData *committedData, bool updateTimestamp, + SingleVerRecord &localRecord, SingleVerRecord &syncRecord, bool &isNeedCallback); + + int UnpublishToLocal(const Key &key, bool deletePublic, bool updateTimestamp); + + int UnpublishInner(SingleVerNaturalStoreCommitNotifyData *&committedData, const SingleVerRecord &syncRecord, + bool updateTimestamp, int &innerErrCode); + + int UnpublishOper(SingleVerNaturalStoreCommitNotifyData *&committedData, const SingleVerRecord &syncRecord, + bool updateTimestamp, int operType); + + void ReleaseCommitData(SingleVerNaturalStoreCommitNotifyData *&committedData); + + int PragmaPublish(void *parameter); + + int PragmaUnpublish(void *parameter); + + SQLiteSingleVerStorageExecutor *GetExecutor(bool isWrite, int &errCode) const; + + void ReleaseExecutor(SQLiteSingleVerStorageExecutor *&executor) const; + + int PragmaSetAutoLifeCycle(const uint32_t *lifeTime); + void InitConflictNotifiedFlag(); + void AddConflictNotifierCount(int target); + void ResetConflictNotifierCount(int target); + + int PragmaResultSetCacheMode(PragmaData inMode); + int PragmaResultSetCacheMaxSize(PragmaData inSize); + + // use for getkvstore migrating cache data + int PragmaTriggerToMigrateData(const SecurityOption &secOption) const; + int CheckAmendValueContentForLocalProcedure(const Value &oriValue, Value &amendValue) const; + + int SaveLocalEntry(const Entry &entry, bool isDelete); + int SaveLocalItem(const LocalDataItem &dataItem) const; + int SaveLocalItemInCacheMode(const LocalDataItem &dataItem) const; + int SaveEntryNormally(DataItem &dataItem); + int SaveEntryInCacheMode(DataItem &dataItem, uint64_t recordVersion); + + int StartTransactionInCacheMode(); + int StartTransactionNormally(); + + bool IsCacheDBMode() const; + bool IsExtendedCacheDBMode() const; + int CheckReadDataControlled() const; + bool IsFileAccessControlled() const; + + int PragmaSetMaxLogSize(uint64_t *limit); + int ForceCheckPoint() const; + + bool CheckLogOverLimit(SQLiteSingleVerStorageExecutor *executor) const; + int CalcHashDevID(PragmaDeviceIdentifier &pragmaDev); + + DECLARE_OBJECT_TAG(SQLiteSingleVerNaturalStoreConnection); + + // ResultSet Related Info + static constexpr std::size_t MAX_RESULT_SET_SIZE = 4; // Max 4 ResultSet At The Same Time + std::atomic cacheModeForNewResultSet_{ResultSetCacheMode::CACHE_FULL_ENTRY}; + std::atomic cacheMaxSizeForNewResultSet_{0}; // Will be init to default value in constructor + + int conflictType_; + uint32_t transactionEntrySize_; // used for transaction + Timestamp currentMaxTimestamp_; // used for transaction + SingleVerNaturalStoreCommitNotifyData *committedData_; // used for transaction + SingleVerNaturalStoreCommitNotifyData *localCommittedData_; + std::atomic transactionExeFlag_; + + NotificationChain::Listener *conflictListener_; + SQLiteSingleVerStorageExecutor *writeHandle_; // only existed while in transaction. + mutable std::set kvDbResultSets_; + std::mutex conflictMutex_; + std::mutex rekeyMutex_; + std::mutex importMutex_; + mutable std::mutex kvDbResultSetsMutex_; + mutable std::mutex transactionMutex_; // used for transaction +}; +} + +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_continue_token.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_continue_token.cpp new file mode 100644 index 0000000000000000000000000000000000000000..671d87321f4d19fea6964f414dcdad3e7e977657 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_continue_token.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "sqlite_single_ver_relational_continue_token.h" +#include "sqlite_utils.h" + +namespace DistributedDB { +SQLiteSingleVerRelationalContinueToken::SQLiteSingleVerRelationalContinueToken( + const SyncTimeRange &timeRange, const QueryObject &object) + : isGettingDeletedData_(false), queryObj_(object), tableName_(queryObj_.GetTableName()), timeRange_(timeRange) +{} + +bool SQLiteSingleVerRelationalContinueToken::CheckValid() const +{ + bool isValid = (magicBegin_ == MAGIC_BEGIN && magicEnd_ == MAGIC_END); + if (!isValid) { + LOGE("Invalid continue token."); + } + return isValid; +} + +int SQLiteSingleVerRelationalContinueToken::GetStatement(sqlite3 *db, sqlite3_stmt *&queryStmt, sqlite3_stmt *&fullStmt, + bool &isGettingDeletedData) +{ + isGettingDeletedData = isGettingDeletedData_; + if (isGettingDeletedData) { + return GetDeletedDataStmt(db, queryStmt); + } + + int errCode = GetQuerySyncStatement(db, queryStmt); + if (errCode != E_OK) { + return errCode; + } + + // if lastQueryTime equals 0, that means never sync before, need not to send miss query data. + // if queryObj is empty, that means to send all data now, need not to send miss query data. + if (timeRange_.lastQueryTime != 0 && !queryObj_.Empty()) { + errCode = GetMissQueryStatement(db, fullStmt); + } + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(queryStmt, true, errCode); + } + return errCode; +} + +void SQLiteSingleVerRelationalContinueToken::SetNextBeginTime(const DataItem &theLastItem) +{ + Timestamp nextBeginTime = theLastItem.timestamp + 1; + if (nextBeginTime > INT64_MAX) { + nextBeginTime = INT64_MAX; + } + if (!isGettingDeletedData_) { + timeRange_.beginTime = nextBeginTime; + timeRange_.lastQueryTime = std::max(nextBeginTime, timeRange_.lastQueryTime); + return; + } + if ((theLastItem.flag & DataItem::DELETE_FLAG) != 0) { // The last one could be non-deleted. + timeRange_.deleteBeginTime = nextBeginTime; + } +} + +void SQLiteSingleVerRelationalContinueToken::FinishGetData() +{ + if (isGettingDeletedData_) { + timeRange_.deleteEndTime = 0; + return; + } + isGettingDeletedData_ = true; + timeRange_.endTime = 0; + return; +} + +bool SQLiteSingleVerRelationalContinueToken::IsGetAllDataFinished() const +{ + return timeRange_.beginTime >= timeRange_.endTime && timeRange_.deleteBeginTime >= timeRange_.deleteEndTime; +} + +int SQLiteSingleVerRelationalContinueToken::GetQuerySyncStatement(sqlite3 *db, sqlite3_stmt *&stmt) +{ + int errCode = E_OK; + SqliteQueryHelper helper = queryObj_.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + if (fieldNames_.empty()) { + LOGE("field names cannot be empty."); + return -E_INTERNAL_ERROR; + } + return helper.GetRelationalQueryStatement(db, timeRange_.beginTime, timeRange_.endTime, fieldNames_, stmt); +} + +int SQLiteSingleVerRelationalContinueToken::GetMissQueryStatement(sqlite3 *db, sqlite3_stmt *&stmt) +{ + int errCode = E_OK; + SqliteQueryHelper helper = queryObj_.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + return helper.GetRelationalMissQueryStatement(db, timeRange_.lastQueryTime + 1, INT64_MAX, fieldNames_, stmt); +} + +int SQLiteSingleVerRelationalContinueToken::GetDeletedDataStmt(sqlite3 *db, sqlite3_stmt *&stmt) const +{ + // get stmt + const std::string sql = GetDeletedDataSQL(); + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + goto ERROR; + } + + // bind stmt + errCode = SQLiteUtils::BindInt64ToStatement(stmt, 1, timeRange_.deleteBeginTime); // 1 means begin time + if (errCode != E_OK) { + goto ERROR; + } + errCode = SQLiteUtils::BindInt64ToStatement(stmt, 2, timeRange_.deleteEndTime); // 2 means end time + if (errCode != E_OK) { + goto ERROR; + } + return errCode; + +ERROR: + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; +} + +const QueryObject &SQLiteSingleVerRelationalContinueToken::GetQuery() const +{ + return queryObj_; +} + +std::string SQLiteSingleVerRelationalContinueToken::GetDeletedDataSQL() const +{ + std::string tableName = DBConstant::RELATIONAL_PREFIX + tableName_ + "_log"; + return "SELECT * FROM " + tableName + + " WHERE timestamp >= ? AND timestamp < ? AND (flag&0x03 = 0x03) ORDER BY timestamp ASC;"; +} + +void SQLiteSingleVerRelationalContinueToken::SetFieldNames(const std::vector &fieldNames) +{ + fieldNames_ = fieldNames; +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_continue_token.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_continue_token.h new file mode 100644 index 0000000000000000000000000000000000000000..04d3ed37bdee25e88763ba9b0c04b84f744ff26a --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_continue_token.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SQLITE_SINGLE_VER_RELATIONAL_CONTINUE_TOKEN_H +#define SQLITE_SINGLE_VER_RELATIONAL_CONTINUE_TOKEN_H +#ifdef RELATIONAL_STORE +#include +#include + +#include "db_types.h" +#include "query_object.h" + +namespace DistributedDB { +class SQLiteSingleVerRelationalContinueToken { +public: + SQLiteSingleVerRelationalContinueToken(const SyncTimeRange &timeRange, const QueryObject &queryObject); + ~SQLiteSingleVerRelationalContinueToken() = default; + + // Check the magic number at the beginning and end of the SQLiteSingleVerRelationalContinueToken. + bool CheckValid() const; + // The statement should be release by the caller. + int GetStatement(sqlite3 *db, sqlite3_stmt *&queryStmt, sqlite3_stmt *&fullStmt, bool &isGettingDeletedData); + void SetNextBeginTime(const DataItem &theLastItem); + void FinishGetData(); + bool IsGetAllDataFinished() const; + const QueryObject &GetQuery() const; + void SetFieldNames(const std::vector &fieldNames); + +private: + std::string GetDeletedDataSQL() const; + int GetQuerySyncStatement(sqlite3 *db, sqlite3_stmt *&stmt); + int GetDeletedDataStmt(sqlite3 *db, sqlite3_stmt *&stmt) const; + int GetMissQueryStatement(sqlite3 *db, sqlite3_stmt *&stmt); + + static const unsigned int MAGIC_BEGIN = 0x600D0AC7; // for token guard + static const unsigned int MAGIC_END = 0x0AC7600D; // for token guard + unsigned int magicBegin_ = MAGIC_BEGIN; + int isGettingDeletedData_ = false; + QueryObject queryObj_; + const std::string &tableName_; + SyncTimeRange timeRange_; + std::vector fieldNames_; + unsigned int magicEnd_ = MAGIC_END; +}; +} // namespace DistributedDB +#endif // RELATIONAL_STORE +#endif // SQLITE_SINGLE_VER_RELATIONAL_CONTINUE_TOKEN_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_storage_executor.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_storage_executor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..805667835b37b99eaf61a0bcaba3240bd21acc1a --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_storage_executor.cpp @@ -0,0 +1,1316 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "sqlite_single_ver_relational_storage_executor.h" +#include +#include "data_transformer.h" +#include "db_common.h" + +namespace DistributedDB { +SQLiteSingleVerRelationalStorageExecutor::SQLiteSingleVerRelationalStorageExecutor(sqlite3 *dbHandle, bool writable) + : SQLiteStorageExecutor(dbHandle, writable, false) +{} + +int SQLiteSingleVerRelationalStorageExecutor::CreateDistributedTable(const std::string &tableName, TableInfo &table, + bool isUpgrade) +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = SQLiteUtils::AnalysisSchema(dbHandle_, tableName, table); + if (errCode != E_OK) { + LOGE("[CreateDistributedTable] analysis table schema failed. %d", errCode); + return errCode; + } + + if (table.GetCreateTableSql().find("WITHOUT ROWID") != std::string::npos) { + LOGE("[CreateDistributedTable] Not support create distributed table without rowid."); + return -E_NOT_SUPPORT; + } + + bool isTableEmpty = false; + errCode = SQLiteUtils::CheckTableEmpty(dbHandle_, tableName, isTableEmpty); + if (errCode != E_OK) { + LOGE("[CreateDistributedTable] Check table [%s] is empty failed. %d", tableName.c_str(), errCode); + return errCode; + } + + if (!isUpgrade && !isTableEmpty) { // create distributed table should on an empty table + LOGE("[CreateDistributedTable] Create distributed table should on an empty table when first create."); + return -E_NOT_SUPPORT; + } + + // create log table + errCode = SQLiteUtils::CreateRelationalLogTable(dbHandle_, tableName); + if (errCode != E_OK) { + LOGE("[CreateDistributedTable] create log table failed"); + return errCode; + } + + // add trigger + errCode = SQLiteUtils::AddRelationalLogTableTrigger(dbHandle_, table); + if (errCode != E_OK) { + LOGE("[CreateDistributedTable] Add relational log table trigger failed."); + return errCode; + } + return E_OK; +} + +int SQLiteSingleVerRelationalStorageExecutor::UpgradeDistributedTable(const TableInfo &tableInfo, + TableInfo &newTableInfo) +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = SQLiteUtils::AnalysisSchema(dbHandle_, tableInfo.GetTableName(), newTableInfo); + if (errCode != E_OK) { + LOGE("[UpgradeDistributedTable] analysis table schema failed. %d", errCode); + return errCode; + } + + if (newTableInfo.GetCreateTableSql().find("WITHOUT ROWID") != std::string::npos) { + LOGE("[UpgradeDistributedTable] Not support create distributed table without rowid."); + return -E_NOT_SUPPORT; + } + + // new table should has same or compatible upgrade + errCode = tableInfo.CompareWithTable(newTableInfo); + if (errCode == -E_RELATIONAL_TABLE_INCOMPATIBLE) { + LOGE("[UpgradeDistributedTable] Not support with incompatible upgrade."); + return -E_SCHEMA_MISMATCH; + } + + errCode = AlterAuxTableForUpgrade(tableInfo, newTableInfo); + if (errCode != E_OK) { + LOGE("[UpgradeDistributedTable] Alter aux table for upgrade failed. %d", errCode); + } + + return errCode; +} + +namespace { +int GetDeviceTableName(sqlite3 *handle, const std::string &tableName, const std::string &device, + std::vector &deviceTables) +{ + if (device.empty() && tableName.empty()) { // device and table name should not both be empty + return -E_INVALID_ARGS; + } + std::string deviceHash = DBCommon::TransferStringToHex(DBCommon::TransferHashString(device)); + std::string devicePattern = device.empty() ? "%" : deviceHash; + std::string tablePattern = tableName.empty() ? "%" : tableName; + std::string deviceTableName = DBConstant::RELATIONAL_PREFIX + tablePattern + "_" + devicePattern; + + const std::string checkSql = "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '" + + deviceTableName + "';"; + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(handle, checkSql, stmt); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; + } + + do { + errCode = SQLiteUtils::StepWithRetry(stmt, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + LOGE("Get table name failed. %d", errCode); + break; + } + std::string realTableName; + errCode = SQLiteUtils::GetColumnTextValue(stmt, 0, realTableName); // 0: table name result column index + if (errCode != E_OK || realTableName.empty()) { // sqlite might return a row with NULL + continue; + } + if (realTableName.rfind("_log") == (realTableName.length() - 4)) { // 4:suffix length of "_log" + continue; + } + deviceTables.emplace_back(realTableName); + } while (true); + + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; +} + +std::vector GetUpgradeFields(const TableInfo &oldTableInfo, const TableInfo &newTableInfo) +{ + std::vector fields; + auto itOld = oldTableInfo.GetFields().begin(); + auto itNew = newTableInfo.GetFields().begin(); + for (; itNew != newTableInfo.GetFields().end(); itNew++) { + if (itOld == oldTableInfo.GetFields().end() || itOld->first != itNew->first) { + fields.emplace_back(itNew->second); + continue; + } + itOld++; + } + return fields; +} + +int UpgradeFields(sqlite3 *db, const std::vector &tables, std::vector &fields) +{ + if (db == nullptr) { + return -E_INVALID_ARGS; + } + + std::sort(fields.begin(), fields.end(), [] (const FieldInfo &a, const FieldInfo &b) { + return a.GetColumnId()< b.GetColumnId(); + }); + int errCode = E_OK; + for (const auto &table : tables) { + for (const auto &field : fields) { + std::string alterSql = "ALTER TABLE " + table + " ADD " + field.GetFieldName() + " " + field.GetDataType(); + alterSql += field.IsNotNull() ? " NOT NULL" : ""; + alterSql += field.HasDefaultValue() ? " DEFAULT " + field.GetDefaultValue() : ""; + alterSql += ";"; + errCode = SQLiteUtils::ExecuteRawSQL(db, alterSql); + if (errCode != E_OK) { + LOGE("Alter table failed. %d", errCode); + break; + } + } + } + return errCode; +} + +std::map GetChangedIndexes(const TableInfo &oldTableInfo, const TableInfo &newTableInfo) +{ + std::map indexes; + auto itOld = oldTableInfo.GetIndexDefine().begin(); + auto itNew = newTableInfo.GetIndexDefine().begin(); + auto itOldEnd = oldTableInfo.GetIndexDefine().end(); + auto itNewEnd = newTableInfo.GetIndexDefine().end(); + + while (itOld != itOldEnd && itNew != itNewEnd) { + if (itOld->first == itNew->first) { + if (itOld->second != itNew->second) { + indexes.insert({itNew->first, itNew->second}); + } + itOld++; + itNew++; + } else if (itOld->first < itNew->first) { + indexes.insert({itOld->first, {}}); + itOld++; + } else if (itOld->first > itNew->first) { + indexes.insert({itNew->first, itNew->second}); + itNew++; + } + } + + while (itOld != itOldEnd) { + indexes.insert({itOld->first, {}}); + itOld++; + } + + while (itNew != itNewEnd) { + indexes.insert({itNew->first, itNew->second}); + itNew++; + } + + return indexes; +} + +int Upgradeindexes(sqlite3 *db, const std::vector &tables, + const std::map &indexes) +{ + if (db == nullptr) { + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + for (const auto &table : tables) { + for (const auto &index : indexes) { + if (index.first.empty()) { + continue; + } + std::string realIndexName = table + "_" + index.first; + std::string deleteIndexSql = "DROP INDEX IF EXISTS " + realIndexName; + errCode = SQLiteUtils::ExecuteRawSQL(db, deleteIndexSql); + if (errCode != E_OK) { + LOGE("Drop index failed. %d", errCode); + return errCode; + } + + if (index.second.empty()) { // empty means drop index only + continue; + } + + auto it = index.second.begin(); + std::string indexDefine = *it++; + while (it != index.second.end()) { + indexDefine += ", " + *it++; + } + std::string createIndexSql = "CREATE INDEX IF NOT EXISTS " + realIndexName + " ON " + table + + "(" + indexDefine + ");"; + errCode = SQLiteUtils::ExecuteRawSQL(db, createIndexSql); + if (errCode != E_OK) { + LOGE("Create index failed. %d", errCode); + break; + } + } + } + return errCode; +} +} + +int SQLiteSingleVerRelationalStorageExecutor::AlterAuxTableForUpgrade(const TableInfo &oldTableInfo, + const TableInfo &newTableInfo) +{ + std::vector upgradeFields = GetUpgradeFields(oldTableInfo, newTableInfo); + std::map upgradeIndexces = GetChangedIndexes(oldTableInfo, newTableInfo); + std::vector deviceTables; + int errCode = GetDeviceTableName(dbHandle_, oldTableInfo.GetTableName(), {}, deviceTables); + if (errCode != E_OK) { + LOGE("Get device table name for alter table failed. %d", errCode); + return errCode; + } + + LOGD("Begin to alter table: upgrade fields[%zu], indexces[%zu], deviceTable[%zu]", upgradeFields.size(), + upgradeIndexces.size(), deviceTables.size()); + errCode = UpgradeFields(dbHandle_, deviceTables, upgradeFields); + if (errCode != E_OK) { + LOGE("upgrade fields failed. %d", errCode); + return errCode; + } + + errCode = Upgradeindexes(dbHandle_, deviceTables, upgradeIndexces); + if (errCode != E_OK) { + LOGE("upgrade indexes failed. %d", errCode); + } + + return E_OK; +} + +int SQLiteSingleVerRelationalStorageExecutor::StartTransaction(TransactType type) +{ + if (dbHandle_ == nullptr) { + LOGE("Begin transaction failed, dbHandle is null."); + return -E_INVALID_DB; + } + int errCode = SQLiteUtils::BeginTransaction(dbHandle_, type); + if (errCode != E_OK) { + LOGE("Begin transaction failed, errCode = %d", errCode); + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::Commit() +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + return SQLiteUtils::CommitTransaction(dbHandle_); +} + +int SQLiteSingleVerRelationalStorageExecutor::Rollback() +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = SQLiteUtils::RollbackTransaction(dbHandle_); + if (errCode != E_OK) { + LOGE("sqlite single ver storage executor rollback fail! errCode = [%d]", errCode); + } + return errCode; +} + +void SQLiteSingleVerRelationalStorageExecutor::SetTableInfo(const TableInfo &tableInfo) +{ + table_ = tableInfo; +} + +static int GetDataValueByType(sqlite3_stmt *statement, DataValue &value, int cid) +{ + int errCode = E_OK; + int storageType = sqlite3_column_type(statement, cid); + switch (storageType) { + case SQLITE_INTEGER: { + value = static_cast(sqlite3_column_int64(statement, cid)); + break; + } + case SQLITE_FLOAT: { + value = sqlite3_column_double(statement, cid); + break; + } + case SQLITE_BLOB: { + std::vector blobValue; + errCode = SQLiteUtils::GetColumnBlobValue(statement, cid, blobValue); + if (errCode != E_OK) { + return errCode; + } + auto blob = new (std::nothrow) Blob; + if (blob == nullptr) { + return -E_OUT_OF_MEMORY; + } + blob->WriteBlob(blobValue.data(), static_cast(blobValue.size())); + errCode = value.Set(blob); + break; + } + case SQLITE_NULL: { + break; + } + case SQLITE3_TEXT: { + const char *colValue = reinterpret_cast(sqlite3_column_text(statement, cid)); + if (colValue == nullptr) { + value.ResetValue(); + } else { + value = std::string(colValue); + if (value.GetType() == StorageType::STORAGE_TYPE_NULL) { + errCode = -E_OUT_OF_MEMORY; + } + } + break; + } + default: { + break; + } + } + return errCode; +} + +static int BindDataValueByType(sqlite3_stmt *statement, const std::optional &data, int cid) +{ + int errCode = E_OK; + StorageType type = data.value().GetType(); + switch (type) { + case StorageType::STORAGE_TYPE_INTEGER: { + int64_t intData = 0; + (void)data.value().GetInt64(intData); + errCode = SQLiteUtils::MapSQLiteErrno(sqlite3_bind_int64(statement, cid, intData)); + break; + } + + case StorageType::STORAGE_TYPE_REAL: { + double doubleData = 0; + (void)data.value().GetDouble(doubleData); + errCode = SQLiteUtils::MapSQLiteErrno(sqlite3_bind_double(statement, cid, doubleData)); + break; + } + + case StorageType::STORAGE_TYPE_TEXT: { + std::string strData; + (void)data.value().GetText(strData); + errCode = SQLiteUtils::BindTextToStatement(statement, cid, strData); + break; + } + + case StorageType::STORAGE_TYPE_BLOB: { + Blob blob; + (void)data.value().GetBlob(blob); + std::vector blobData(blob.GetData(), blob.GetData() + blob.GetSize()); + errCode = SQLiteUtils::BindBlobToStatement(statement, cid, blobData, true); + break; + } + + case StorageType::STORAGE_TYPE_NULL: { + errCode = SQLiteUtils::MapSQLiteErrno(sqlite3_bind_null(statement, cid)); + break; + } + + default: + break; + } + return errCode; +} + +static int GetLogData(sqlite3_stmt *logStatement, LogInfo &logInfo) +{ + logInfo.dataKey = sqlite3_column_int64(logStatement, 0); // 0 means dataKey index + + std::vector dev; + int errCode = SQLiteUtils::GetColumnBlobValue(logStatement, 1, dev); // 1 means dev index + if (errCode != E_OK) { + return errCode; + } + logInfo.device = std::string(dev.begin(), dev.end()); + + std::vector oriDev; + errCode = SQLiteUtils::GetColumnBlobValue(logStatement, 2, oriDev); // 2 means ori_dev index + if (errCode != E_OK) { + return errCode; + } + logInfo.originDev = std::string(oriDev.begin(), oriDev.end()); + logInfo.timestamp = static_cast(sqlite3_column_int64(logStatement, 3)); // 3 means timestamp index + logInfo.wTimestamp = static_cast(sqlite3_column_int64(logStatement, 4)); // 4 means w_timestamp index + logInfo.flag = static_cast(sqlite3_column_int64(logStatement, 5)); // 5 means flag index + logInfo.flag &= (~DataItem::LOCAL_FLAG); + logInfo.flag &= (~DataItem::UPDATE_FLAG); + return SQLiteUtils::GetColumnBlobValue(logStatement, 6, logInfo.hashKey); // 6 means hashKey index +} + +static size_t GetDataItemSerialSize(DataItem &item, size_t appendLen) +{ + // timestamp and local flag: 3 * uint64_t, version(uint32_t), key, value, origin dev and the padding size. + // the size would not be very large. + static const size_t maxOrigDevLength = 40; + size_t devLength = std::max(maxOrigDevLength, item.origDev.size()); + size_t dataSize = (Parcel::GetUInt64Len() * 3 + Parcel::GetUInt32Len() + Parcel::GetVectorCharLen(item.key) + + Parcel::GetVectorCharLen(item.value) + devLength + appendLen); + return dataSize; +} + +int SQLiteSingleVerRelationalStorageExecutor::GetKvData(const Key &key, Value &value) const +{ + static const std::string SELECT_META_VALUE_SQL = "SELECT value FROM " + DBConstant::RELATIONAL_PREFIX + + "metadata WHERE key=?;"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_META_VALUE_SQL, statement); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // first arg. + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = -E_NOT_FOUND; + goto END; + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + goto END; + } + + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, value); // only one result. + END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::PutKvData(const Key &key, const Value &value) const +{ + static const std::string INSERT_META_SQL = "INSERT OR REPLACE INTO " + DBConstant::RELATIONAL_PREFIX + + "metadata VALUES(?,?);"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, INSERT_META_SQL, statement); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // 1 means key index + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindPutKv]Bind key error:%d", errCode); + goto ERROR; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 2, value, true); // 2 means value index + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindPutKv]Bind value error:%d", errCode); + goto ERROR; + } + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::DeleteMetaData(const std::vector &keys) const +{ + static const std::string REMOVE_META_VALUE_SQL = "DELETE FROM " + DBConstant::RELATIONAL_PREFIX + + "metadata WHERE key=?;"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, REMOVE_META_VALUE_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + + for (const auto &key : keys) { + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // first arg. + if (errCode != E_OK) { + break; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + break; + } + errCode = E_OK; + SQLiteUtils::ResetStatement(statement, false, errCode); + } + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerRelationalStorageExecutor::DeleteMetaDataByPrefixKey(const Key &keyPrefix) const +{ + static const std::string REMOVE_META_VALUE_BY_KEY_PREFIX_SQL = "DELETE FROM " + DBConstant::RELATIONAL_PREFIX + + "metadata WHERE key>=? AND key<=?;"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, REMOVE_META_VALUE_BY_KEY_PREFIX_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::BindPrefixKey(statement, 1, keyPrefix); // 1 is first arg. + if (errCode == E_OK) { + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + } + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +static int GetAllKeys(sqlite3_stmt *statement, std::vector &keys) +{ + if (statement == nullptr) { + return -E_INVALID_DB; + } + int errCode; + do { + errCode = SQLiteUtils::StepWithRetry(statement, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + Key key; + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, key); + if (errCode != E_OK) { + break; + } + + keys.push_back(std::move(key)); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else { + LOGE("SQLite step for getting all keys failed:%d", errCode); + break; + } + } while (true); + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::GetAllMetaKeys(std::vector &keys) const +{ + static const std::string SELECT_ALL_META_KEYS = "SELECT key FROM " + DBConstant::RELATIONAL_PREFIX + "metadata;"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_ALL_META_KEYS, statement); + if (errCode != E_OK) { + LOGE("[Relational][GetAllKey] Get statement failed:%d", errCode); + return errCode; + } + errCode = GetAllKeys(statement, keys); + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::PrepareForSavingLog(const QueryObject &object, + const std::string &deviceName, sqlite3_stmt *&logStmt, sqlite3_stmt *&queryStmt) const +{ + std::string devName = DBCommon::TransferHashString(deviceName); + const std::string tableName = DBConstant::RELATIONAL_PREFIX + object.GetTableName() + "_log"; + std::string dataFormat = "?, '" + deviceName + "', ?, ?, ?, ?, ?"; + std::string columnList = "data_key, device, ori_device, timestamp, wtimestamp, flag, hash_key"; + std::string sql = "INSERT OR REPLACE INTO " + tableName + + " (" + columnList + ") VALUES (" + dataFormat + ");"; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, logStmt); + if (errCode != E_OK) { + LOGE("[info statement] Get log statement fail! errCode:%d", errCode); + return errCode; + } + std::string selectSql = "select " + columnList + " from " + tableName + " where hash_key = ? and device = ?;"; + errCode = SQLiteUtils::GetStatement(dbHandle_, selectSql, queryStmt); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(logStmt, true, errCode); + LOGE("[info statement] Get query statement fail! errCode:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::PrepareForSavingData(const QueryObject &object, + sqlite3_stmt *&statement) const +{ + std::string colName; + std::string dataFormat; + for (size_t colId = 0; colId < table_.GetFields().size(); ++colId) { + colName += table_.GetFieldName(colId) + ","; + dataFormat += "?,"; + } + colName.pop_back(); + dataFormat.pop_back(); + + const std::string sql = "INSERT OR REPLACE INTO " + table_.GetTableName() + + " (" + colName + ") VALUES (" + dataFormat + ");"; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + LOGE("[info statement] Get saving data statement fail! errCode:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::SaveSyncLog(sqlite3_stmt *statement, sqlite3_stmt *queryStmt, + const DataItem &dataItem, int64_t rowid) +{ + int errCode = SQLiteUtils::BindBlobToStatement(queryStmt, 1, dataItem.hashKey); // 1 means hashkey index. + if (errCode != E_OK) { + return errCode; + } + errCode = SQLiteUtils::BindTextToStatement(queryStmt, 2, dataItem.dev); // 2 means device index. + if (errCode != E_OK) { + return errCode; + } + + LogInfo logInfoGet; + errCode = SQLiteUtils::StepWithRetry(queryStmt, isMemDb_); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = -E_NOT_FOUND; + } else { + errCode = GetLogData(queryStmt, logInfoGet); + } + + LogInfo logInfoBind; + logInfoBind.hashKey = dataItem.hashKey; + logInfoBind.device = dataItem.dev; + logInfoBind.timestamp = dataItem.timestamp; + logInfoBind.flag = dataItem.flag; + + if (errCode == -E_NOT_FOUND) { // insert + logInfoBind.wTimestamp = dataItem.writeTimestamp; + logInfoBind.originDev = dataItem.dev; + } else if (errCode == E_OK) { // update + logInfoBind.wTimestamp = logInfoGet.wTimestamp; + logInfoBind.originDev = logInfoGet.originDev; + } else { + return errCode; + } + + // bind + SQLiteUtils::BindInt64ToStatement(statement, 1, rowid); // 1 means dataKey index + std::vector originDev(logInfoBind.originDev.begin(), logInfoBind.originDev.end()); + SQLiteUtils::BindBlobToStatement(statement, 2, originDev); // 2 means ori_dev index + SQLiteUtils::BindInt64ToStatement(statement, 3, logInfoBind.timestamp); // 3 means timestamp index + SQLiteUtils::BindInt64ToStatement(statement, 4, logInfoBind.wTimestamp); // 4 means w_timestamp index + SQLiteUtils::BindInt64ToStatement(statement, 5, logInfoBind.flag); // 5 means flag index + SQLiteUtils::BindBlobToStatement(statement, 6, logInfoBind.hashKey); // 6 means hashKey index + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + return E_OK; + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::DeleteSyncDataItem(const DataItem &dataItem, sqlite3_stmt *&stmt) +{ + if (stmt == nullptr) { + const std::string sql = "DELETE FROM " + table_.GetTableName() + " WHERE rowid IN (" + "SELECT data_key FROM " + DBConstant::RELATIONAL_PREFIX + baseTblName_ + "_log " + "WHERE hash_key=? AND device=? AND flag&0x01=0);"; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, stmt); + if (errCode != E_OK) { + LOGE("[DeleteSyncDataItem] Get statement fail!, errCode:%d", errCode); + return errCode; + } + } + + int errCode = SQLiteUtils::BindBlobToStatement(stmt, 1, dataItem.hashKey); // 1 means hash_key index + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; + } + errCode = SQLiteUtils::BindTextToStatement(stmt, 2, dataItem.dev); // 2 means device index + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; + } + errCode = SQLiteUtils::StepWithRetry(stmt, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + SQLiteUtils::ResetStatement(stmt, false, errCode); // Finalize outside. + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::SaveSyncDataItem(const DataItem &dataItem, sqlite3_stmt *&saveDataStmt, + sqlite3_stmt *&rmDataStmt, const std::vector &fieldInfos, int64_t &rowid) +{ + if ((dataItem.flag & DataItem::DELETE_FLAG) != 0) { + return DeleteSyncDataItem(dataItem, rmDataStmt); + } + + // For no pk data, cannot replace. Must delete and insert. + if (table_.GetPrimaryKey() == "rowid") { + int errCode = DeleteSyncDataItem(dataItem, rmDataStmt); + if (errCode != E_OK) { + LOGE("Delete no pk data before insert failed, errCode=%d.", errCode); + return errCode; + } + } + + OptRowDataWithLog data; + int errCode = DataTransformer::DeSerializeDataItem(dataItem, data, fieldInfos); + if (errCode != E_OK) { + LOGE("[RelationalStorageExecutor] DeSerialize dataItem failed! errCode = [%d]", errCode); + return errCode; + } + + if (data.optionalData.size() != table_.GetFields().size()) { + LOGW("Remote data has different fields with local data. Remote size:%zu, local size:%zu", + data.optionalData.size(), table_.GetFields().size()); + } + + auto putSize = std::min(data.optionalData.size(), table_.GetFields().size()); + for (size_t cid = 0; cid < putSize; ++cid) { + const auto &fieldData = data.optionalData[cid]; + errCode = BindDataValueByType(saveDataStmt, fieldData, cid + 1); + if (errCode != E_OK) { + LOGE("Bind data failed, errCode:%d, cid:%zu.", errCode, cid + 1); + return errCode; + } + } + + errCode = SQLiteUtils::StepWithRetry(saveDataStmt, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + rowid = SQLiteUtils::GetLastRowId(dbHandle_); + errCode = E_OK; + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::DeleteSyncLog(const DataItem &dataItem, sqlite3_stmt *&stmt) +{ + if (stmt == nullptr) { + const std::string sql = "DELETE FROM " + DBConstant::RELATIONAL_PREFIX + baseTblName_ + "_log " + "WHERE hash_key=? AND device=?"; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, stmt); + if (errCode != E_OK) { + LOGE("[DeleteSyncLog] Get statement fail!"); + return errCode; + } + } + + int errCode = SQLiteUtils::BindBlobToStatement(stmt, 1, dataItem.hashKey); // 1 means hashkey index + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; + } + errCode = SQLiteUtils::BindTextToStatement(stmt, 2, dataItem.dev); // 2 means device index + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; + } + errCode = SQLiteUtils::StepWithRetry(stmt, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + SQLiteUtils::ResetStatement(stmt, false, errCode); // Finalize outside. + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::ProcessMissQueryData(const DataItem &item, sqlite3_stmt *&rmDataStmt, + sqlite3_stmt *&rmLogStmt) +{ + int errCode = DeleteSyncDataItem(item, rmDataStmt); + if (errCode != E_OK) { + return errCode; + } + return DeleteSyncLog(item, rmLogStmt); +} + +int SQLiteSingleVerRelationalStorageExecutor::GetSyncDataPre(const DataItem &dataItem, DataItem &itemGet) +{ + if (saveStmt_.queryStmt == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = SQLiteUtils::BindBlobToStatement(saveStmt_.queryStmt, 1, dataItem.hashKey); // 1 index for hashkey + if (errCode != E_OK) { + return errCode; + } + errCode = SQLiteUtils::BindTextToStatement(saveStmt_.queryStmt, 2, dataItem.dev); // 2 index for devices + if (errCode != E_OK) { + return errCode; + } + + LogInfo logInfoGet; + errCode = SQLiteUtils::StepWithRetry(saveStmt_.queryStmt, isMemDb_); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = -E_NOT_FOUND; + } else { + errCode = GetLogData(saveStmt_.queryStmt, logInfoGet); + } + itemGet.timestamp = logInfoGet.timestamp; + SQLiteUtils::ResetStatement(saveStmt_.queryStmt, false, errCode); + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::CheckDataConflictDefeated(const DataItem &dataItem, bool &isDefeated) +{ + if ((dataItem.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) != DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) { + isDefeated = false; // no need to slove conflict except miss query data + return E_OK; + } + + DataItem itemGet; + int errCode = GetSyncDataPre(dataItem, itemGet); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + LOGE("Failed to get raw data. %d", errCode); + return errCode; + } + isDefeated = (dataItem.timestamp <= itemGet.timestamp); // defeated if item timestamp is earlier then raw data + return E_OK; +} + +int SQLiteSingleVerRelationalStorageExecutor::SaveSyncDataItem(const std::vector &fieldInfos, + const std::string &deviceName, DataItem &item) +{ + item.dev = deviceName; + bool isDefeated = false; + int errCode = CheckDataConflictDefeated(item, isDefeated); + if (errCode != E_OK) { + LOGE("check data conflict failed. %d", errCode); + return errCode; + } + + if (isDefeated) { + LOGD("Data was defeated."); + return E_OK; + } + if ((item.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) != 0) { + return ProcessMissQueryData(item, saveStmt_.rmDataStmt, saveStmt_.rmLogStmt); + } + int64_t rowid = -1; + errCode = SaveSyncDataItem(item, saveStmt_.saveDataStmt, saveStmt_.rmDataStmt, fieldInfos, rowid); + if (errCode == E_OK || errCode == -E_NOT_FOUND) { + errCode = SaveSyncLog(saveStmt_.saveLogStmt, saveStmt_.queryStmt, item, rowid); + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::SaveSyncDataItems(const QueryObject &object, + std::vector &dataItems, const std::string &deviceName) +{ + int errCode = PrepareForSavingData(object, saveStmt_.saveDataStmt); + if (errCode != E_OK) { + return errCode; + } + errCode = PrepareForSavingLog(object, deviceName, saveStmt_.saveLogStmt, saveStmt_.queryStmt); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(saveStmt_.saveDataStmt, true, errCode); + return errCode; + } + std::vector fieldInfos; + for (const auto &col: table_.GetFields()) { + fieldInfos.push_back(col.second); + } + + for (auto &item : dataItems) { + if (item.neglect) { // Do not save this record if it is neglected + continue; + } + errCode = SaveSyncDataItem(fieldInfos, deviceName, item); + if (errCode != E_OK) { + break; + } + // Need not reset rmDataStmt and rmLogStmt here. + saveStmt_.ResetStatements(false); + } + if (errCode == -E_NOT_FOUND) { + errCode = E_OK; + } + saveStmt_.ResetStatements(true); + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::SaveSyncItems(const QueryObject &object, std::vector &dataItems, + const std::string &deviceName, const TableInfo &table) +{ + int errCode = StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + return errCode; + } + baseTblName_ = object.GetTableName(); + SetTableInfo(table); + const std::string tableName = DBCommon::GetDistributedTableName(deviceName, baseTblName_); + table_.SetTableName(tableName); + errCode = SaveSyncDataItems(object, dataItems, deviceName); + if (errCode == E_OK) { + errCode = Commit(); + } else { + (void)Rollback(); // Keep the error code of the first scene + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::GetDataItemForSync(sqlite3_stmt *stmt, DataItem &dataItem, + bool isGettingDeletedData) const +{ + RowDataWithLog data; + int errCode = GetLogData(stmt, data.logInfo); + if (errCode != E_OK) { + LOGE("relational data value transfer to kv fail"); + return errCode; + } + + if (!isGettingDeletedData) { + for (size_t cid = 0; cid < table_.GetFields().size(); ++cid) { + DataValue value; + errCode = GetDataValueByType(stmt, value, cid + DBConstant::RELATIONAL_LOG_TABLE_FIELD_NUM); + if (errCode != E_OK) { + return errCode; + } + data.rowData.push_back(std::move(value)); + } + } + + errCode = DataTransformer::SerializeDataItem(data, + isGettingDeletedData ? std::vector() : table_.GetFieldInfos(), dataItem); + if (errCode != E_OK) { + LOGE("relational data value transfer to kv fail"); + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::GetMissQueryData(sqlite3_stmt *fullStmt, DataItem &item) +{ + int errCode = GetDataItemForSync(fullStmt, item, false); + if (errCode != E_OK) { + return errCode; + } + item.value = {}; + item.flag |= DataItem::REMOTE_DEVICE_DATA_MISS_QUERY; + return errCode; +} + +namespace { +int StepNext(bool isMemDB, sqlite3_stmt *stmt, Timestamp ×tamp) +{ + if (stmt == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = SQLiteUtils::StepWithRetry(stmt, isMemDB); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + timestamp = INT64_MAX; + errCode = E_OK; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + timestamp = static_cast(sqlite3_column_int64(stmt, 3)); // 3 means timestamp index + errCode = E_OK; + } + return errCode; +} + +int AppendData(const DataSizeSpecInfo &sizeInfo, size_t appendLength, size_t &overLongSize, size_t &dataTotalSize, + std::vector &dataItems, DataItem &&item) +{ + // If one record is over 4M, ignore it. + if (item.value.size() > DBConstant::MAX_VALUE_SIZE) { + overLongSize++; + } else { + // If dataTotalSize value is bigger than blockSize value , reserve the surplus data item. + dataTotalSize += GetDataItemSerialSize(item, appendLength); + if ((dataTotalSize > sizeInfo.blockSize && !dataItems.empty()) || dataItems.size() >= sizeInfo.packetSize) { + return -E_UNFINISHED; + } else { + dataItems.push_back(item); + } + } + return E_OK; +} +} + +int SQLiteSingleVerRelationalStorageExecutor::GetQueryDataAndStepNext(bool isFirstTime, bool isGettingDeletedData, + sqlite3_stmt *queryStmt, DataItem &item, Timestamp &queryTime) +{ + if (!isFirstTime) { // For the first time, never step before, can get nothing + int errCode = GetDataItemForSync(queryStmt, item, isGettingDeletedData); + if (errCode != E_OK) { + return errCode; + } + } + return StepNext(isMemDb_, queryStmt, queryTime); +} + +int SQLiteSingleVerRelationalStorageExecutor::GetMissQueryDataAndStepNext(sqlite3_stmt *fullStmt, DataItem &item, + Timestamp &missQueryTime) +{ + int errCode = GetMissQueryData(fullStmt, item); + if (errCode != E_OK) { + return errCode; + } + return StepNext(isMemDb_, fullStmt, missQueryTime); +} + +int SQLiteSingleVerRelationalStorageExecutor::GetSyncDataByQuery(std::vector &dataItems, size_t appendLength, + const DataSizeSpecInfo &sizeInfo, std::function getStmt, + const TableInfo &tableInfo) +{ + baseTblName_ = tableInfo.GetTableName(); + SetTableInfo(tableInfo); + sqlite3_stmt *queryStmt = nullptr; + sqlite3_stmt *fullStmt = nullptr; + bool isGettingDeletedData = false; + int errCode = getStmt(dbHandle_, queryStmt, fullStmt, isGettingDeletedData); + if (errCode != E_OK) { + return errCode; + } + + Timestamp queryTime = 0; + Timestamp missQueryTime = (fullStmt == nullptr ? INT64_MAX : 0); + + bool isFirstTime = true; + size_t dataTotalSize = 0; + size_t overLongSize = 0; + do { + DataItem item; + if (queryTime < missQueryTime) { + errCode = GetQueryDataAndStepNext(isFirstTime, isGettingDeletedData, queryStmt, item, queryTime); + } else if (queryTime == missQueryTime) { + errCode = GetQueryDataAndStepNext(isFirstTime, isGettingDeletedData, queryStmt, item, queryTime); + if (errCode != E_OK) { + break; + } + errCode = StepNext(isMemDb_, fullStmt, missQueryTime); + } else { + errCode = GetMissQueryDataAndStepNext(fullStmt, item, missQueryTime); + } + + if (errCode == E_OK && !isFirstTime) { + errCode = AppendData(sizeInfo, appendLength, overLongSize, dataTotalSize, dataItems, std::move(item)); + } + + if (errCode != E_OK) { + break; + } + + isFirstTime = false; + if (queryTime == INT64_MAX && missQueryTime == INT64_MAX) { + errCode = -E_FINISHED; + break; + } + } while (true); + LOGI("Get sync data finished, rc:%d, record size:%zu, overlong size:%zu, isDeleted:%d", + errCode, dataItems.size(), overLongSize, isGettingDeletedData); + SQLiteUtils::ResetStatement(queryStmt, true, errCode); + SQLiteUtils::ResetStatement(fullStmt, true, errCode); + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::CheckDBModeForRelational() +{ + std::string journalMode; + int errCode = SQLiteUtils::GetJournalMode(dbHandle_, journalMode); + + for (auto &c : journalMode) { // convert to lowercase + c = static_cast(std::tolower(c)); + } + + if (errCode == E_OK && journalMode != "wal") { + LOGE("Not support journal mode %s for relational db, expect wal mode.", journalMode.c_str()); + return -E_NOT_SUPPORT; + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::DeleteDistributedDeviceTable(const std::string &device, + const std::string &tableName) +{ + std::vector deviceTables; + int errCode = GetDeviceTableName(dbHandle_, tableName, device, deviceTables); + if (errCode != E_OK) { + LOGE("Get device table name for alter table failed. %d", errCode); + return errCode; + } + + LOGD("Begin to delete device table: deviceTable[%zu]", deviceTables.size()); + for (const auto &table : deviceTables) { + std::string deleteSql = "DROP TABLE IF EXISTS " + table + ";"; // drop the found table + errCode = SQLiteUtils::ExecuteRawSQL(dbHandle_, deleteSql); + if (errCode != E_OK) { + LOGE("Delete device data failed. %d", errCode); + break; + } + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::DeleteDistributedLogTable(const std::string &tableName) +{ + if (tableName.empty()) { + return -E_INVALID_ARGS; + } + std::string logTableName = DBConstant::RELATIONAL_PREFIX + tableName + "_log"; + std::string deleteSql = "DROP TABLE IF EXISTS " + logTableName + ";"; + int errCode = SQLiteUtils::ExecuteRawSQL(dbHandle_, deleteSql); + if (errCode != E_OK) { + LOGE("Delete distributed log table failed. %d", errCode); + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::CheckAndCleanDistributedTable(const std::vector &tableNames, + std::vector &missingTables) +{ + if (tableNames.empty()) { + return E_OK; + } + const std::string checkSql = "SELECT name FROM sqlite_master WHERE type='table' AND name=?;"; + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, checkSql, stmt); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; + } + for (const auto &tableName : tableNames) { + errCode = SQLiteUtils::BindTextToStatement(stmt, 1, tableName); // 1: tablename bind index + if (errCode != E_OK) { + LOGE("Bind table name to check distributed table statement failed. %d", errCode); + break; + } + + errCode = SQLiteUtils::StepWithRetry(stmt, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { // The table in schema was dropped + errCode = DeleteDistributedDeviceTable({}, tableName); // Clean the auxiliary tables for the dropped table + if (errCode != E_OK) { + LOGE("Delete device tables for missing distributed table failed. %d", errCode); + break; + } + errCode = DeleteDistributedLogTable(tableName); + if (errCode != E_OK) { + LOGE("Delete log tables for missing distributed table failed. %d", errCode); + break; + } + missingTables.emplace_back(tableName); + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + LOGE("Check distributed table failed. %d", errCode); + break; + } + errCode = E_OK; // Check result ok for distributed table is still exists + SQLiteUtils::ResetStatement(stmt, false, errCode); + } + SQLiteUtils::ResetStatement(stmt, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerRelationalStorageExecutor::CreateDistributedDeviceTable(const std::string &device, + const TableInfo &baseTbl) +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + if (device.empty() || !baseTbl.IsValid()) { + return -E_INVALID_ARGS; + } + + std::string deviceTableName = DBCommon::GetDistributedTableName(device, baseTbl.GetTableName()); + int errCode = SQLiteUtils::CreateSameStuTable(dbHandle_, baseTbl, deviceTableName); + if (errCode != E_OK) { + LOGE("Create device table failed. %d", errCode); + return errCode; + } + + errCode = SQLiteUtils::CloneIndexes(dbHandle_, baseTbl.GetTableName(), deviceTableName); + if (errCode != E_OK) { + LOGE("Copy index to device table failed. %d", errCode); + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::CheckQueryObjectLegal(const TableInfo &table, QueryObject &query) +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + TableInfo newTable; + int errCode = SQLiteUtils::AnalysisSchema(dbHandle_, table.GetTableName(), newTable); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + LOGE("Check new schema failed. %d", errCode); + return errCode; + } else { + errCode = table.CompareWithTable(newTable); + if (errCode != -E_RELATIONAL_TABLE_EQUAL && errCode != -E_RELATIONAL_TABLE_COMPATIBLE) { + LOGE("Check schema failed, schema was changed. %d", errCode); + return -E_DISTRIBUTED_SCHEMA_CHANGED; + } else { + errCode = E_OK; + } + } + + SqliteQueryHelper helper = query.GetQueryHelper(errCode); + if (errCode != E_OK) { + LOGE("Get query helper for check query failed. %d", errCode); + return errCode; + } + + if (!query.IsQueryForRelationalDB()) { + LOGE("Not support for this query type."); + return -E_NOT_SUPPORT; + } + + SyncTimeRange defaultTimeRange; + sqlite3_stmt *stmt = nullptr; + errCode = helper.GetRelationalQueryStatement(dbHandle_, defaultTimeRange.beginTime, defaultTimeRange.endTime, {}, + stmt); + if (errCode != E_OK) { + LOGE("Get query statement for check query failed. %d", errCode); + } + + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::SaveSyncDataStmt::ResetStatements(bool isNeedFinalize) +{ + int errCode = E_OK; + if (saveDataStmt != nullptr) { + SQLiteUtils::ResetStatement(saveDataStmt, isNeedFinalize, errCode); + } + if (saveLogStmt != nullptr) { + SQLiteUtils::ResetStatement(saveLogStmt, isNeedFinalize, errCode); + } + if (queryStmt != nullptr) { + SQLiteUtils::ResetStatement(queryStmt, isNeedFinalize, errCode); + } + if (rmDataStmt != nullptr) { + SQLiteUtils::ResetStatement(rmDataStmt, isNeedFinalize, errCode); + } + if (rmLogStmt != nullptr) { + SQLiteUtils::ResetStatement(rmLogStmt, isNeedFinalize, errCode); + } + return errCode; +} + +int SQLiteSingleVerRelationalStorageExecutor::GetMaxTimestamp(const std::vector &tableNames, + Timestamp &maxTimestamp) const +{ + maxTimestamp = 0; + for (const auto &tableName : tableNames) { + const std::string sql = "SELECT max(timestamp) from " + DBConstant::RELATIONAL_PREFIX + tableName + "_log;"; + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, stmt); + if (errCode != E_OK) { + return errCode; + } + errCode = SQLiteUtils::StepWithRetry(stmt, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + maxTimestamp = std::max(maxTimestamp, static_cast(sqlite3_column_int64(stmt, 0))); // 0 is index + errCode = E_OK; + } + SQLiteUtils::ResetStatement(stmt, true, errCode); + if (errCode != E_OK) { + maxTimestamp = 0; + return errCode; + } + } + return E_OK; +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_storage_executor.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_storage_executor.h new file mode 100644 index 0000000000000000000000000000000000000000..6a0db0d14a24f3e8699fd986fa5a68aa8fdcc1f8 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_relational_storage_executor.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SQLITE_SINGLE_VER_RELATIONAL_STORAGE_EXECUTOR_H +#define SQLITE_SINGLE_VER_RELATIONAL_STORAGE_EXECUTOR_H +#ifdef RELATIONAL_STORE + +#include "data_transformer.h" +#include "db_types.h" +#include "macro_utils.h" +#include "sqlite_utils.h" +#include "sqlite_storage_executor.h" +#include "relational_store_delegate.h" +#include "query_object.h" + +namespace DistributedDB { +class SQLiteSingleVerRelationalStorageExecutor : public SQLiteStorageExecutor { +public: + SQLiteSingleVerRelationalStorageExecutor(sqlite3 *dbHandle, bool writable); + ~SQLiteSingleVerRelationalStorageExecutor() override = default; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleVerRelationalStorageExecutor); + + int CreateDistributedTable(const std::string &tableName, TableInfo &table, bool isUpgrade); + + int UpgradeDistributedTable(const TableInfo &tableInfo, TableInfo &newTableInfo); + + int StartTransaction(TransactType type); + int Commit(); + int Rollback(); + + // For Get sync data + int GetSyncDataByQuery(std::vector &dataItems, size_t appendLength, const DataSizeSpecInfo &sizeInfo, + std::function getStmt, const TableInfo &tableInfo); + + // operation of meta data + int GetKvData(const Key &key, Value &value) const; + int PutKvData(const Key &key, const Value &value) const; + int DeleteMetaData(const std::vector &keys) const; + int DeleteMetaDataByPrefixKey(const Key &keyPrefix) const; + int GetAllMetaKeys(std::vector &keys) const; + + // For Put sync data + int SaveSyncItems(const QueryObject &object, std::vector &dataItems, + const std::string &deviceName, const TableInfo &table); + + int AnalysisRelationalSchema(const std::string &tableName, TableInfo &tableInfo); + + int CheckDBModeForRelational(); + + int DeleteDistributedDeviceTable(const std::string &device, const std::string &tableName); + int DeleteDistributedLogTable(const std::string &tableName); + + int CheckAndCleanDistributedTable(const std::vector &tableNames, + std::vector &missingTables); + + int CreateDistributedDeviceTable(const std::string &device, const TableInfo &baseTbl); + + int CheckQueryObjectLegal(const TableInfo &table, QueryObject &query); + + int GetMaxTimestamp(const std::vector &tablesName, Timestamp &maxTimestamp) const; + +private: + struct SaveSyncDataStmt { + sqlite3_stmt *saveDataStmt = nullptr; + sqlite3_stmt *saveLogStmt = nullptr; + sqlite3_stmt *queryStmt = nullptr; + sqlite3_stmt *rmDataStmt = nullptr; + sqlite3_stmt *rmLogStmt = nullptr; + + int ResetStatements(bool isNeedFinalize); + }; + + int PrepareForSyncDataByTime(Timestamp begin, Timestamp end, + sqlite3_stmt *&statement, bool getDeletedData) const; + + int GetDataItemForSync(sqlite3_stmt *statement, DataItem &dataItem, bool isGettingDeletedData) const; + + int GetSyncDataPre(const DataItem &dataItem, DataItem &itemGet); + + int CheckDataConflictDefeated(const DataItem &item, bool &isDefeated); + + int SaveSyncDataItem(const std::vector &fieldInfos, const std::string &deviceName, DataItem &item); + + int SaveSyncDataItems(const QueryObject &object, std::vector &dataItems, const std::string &deviceName); + int SaveSyncDataItem(const DataItem &dataItem, sqlite3_stmt *&saveDataStmt, sqlite3_stmt *&rmDataStmt, + const std::vector &fieldInfos, int64_t &rowid); + + int DeleteSyncDataItem(const DataItem &dataItem, sqlite3_stmt *&rmDataStmt); + + int SaveSyncLog(sqlite3_stmt *statement, sqlite3_stmt *queryStmt, const DataItem &dataItem, int64_t rowid); + int PrepareForSavingData(const QueryObject &object, sqlite3_stmt *&statement) const; + int PrepareForSavingLog(const QueryObject &object, const std::string &deviceName, + sqlite3_stmt *&statement, sqlite3_stmt *&queryStmt) const; + + int AlterAuxTableForUpgrade(const TableInfo &oldTableInfo, const TableInfo &newTableInfo); + + int DeleteSyncLog(const DataItem &item, sqlite3_stmt *&rmLogStmt); + int ProcessMissQueryData(const DataItem &item, sqlite3_stmt *&rmDataStmt, sqlite3_stmt *&rmLogStmt); + int GetMissQueryData(sqlite3_stmt *fullStmt, DataItem &item); + int GetQueryDataAndStepNext(bool isFirstTime, bool isGettingDeletedData, sqlite3_stmt *queryStmt, DataItem &item, + Timestamp &queryTime); + int GetMissQueryDataAndStepNext(sqlite3_stmt *fullStmt, DataItem &item, Timestamp &missQueryTime); + + void SetTableInfo(const TableInfo &tableInfo); // When put or get sync data, must call the func first. + std::string baseTblName_; + TableInfo table_; // Always operating table, user table when get, device table when put. + + SaveSyncDataStmt saveStmt_; +}; +} // namespace DistributedDB +#endif +#endif // SQLITE_SINGLE_VER_RELATIONAL_STORAGE_EXECUTOR_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f77bb141c010c39ace6382f3c3eb7564d44d935 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_result_set.h" +#include +#include "log_print.h" +#include "db_errno.h" +#include "sqlite_single_ver_forward_cursor.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_storage_executor.h" + +namespace DistributedDB { +namespace { + const int64_t MEM_WINDOW_SIZE = 0xFFFFFFFF; // 4G for max + const double MEM_WINDOW_SCALE = 0.5; // set default window size to 2G + const double DEFAULT_WINDOW_SCALE = 1; // For non-mem db + const int64_t WINDOW_SIZE_MB_UNIT = 1024 * 1024; // 1024 is scale +} + +SQLiteSingleVerResultSet::SQLiteSingleVerResultSet(SQLiteSingleVerNaturalStore *kvDB, const Key &keyPrefix, + const Option& option) : option_(option), type_(ResultSetType::KEYPREFIX), keyPrefix_(keyPrefix), kvDB_(kvDB) {} + +SQLiteSingleVerResultSet::SQLiteSingleVerResultSet(SQLiteSingleVerNaturalStore *kvDB, const QueryObject &queryObj, + const Option& option) : option_(option), type_(ResultSetType::QUERY), queryObj_(queryObj), kvDB_(kvDB) {} + +SQLiteSingleVerResultSet::~SQLiteSingleVerResultSet() +{ + isOpen_ = false; + count_ = 0; + position_ = INIT_POSTION; + kvDB_ = nullptr; + window_ = nullptr; + rawCursor_ = nullptr; + handle_ = nullptr; + cacheStartPosition_ = INIT_POSTION; +} + +// The user get KvStoreResultSet after Open function called, so no need mutex during open procedure +int SQLiteSingleVerResultSet::Open(bool isMemDb) +{ + if (isOpen_) { + return E_OK; + } + if (kvDB_ == nullptr) { // Unlikely + return -E_INVALID_ARGS; + } + if (option_.cacheMode == ResultSetCacheMode::CACHE_FULL_ENTRY) { + return OpenForCacheFullEntryMode(isMemDb); + } else { + return OpenForCacheEntryIdMode(); + } +} + +int SQLiteSingleVerResultSet::OpenForCacheFullEntryMode(bool isMemDb) +{ + if (type_ == ResultSetType::KEYPREFIX) { + rawCursor_ = new (std::nothrow) SQLiteSingleVerForwardCursor(kvDB_, keyPrefix_); + } else { + rawCursor_ = new (std::nothrow) SQLiteSingleVerForwardCursor(kvDB_, queryObj_); + } + if (rawCursor_ == nullptr) { + LOGE("[SqlSinResSet][OpenForEntry] OOM When Create ForwardCursor."); + return E_OUT_OF_MEMORY; + } + window_ = new (std::nothrow) ResultEntriesWindow(); + if (window_ == nullptr) { + LOGE("[SqlSinResSet][OpenForEntry] OOM When Create EntryWindow."); + delete rawCursor_; + rawCursor_ = nullptr; + return -E_OUT_OF_MEMORY; + } + // cacheMaxSize is within [1,16] + int64_t windowSize = isMemDb ? MEM_WINDOW_SIZE : (option_.cacheMaxSize * WINDOW_SIZE_MB_UNIT); + double scale = isMemDb ? MEM_WINDOW_SCALE : DEFAULT_WINDOW_SCALE; + int errCode = window_->Init(rawCursor_, windowSize, scale); + if (errCode != E_OK) { + LOGE("[SqlSinResSet][OpenForEntry] EntryWindow Init Fail, ErrCode=%d.", errCode); + delete window_; + window_ = nullptr; + delete rawCursor_; + rawCursor_ = nullptr; + return errCode; + } + count_ = window_->GetTotalCount(); + isOpen_ = true; + LOGD("[SqlSinResSet][OpenForEntry] Type=%d, CacheMaxSize=%d(MB), Count=%d, IsMem=%d.", static_cast(type_), + option_.cacheMaxSize, count_, isMemDb); + return E_OK; +} + +int SQLiteSingleVerResultSet::OpenForCacheEntryIdMode() +{ + int errCode = E_OK; + handle_ = kvDB_->GetHandle(false, errCode); + if (handle_ == nullptr) { + LOGE("[SqlSinResSet][OpenForRowId] Get handle fail, errCode=%d.", errCode); + return errCode; + } + // cacheMaxSize is within [1,16], rowId is of type int64_t + uint32_t cacheLimit = option_.cacheMaxSize * (WINDOW_SIZE_MB_UNIT / sizeof(int64_t)); + if (type_ == ResultSetType::KEYPREFIX) { + errCode = handle_->OpenResultSetForCacheRowIdMode(keyPrefix_, cachedRowIds_, cacheLimit, count_); + } else { + errCode = handle_->OpenResultSetForCacheRowIdMode(queryObj_, cachedRowIds_, cacheLimit, count_); + } + if (errCode != E_OK) { + LOGE("[SqlSinResSet][OpenForRowId] Open ResultSet fail, errCode=%d.", errCode); + kvDB_->ReleaseHandle(handle_); + cachedRowIds_.clear(); + return errCode; + } + // If no result, then nothing is cached, so the cacheStartPosition_ is still INIT_POSTION + if (count_ != 0) { + cacheStartPosition_ = 0; + } + isOpen_ = true; + LOGD("[SqlSinResSet][OpenForRowId] Type=%d, CacheMaxSize=%d(MB), Count=%d, Cached=%zu.", static_cast(type_), + option_.cacheMaxSize, count_, cachedRowIds_.size()); + return E_OK; +} + +int SQLiteSingleVerResultSet::GetCount() const +{ + // count_ never changed after ResultSet opened + return count_; +} + +int SQLiteSingleVerResultSet::GetPosition() const +{ + std::lock_guard lockGuard(mutex_); + return position_; +} + +int SQLiteSingleVerResultSet::MoveTo(int position) const +{ + std::lock_guard lockGuard(mutex_); + if (!isOpen_) { + return -E_RESULT_SET_STATUS_INVALID; + } + if (count_ == 0) { + position_ = (position >= 0) ? 0 : INIT_POSTION; + LOGW("[SqlSinResSet][MoveTo] Empty ResultSet."); + return -E_RESULT_SET_EMPTY; + } + if (position < 0) { + position_ = INIT_POSTION; + LOGW("[SqlSinResSet][MoveTo] Target Position=%d invalid.", position); + return -E_INVALID_ARGS; + } + if (position >= count_) { + position_ = count_; + LOGW("[SqlSinResSet][MoveTo] Target Position=%d Exceed Count=%d.", position, count_); + return -E_INVALID_ARGS; + } + if (position_ == position) { + return E_OK; + } + if (option_.cacheMode == ResultSetCacheMode::CACHE_FULL_ENTRY) { + return MoveToForCacheFullEntryMode(position); + } else { + return MoveToForCacheEntryIdMode(position); + } +} + +int SQLiteSingleVerResultSet::MoveToForCacheFullEntryMode(int position) const +{ + if (window_->MoveToPosition(position)) { + position_ = position; + return E_OK; + } + position_ = INIT_POSTION; + LOGE("[SqlSinResSet][MoveForEntry] Move to position=%d fail.", position); + return -E_UNEXPECTED_DATA; +} + +int SQLiteSingleVerResultSet::MoveToForCacheEntryIdMode(int position) const +{ + // The parameter position now is in [0, count_) with this resultSet not empty + // cacheEndPosition is just after cachedRowIds_, the cached range is [cacheStartPosition_, cacheEndPosition) + int cacheEndPosition = cacheStartPosition_ + cachedRowIds_.size(); + if (position >= cacheStartPosition_ && position < cacheEndPosition) { + // Already in the cachedRowId range, Just move position + position_ = position; + return E_OK; + } + // Not in the cachedRowId range, but valid position, we should reload the cachedRowIds to contain this position + int newCacheStartPos = position; + // cacheMaxSize is within [1,16], rowId is of type int64_t + uint32_t cacheLimit = option_.cacheMaxSize * (WINDOW_SIZE_MB_UNIT / sizeof(int64_t)); + if (position > cacheStartPosition_) { + // Move Forward + int newCacheEndPos = newCacheStartPos + cacheLimit; + if (newCacheEndPos > count_) { + // Since startPos in [0, count_), So the right in (0, cacheLimit), So position still in range + newCacheStartPos -= (newCacheEndPos - count_); + } + } else { + // Move Backward + newCacheStartPos -= (cacheLimit - 1); // Attention, subtract by 1 to ensure position still in range + } + newCacheStartPos = std::max(newCacheStartPos, 0); // Adjust to at least 0 if less then 0 + // Clear rowId cache to accept new rowIds + cachedRowIds_.clear(); + int errCode; + if (type_ == ResultSetType::KEYPREFIX) { + errCode = handle_->ReloadResultSetForCacheRowIdMode(keyPrefix_, cachedRowIds_, cacheLimit, newCacheStartPos); + } else { + errCode = handle_->ReloadResultSetForCacheRowIdMode(queryObj_, cachedRowIds_, cacheLimit, newCacheStartPos); + } + if (errCode != E_OK) { + LOGE("[SqlSinResSet][MoveForRowid] Move to position=%d, Reload fail, errCode=%d.", position, errCode); + // What else shall we do if error happened ? + cachedRowIds_.clear(); + cacheStartPosition_ = INIT_POSTION; + position_ = INIT_POSTION; // Reset Position As MoveForEntry Do + return -E_UNEXPECTED_DATA; + } + LOGD("[SqlSinResSet][MoveForRowid] Reload: position=%d, cacheStartPos=%d, cached=%zu, count=%d.", + position, newCacheStartPos, cachedRowIds_.size(), count_); + // Everything OK + position_ = position; + cacheStartPosition_ = newCacheStartPos; + return E_OK; +} + +int SQLiteSingleVerResultSet::GetEntry(Entry &entry) const +{ + std::lock_guard lockGuard(mutex_); + if (!isOpen_ || count_ == 0) { + return -E_NO_SUCH_ENTRY; + } + if (position_ > INIT_POSTION && position_ < count_) { + // If position_ in the valid range, it can be guaranteed that everything is ok without errors + if (option_.cacheMode == ResultSetCacheMode::CACHE_FULL_ENTRY) { + return window_->GetEntry(entry); + } else { + // It can be guaranteed position_ in the range [cacheStartPosition_, cacheEndPosition) + // For CodeDex false alarm, we still do the check which is not necessary + int cacheIndex = position_ - cacheStartPosition_; + if (cacheIndex < 0 || cacheIndex >= static_cast(cachedRowIds_.size())) { // Not Possible + LOGE("[SqlSinResSet][GetEntry] Internal Error: Position=%d, CacheStartPos=%d, cached=%zu.", position_, + cacheStartPosition_, cachedRowIds_.size()); + return -E_INTERNAL_ERROR; + } + int errCode = handle_->GetEntryByRowId(cachedRowIds_[cacheIndex], entry); + if (errCode != E_OK) { + LOGE("[SqlSinResSet][GetEntry] GetEntryByRowId fail, errCode=%d.", errCode); + return errCode; + } + return E_OK; + } + } + return -E_NO_SUCH_ENTRY; +} + +void SQLiteSingleVerResultSet::Close() +{ + std::lock_guard lockGuard(mutex_); + if (!isOpen_) { + return; + } + if (option_.cacheMode == ResultSetCacheMode::CACHE_FULL_ENTRY) { + CloseForCacheFullEntryMode(); + } else { + CloseForCacheEntryIdMode(); + } + isOpen_ = false; + count_ = 0; + position_ = INIT_POSTION; + LOGD("[SqlSinResSet][Close] Done, Type=%d, Mode=%d.", static_cast(type_), static_cast(option_.cacheMode)); +} + +void SQLiteSingleVerResultSet::CloseForCacheFullEntryMode() +{ + // Attention! Must Delete EntryWindow First(will call ForwardCursor::Close), then delete ForwardCursor. + // ForwardCursor::Close will call Executor::CloseResultSet(Reset the statement and rollback transaction) + delete window_; // It is defined behavior to delete even a nullptr + window_ = nullptr; + // Attention! Delete ForwardCursor Later. + delete rawCursor_; // It is defined behavior to delete even a nullptr + rawCursor_ = nullptr; +} + +void SQLiteSingleVerResultSet::CloseForCacheEntryIdMode() +{ + cacheStartPosition_ = INIT_POSTION; + cachedRowIds_.clear(); + // In Fact : handle_ and kvDB_ is guaranteed to be not nullptr + if (handle_ != nullptr) { + handle_->CloseResultSet(); + kvDB_->ReleaseHandle(handle_); + } +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.h new file mode 100644 index 0000000000000000000000000000000000000000..d4c538304c52fe0d981c1edc79f01ffad129298a --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_SINGLE_VER_RESULT_SET_H +#define SQLITE_SINGLE_VER_RESULT_SET_H + +#include +#include + +#include "kvdb_windowed_result_set.h" +#include "ikvdb_raw_cursor.h" +#include "query_object.h" + +namespace DistributedDB { +constexpr int INIT_POSTION = -1; +constexpr int DEFAULT_RESULT_SET_CACHE_MAX_SIZE = 1; // Unit MB, default 1 MB +constexpr int RESULT_SET_CACHE_MAX_SIZE_MIN = 1; +constexpr int RESULT_SET_CACHE_MAX_SIZE_MAX = 16; +enum class ResultSetType : int { + KEYPREFIX = 0, + QUERY = 1, +}; +// Forward declaration +class SQLiteSingleVerNaturalStore; +class SQLiteSingleVerStorageExecutor; + +class SQLiteSingleVerResultSet : public KvDBWindowedResultSet { +public: + struct Option { + ResultSetCacheMode cacheMode = ResultSetCacheMode::CACHE_FULL_ENTRY; + int cacheMaxSize = DEFAULT_RESULT_SET_CACHE_MAX_SIZE; + }; + + SQLiteSingleVerResultSet(SQLiteSingleVerNaturalStore *kvDB, const Key &keyPrefix, const Option& option); + SQLiteSingleVerResultSet(SQLiteSingleVerNaturalStore *kvDB, const QueryObject &queryObj, const Option& option); + ~SQLiteSingleVerResultSet() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleVerResultSet); + + // Initialize logic + int Open(bool isMemDb) override; + + // Get total entries count. + // >= 0: count, < 0: errCode. + int GetCount() const override; + + // Get current read position. + // >= 0: position, < 0: errCode + int GetPosition() const override; + + // Move the read position to an absolute position value. + int MoveTo(int position) const override; + + // Get the entry of current position. + int GetEntry(Entry &entry) const override; + + // Finalize logic + void Close() override; +private: + int OpenForCacheFullEntryMode(bool isMemDb); + int OpenForCacheEntryIdMode(); + + int MoveToForCacheFullEntryMode(int position) const; + int MoveToForCacheEntryIdMode(int position) const; + + void CloseForCacheFullEntryMode(); + void CloseForCacheEntryIdMode(); + + const Option option_; + + // Common Part Of Two ResultSet Mode. + bool isOpen_ = false; + int count_ = 0; + mutable int position_ = INIT_POSTION; // The position in the overall result + mutable std::mutex mutex_; + + // For KeyPrefix Type Or Query Type. + const ResultSetType type_ = ResultSetType::KEYPREFIX; + Key keyPrefix_; + mutable QueryObject queryObj_; // Some QueryObject member function need to call is not a const function(BAD...) + // Common Pointer For Use, Not Own it, Not Responsible To Release It. + SQLiteSingleVerNaturalStore *kvDB_ = nullptr; + + // Cache Full Entry Mode Using ResultEntriesWindow and IKvDBRawCursor, Own It, Responsible To Release It. + ResultEntriesWindow *window_ = nullptr; + IKvDBRawCursor *rawCursor_ = nullptr; + + // Cache EntryId Mode Using StorageExecutor, Own It, Responsible To Release It. + SQLiteSingleVerStorageExecutor *handle_ = nullptr; + mutable std::vector cachedRowIds_; + mutable int cacheStartPosition_ = INIT_POSTION; // The offset of the first cached rowid in all result rowids +}; +} // namespace DistributedDB + +#endif // SQLITE_SINGLE_VER_RESULT_SET_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4aaaf25ce0b56ede0fb3474cfbbd0047dd8c2611 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_schema_database_upgrader.h" +#include "db_errno.h" +#include "log_print.h" +#include "schema_utils.h" +#include "db_constant.h" + +namespace DistributedDB { +SQLiteSingleVerSchemaDatabaseUpgrader::SQLiteSingleVerSchemaDatabaseUpgrader(sqlite3 *db, + const SchemaObject &newSchema, const SecurityOption &securityOpt, bool isMemDB) + : SQLiteSingleVerDatabaseUpgrader(db, securityOpt, isMemDB), SingleVerSchemaDatabaseUpgrader(newSchema) +{ +} + +int SQLiteSingleVerSchemaDatabaseUpgrader::GetDatabaseSchema(std::string &dbSchema) const +{ + int errCode = SQLiteUtils::GetSchema(db_, dbSchema); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + LOGE("[SqlSingleSchemaUp][GetSchema] ErrCode=%d", errCode); + return errCode; + } + return E_OK; +} + +int SQLiteSingleVerSchemaDatabaseUpgrader::SetDatabaseSchema(const std::string &dbSchema) +{ + int errCode = SQLiteUtils::SaveSchema(db_, dbSchema); + if (errCode != E_OK) { + LOGE("[SqlSingleSchemaUp][SetSchema] ErrCode=%d", errCode); + } + return errCode; +} + +struct ValueUpgradeContext { + SchemaObject schema; + uint32_t checkCount = 0; + uint32_t getCount = 0; + int errCode = E_OK; +}; + +namespace { +const std::string FUNC_NAME_CHECK_AMEND_VALUE = "check_amend_value"; +const std::string FUNC_NAME_GET_AMENDED_VALUE = "get_amended_value"; +// Current implementation is not of ideal performance: at first, we hope to use check_amend_value to filter out values +// that do not need amend, and call get_amended_value immediately for those value that need amend and obtain amended +// value from ValueUpgradeContext which is cache by check_amend_value just before. It works well for case upgrading from +// kv to schema database, but in the case the original schema having index, the sqlite will gather all rowid of values +// that after filtering at first, then call get_amended_value for each value of rowid later. +// Finally we can only parse value the twice in get_amended_value. +const std::string VALUE_UPGRADE_SQL = "UPDATE sync_data SET value=get_amended_value(value) " + "WHERE (flag&0x01=0) AND check_amend_value(value) != 0;"; +constexpr int USING_STR_LEN = -1; + +void CheckGetForJsonSchema(sqlite3_context *ctx, ValueUpgradeContext &context, const RawValue &inValue, + bool checkTrueGetFalse) +{ + ValueObject valueObj; + int errCode = valueObj.Parse(inValue.first, inValue.first + inValue.second, context.schema.GetSkipSize()); + if (errCode != E_OK) { // Unlikely + sqlite3_result_error(ctx, "[SqlSingleSchemaUp][CheckGet] Json value parse fail.", USING_STR_LEN); + LOGE("[SqlSingleSchemaUp][CheckGet] IsCheck=%d, Json value(cnt=%u) parse fail=%d.", checkTrueGetFalse, + (checkTrueGetFalse ? context.checkCount : context.getCount), errCode); + return; + } + errCode = context.schema.CheckValueAndAmendIfNeed(ValueSource::FROM_DBFILE, valueObj); + if (checkTrueGetFalse) { + if (errCode == -E_VALUE_MATCH) { + sqlite3_result_int(ctx, 0); // SQLiteResult 0 for check_ok_no_amend + } else if (errCode == -E_VALUE_MATCH_AMENDED) { + sqlite3_result_int(ctx, E_VALUE_MATCH_AMENDED); // SQLiteResult not 0 for check_ok_and_amend + } else { + sqlite3_result_error(ctx, "[SqlSingleSchemaUp][CheckGet] Json value check fail.", USING_STR_LEN); + LOGE("[SqlSingleSchemaUp][CheckGet] Json value(cnt=%u) check fail=%d.", context.checkCount, errCode); + context.errCode = -E_SCHEMA_VIOLATE_VALUE; + } + } else { + if (errCode != -E_VALUE_MATCH_AMENDED) { // Unlikely + sqlite3_result_error(ctx, "[SqlSingleSchemaUp][CheckGet] Json value no need amend.", USING_STR_LEN); + LOGE("[SqlSingleSchemaUp][CheckGet] Json value(cnt=%u) no need amend=%d.", context.getCount, errCode); + context.errCode = -E_INTERNAL_ERROR; + } + std::vector valueAmended; + valueObj.WriteIntoVector(valueAmended); + if (valueAmended.size() > DBConstant::MAX_VALUE_SIZE) { + sqlite3_result_error(ctx, "[SqlSingleSchemaUp][CheckGet] ValSize exceed limit after amend.", USING_STR_LEN); + LOGE("[SqlSingleSchemaUp][CheckGet] Value(cnt=%u) size=%zu exceed limit after amend.", context.getCount, + valueAmended.size()); + context.errCode = -E_SCHEMA_VIOLATE_VALUE; + return; + } + // For SQLITE_TRANSIENT, SQLite makes a copy of result into space obtained from sqlite3_malloc before it returns + sqlite3_result_blob(ctx, valueAmended.data(), valueAmended.size(), SQLITE_TRANSIENT); + } +} + +void CheckGetForFlatBufferSchema(sqlite3_context *ctx, ValueUpgradeContext &context, const RawValue &inValue, + bool checkTrueGetFalse) +{ + if (!checkTrueGetFalse) { + sqlite3_result_error(ctx, "[SqlSingleSchemaUp][CheckGet] FlatBuffer value no need amend.", USING_STR_LEN); + LOGE("[SqlSingleSchemaUp][CheckGet] FlatBuffer value(cnt=%u) no need amend.", context.getCount); + context.errCode = -E_INTERNAL_ERROR; + } + int errCode = context.schema.VerifyValue(ValueSource::FROM_DBFILE, inValue); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "[SqlSingleSchemaUp][CheckGet] FlatBuffer value verify fail.", USING_STR_LEN); + LOGE("[SqlSingleSchemaUp][CheckGet] FlatBuffer value(cnt=%u) verify fail=%d.", context.checkCount, errCode); + context.errCode = -E_SCHEMA_VIOLATE_VALUE; + return; + } + sqlite3_result_int(ctx, 0); // SQLiteResult 0 for check_ok_no_amend +} + +// SQLiteResult 0 for check_ok_no_amend, SQLiteResult not 0 for check_ok_and_amend, SQLiteResult error for check_fail +void CheckValueOrGetAmendValue(sqlite3_context *ctx, int argc, sqlite3_value **argv, bool checkTrueGetFalse) +{ + if (ctx == nullptr || argc != 1 || argv == nullptr) { // 1 parameters, which are value. Unlikely + LOGE("[SqlSingleSchemaUp][CheckGet] Invalid parameter, argc=%d.", argc); + return; + } + auto context = static_cast(sqlite3_user_data(ctx)); + if (context == nullptr || !context->schema.IsSchemaValid()) { // Unlikely + sqlite3_result_error(ctx, "[SqlSingleSchemaUp][CheckGet] No context or schema invalid.", USING_STR_LEN); + LOGE("[SqlSingleSchemaUp][CheckGet] No context or schema invalid."); + return; + } + auto valueBlob = static_cast(sqlite3_value_blob(argv[0])); + int valueBlobLen = sqlite3_value_bytes(argv[0]); + if ((valueBlob == nullptr) || (valueBlobLen <= 0)) { // Is delete record, Unlikely + // Currently delete records are filtered out of value upgrade sql, so not allowed here. + sqlite3_result_error(ctx, "[SqlSingleSchemaUp][CheckGet] Delete record not allowed.", USING_STR_LEN); + LOGE("[SqlSingleSchemaUp][CheckGet] Delete record not allowed."); + return; + } + + if (context->schema.GetSchemaType() == SchemaType::JSON) { + CheckGetForJsonSchema(ctx, *context, RawValue{valueBlob, valueBlobLen}, checkTrueGetFalse); + } else { + CheckGetForFlatBufferSchema(ctx, *context, RawValue{valueBlob, valueBlobLen}, checkTrueGetFalse); + } + // Count only for non-delete value in check_func or get_func + if (checkTrueGetFalse) { + context->checkCount++; + } else { + context->getCount++; + } +} + +void CheckAmendValue(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + CheckValueOrGetAmendValue(ctx, argc, argv, true); +} + +void GetAmendedValue(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + CheckValueOrGetAmendValue(ctx, argc, argv, false); +} +} + +int SQLiteSingleVerSchemaDatabaseUpgrader::UpgradeValues() +{ + ValueUpgradeContext context; + context.schema = newSchema_; + LOGD("[SqlSingleSchemaUp][UpValue] Begin."); + int errCode = sqlite3_create_function_v2(db_, FUNC_NAME_CHECK_AMEND_VALUE.c_str(), + 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, &context, &CheckAmendValue, nullptr, nullptr, nullptr); // 1 args + if (errCode != SQLITE_OK) { + LOGE("[SqlSingleSchemaUp][UpValue] Create func=check_amend_value return=%d.", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + // GetAmendedValue is better not be of deterministic type, otherwise sqlite may take it as constant + errCode = sqlite3_create_function_v2(db_, FUNC_NAME_GET_AMENDED_VALUE.c_str(), + 1, SQLITE_UTF8, &context, &GetAmendedValue, nullptr, nullptr, nullptr); // 1 args + if (errCode != SQLITE_OK) { + LOGE("[SqlSingleSchemaUp][UpValue] Create func=get_amended_value return=%d.", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + errCode = SQLiteUtils::ExecuteRawSQL(db_, VALUE_UPGRADE_SQL); + if (errCode != E_OK) { + LOGE("[SqlSingleSchemaUp][UpValue] Execute value upgrade fail=%d, contextErr=%d.", errCode, context.errCode); + // If error caused by upgrade nor sqlite, using contextErr as the final errCode + errCode = ((context.errCode == E_OK ? errCode : context.errCode)); + } + LOGD("[SqlSingleSchemaUp][UpValue] End."); + return errCode; +} + +int SQLiteSingleVerSchemaDatabaseUpgrader::UpgradeIndexes(const IndexDifference &indexDiffer) +{ + uint32_t skipSize = newSchema_.GetSkipSize(); + SchemaType theType = newSchema_.GetSchemaType(); + // The order of index upgrade is not compulsory, we think order "decrease, change, increase" may be better. + for (const auto &entry : indexDiffer.decrease) { + LOGI("[SqlSingleSchemaUp][UpIndex] DecreaseIndex : indexName=%s.", SchemaUtils::FieldPathString(entry).c_str()); + int errCode = SQLiteUtils::DecreaseIndex(db_, entry); + if (errCode != E_OK) { + LOGE("[SqlSingleSchemaUp][UpIndex] DecreaseIndex fail, errCode=%d.", errCode); + return errCode; + } + } + for (const auto &entry : indexDiffer.change) { + LOGI("[SqlSingleSchemaUp][UpIndex] ChangeIndex : SkipSize=%u, indexName=%s, fieldCount=%zu, type=%s.", + skipSize, SchemaUtils::FieldPathString(entry.first).c_str(), entry.second.size(), + SchemaUtils::SchemaTypeString(theType).c_str()); + int errCode = SQLiteUtils::ChangeIndex(db_, entry.first, entry.second, theType, skipSize); + if (errCode != E_OK) { + LOGE("[SqlSingleSchemaUp][UpIndex] ChangeIndex fail, errCode=%d.", errCode); + return errCode; + } + } + for (const auto &entry : indexDiffer.increase) { + LOGI("[SqlSingleSchemaUp][UpIndex] IncreaseIndex : SkipSize=%u, indexName=%s, fieldCount=%zu, type=%s.", + skipSize, SchemaUtils::FieldPathString(entry.first).c_str(), entry.second.size(), + SchemaUtils::SchemaTypeString(theType).c_str()); + int errCode = SQLiteUtils::IncreaseIndex(db_, entry.first, entry.second, theType, skipSize); + if (errCode != E_OK) { + LOGE("[SqlSingleSchemaUp][UpIndex] IncreaseIndex fail, errCode=%d.", errCode); + return errCode; + } + } + return E_OK; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.h new file mode 100644 index 0000000000000000000000000000000000000000..4a534881a41d3e87a54942891d72c52d2bc2c57b --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_SINGLE_VER_SCHEMA_DATABASE_UPGRADER_H +#define SQLITE_SINGLE_VER_SCHEMA_DATABASE_UPGRADER_H + +#include "sqlite_single_ver_database_upgrader.h" +#include "single_ver_schema_database_upgrader.h" + +namespace DistributedDB { +class SQLiteSingleVerSchemaDatabaseUpgrader final : public SQLiteSingleVerDatabaseUpgrader, + public SingleVerSchemaDatabaseUpgrader { +public: + // An invalid SchemaObject indicate no schema + SQLiteSingleVerSchemaDatabaseUpgrader(sqlite3 *db, const SchemaObject &newSchema, + const SecurityOption &securityOpt, bool isMemDB); + ~SQLiteSingleVerSchemaDatabaseUpgrader() override {}; +protected: + // Get an empty string with return_code E_OK indicate no schema but everything normally + int GetDatabaseSchema(std::string &dbSchema) const override; + + // Set or update schema into database file + int SetDatabaseSchema(const std::string &dbSchema) override; + + int UpgradeValues() override; + int UpgradeIndexes(const IndexDifference &indexDiffer) override; +}; +} // namespace DistributedDB +#endif // SQLITE_SINGLE_VER_SCHEMA_DATABASE_UPGRADER_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c3e075920b93bedb2be5163199b04a8b6426151 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.cpp @@ -0,0 +1,1141 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_storage_engine.h" + +#include + +#include "db_errno.h" +#include "log_print.h" +#include "db_constant.h" +#include "sqlite_single_ver_database_upgrader.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_schema_database_upgrader.h" +#include "platform_specific.h" +#include "runtime_context.h" +#include "db_common.h" +#include "kvdb_manager.h" +#include "param_check_utils.h" + +namespace DistributedDB { +namespace { + const uint64_t CACHE_RECORD_DEFAULT_VERSION = 1; + int GetPathSecurityOption(const std::string &filePath, SecurityOption &secOpt) + { + return RuntimeContext::GetInstance()->GetSecurityOption(filePath, secOpt); + } + + enum class DbType { + MAIN, + META, + CACHE + }; + + std::string GetDbDir(const std::string &subDir, DbType type) + { + static const std::map dbDirDic { + { DbType::MAIN, DBConstant::MAINDB_DIR }, + { DbType::META, DBConstant::METADB_DIR }, + { DbType::CACHE, DBConstant::CACHEDB_DIR }, + }; // for ensure static compilation order + + if (dbDirDic.find(type) == dbDirDic.end()) { + return std::string(); + } + return subDir + "/" + dbDirDic.at(type); + } +} // namespace + +SQLiteSingleVerStorageEngine::SQLiteSingleVerStorageEngine() + : cacheRecordVersion_(CACHE_RECORD_DEFAULT_VERSION), + executorState_(ExecutorState::INVALID), + isCorrupted_(false), + isNeedUpdateSecOpt_(false) +{} + +SQLiteSingleVerStorageEngine::~SQLiteSingleVerStorageEngine() +{} + +int SQLiteSingleVerStorageEngine::MigrateLocalData(SQLiteSingleVerStorageExecutor *handle) const +{ + return handle->MigrateLocalData(); +} + +int SQLiteSingleVerStorageEngine::EraseDeviceWaterMark(SQLiteSingleVerStorageExecutor *&handle, + const std::vector &dataItems) +{ + int errCode = E_OK; + for (const auto &dataItem : dataItems) { + if ((dataItem.flag & DataItem::REMOVE_DEVICE_DATA_FLAG) == DataItem::REMOVE_DEVICE_DATA_FLAG || + (dataItem.flag & DataItem::REMOVE_DEVICE_DATA_NOTIFY_FLAG) == DataItem::REMOVE_DEVICE_DATA_NOTIFY_FLAG) { + auto kvdbManager = KvDBManager::GetInstance(); + if (kvdbManager == nullptr) { + return -E_INVALID_DB; + } + + // sync module will use handle to fix water mark, if fix fail then migrate fail, not need hold write handle + errCode = ReleaseExecutor(handle); + if (errCode != E_OK) { + LOGE("release executor for erase water mark! errCode = [%d]", errCode); + return errCode; + } + + auto identifier = GetIdentifier(); + auto kvdb = kvdbManager->FindKvDB(identifier); + if (kvdb == nullptr) { + LOGE("[SingleVerEngine::EraseWaterMark] kvdb is null."); + return -E_INVALID_DB; + } + + auto kvStore = static_cast(kvdb); + errCode = kvStore->EraseDeviceWaterMark(dataItem.dev, false); + RefObject::DecObjRef(kvdb); + if (errCode != E_OK) { + LOGE("EraseDeviceWaterMark failed when migrating, errCode = [%d]", errCode); + return errCode; + } + + handle = static_cast(FindExecutor(true, OperatePerm::NORMAL_PERM, + errCode)); + if (errCode != E_OK) { + LOGE("Migrate sync data fail, Can not get available executor, errCode = [%d]", errCode); + return errCode; + } + } + } + return errCode; +} + +int SQLiteSingleVerStorageEngine::MigrateSyncDataByVersion(SQLiteSingleVerStorageExecutor *&handle, + NotifyMigrateSyncData &syncData, uint64_t &curMigrateVer) +{ + if (syncData.committedData == nullptr) { + syncData.committedData = new (std::nothrow) SingleVerNaturalStoreCommitNotifyData(); + if (syncData.committedData == nullptr) { + LOGE("[SQLiteSingleVerStorageEngine::MigrateSyncData] committedData is null."); + return -E_OUT_OF_MEMORY; + } + } + InitConflictNotifiedFlag(syncData.committedData); + + std::vector dataItems; + uint64_t minVerIncurCacheDb = 0; + int errCode = handle->GetMinVersionCacheData(dataItems, minVerIncurCacheDb); + if (errCode != E_OK) { + LOGE("[MigrateSyncDataByVersion]Fail to get cur data in cache! err[%d]", errCode); + return errCode; + } + + if (minVerIncurCacheDb == 0) { // min version in cache db is 1 + ++curMigrateVer; + return E_OK; + } + + if (minVerIncurCacheDb != curMigrateVer) { // double check for latest version is migrated + curMigrateVer = minVerIncurCacheDb; + } + + // Call the syncer module to erase the water mark. + errCode = EraseDeviceWaterMark(handle, dataItems); + if (errCode != E_OK) { + LOGE("[MigrateSyncData] Erase water mark failed:%d", errCode); + return errCode; + } + + // next version need process + LOGD("MigrateVer[%" PRIu64 "], minVer[%" PRIu64 "] maxVer[%" PRIu64 "]", + curMigrateVer, minVerIncurCacheDb, GetCacheRecordVersion()); + errCode = handle->MigrateSyncDataByVersion(curMigrateVer++, syncData, dataItems); + if (errCode != E_OK) { + LOGE("Migrate sync data fail and rollback, errCode = [%d]", errCode); + return errCode; + } + + CommitNotifyForMigrateCache(syncData); + + Timestamp timestamp = 0; + errCode = handle->GetMaxTimestampDuringMigrating(timestamp); + if (errCode == E_OK) { + SetMaxTimestamp(timestamp); + } + + errCode = ReleaseHandleTransiently(handle, 2ull); // temporary release handle 2ms + if (errCode != E_OK) { + return errCode; + } + + return E_OK; +} + +// Temporary release handle for idleTime ms, avoid long-term blocking +int SQLiteSingleVerStorageEngine::ReleaseHandleTransiently(SQLiteSingleVerStorageExecutor *&handle, uint64_t idleTime) +{ + int errCode = ReleaseExecutor(handle); + if (errCode != E_OK) { + LOGE("release executor for reopen database! errCode = [%d]", errCode); + return errCode; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(idleTime)); // Wait 2 ms to free this handle for put data + handle = static_cast(FindExecutor(true, OperatePerm::NORMAL_PERM, errCode)); + if (errCode != E_OK) { + LOGE("Migrate sync data fail, Can not get available executor, errCode = [%d]", errCode); + return errCode; + } + return errCode; +} + +int SQLiteSingleVerStorageEngine::AddSubscribeToMainDBInMigrate() +{ + LOGD("Add subscribe to mainDB from cache. %d", engineState_); + std::lock_guard lock(subscribeMutex_); + if (subscribeQuery_.empty()) { + return E_OK; + } + int errCode = E_OK; + auto handle = static_cast(FindExecutor(true, OperatePerm::NORMAL_PERM, errCode)); + if (errCode != E_OK || handle == nullptr) { + LOGE("Get available executor for add subscribe failed. %d", errCode); + return errCode; + } + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + goto END; + } + for (auto item : subscribeQuery_) { + errCode = handle->AddSubscribeTrigger(item.second, item.first); + if (errCode != E_OK) { + LOGE("Add subscribe trigger failed: %d id: %s", errCode, item.first.c_str()); + } + } + subscribeQuery_.clear(); + // Not rollback even if some triggers add failed. Users don’t perceive errors, add triggers as much as possible + (void)handle->Commit(); +END: + ReleaseExecutor(handle); + return errCode; +} + +int SQLiteSingleVerStorageEngine::MigrateSyncData(SQLiteSingleVerStorageExecutor *&handle, bool &isNeedTriggerSync) +{ + int errCode = E_OK; + if (handle == nullptr) { + handle = static_cast(FindExecutor(true, OperatePerm::NORMAL_PERM, errCode)); + if (errCode != E_OK) { + LOGE("Migrate sync data fail, Can not get available executor, errCode = [%d]", errCode); + return errCode; + } + } + + LOGD("Begin migrate sync data, need migrate version[%" PRIu64 "]", GetCacheRecordVersion() - 1); + uint64_t curMigrateVer = 0; // The migration process is asynchronous and continuous + NotifyMigrateSyncData syncData; + auto kvdbManager = KvDBManager::GetInstance(); + if (kvdbManager != nullptr) { + auto identifier = GetIdentifier(); + auto kvdb = kvdbManager->FindKvDB(identifier); + if (kvdb != nullptr) { + auto kvStore = static_cast(kvdb); + syncData.isPermitForceWrite = + !(kvStore->GetDbProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false)); + RefObject::DecObjRef(kvdb); + } else { + LOGE("[SingleVerEngine] kvdb is null."); + } + } + // cache atomic version represents version of cacheDb input next time + while (curMigrateVer < GetCacheRecordVersion()) { + errCode = MigrateSyncDataByVersion(handle, syncData, curMigrateVer); + if (errCode != E_OK) { + LOGE("Migrate version[%" PRIu64 "] failed! errCode = [%d]", curMigrateVer, errCode); + break; + } + if (!syncData.isRemote) { + isNeedTriggerSync = true; + } + } + if (syncData.committedData != nullptr) { + RefObject::DecObjRef(syncData.committedData); + syncData.committedData = nullptr; + } + // When finished Migrating sync data, will fix engine state + return errCode; +} + +int SQLiteSingleVerStorageEngine::AttachMainDbAndCacheDb(SQLiteSingleVerStorageExecutor *handle, + EngineState stateBeforeMigrate) +{ + LOGD("Begin attach main db and cache db by executor!"); + // Judge the file corresponding to db by the engine status and attach it to another file + int errCode = E_OK; + std::string attachAbsPath; + if (stateBeforeMigrate == EngineState::MAINDB) { + attachAbsPath = GetDbDir(option_.subdir, DbType::CACHE) + "/" + DBConstant::SINGLE_VER_CACHE_STORE + + DBConstant::SQLITE_DB_EXTENSION; + errCode = handle->AttachMainDbAndCacheDb(option_.cipherType, option_.passwd, attachAbsPath, stateBeforeMigrate); + } else if (stateBeforeMigrate == EngineState::CACHEDB) { + attachAbsPath = GetDbDir(option_.subdir, DbType::MAIN) + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + errCode = handle->AttachMainDbAndCacheDb(option_.cipherType, option_.passwd, attachAbsPath, stateBeforeMigrate); + } else { + return -E_NOT_SUPPORT; + } + if (errCode != E_OK) { + LOGE("Attached database failed, errCode = [%d] engine state = [%d]", errCode, stateBeforeMigrate); + return errCode; + } + + uint64_t maxVersion = 0; + errCode = handle->GetMaxVersionIncacheDb(maxVersion); + if (errCode != E_OK || maxVersion < CACHE_RECORD_DEFAULT_VERSION) { + maxVersion = CACHE_RECORD_DEFAULT_VERSION; + } + + (void)cacheRecordVersion_.store(maxVersion + 1, std::memory_order_seq_cst); + return errCode; +} + +int SQLiteSingleVerStorageEngine::AttachMainDbAndCacheDb(sqlite3 *dbHandle, EngineState stateBeforeMigrate) const +{ + LOGD("Begin attach main db and cache db by sqlite handle!"); + // Judge the file corresponding to db by the engine status and attach it to another file + int errCode = E_OK; + std::string attachAbsPath; + if (stateBeforeMigrate == EngineState::MAINDB) { + attachAbsPath = GetDbDir(option_.subdir, DbType::CACHE) + "/" + DBConstant::SINGLE_VER_CACHE_STORE + + DBConstant::SQLITE_DB_EXTENSION; + errCode = SQLiteUtils::AttachNewDatabase(dbHandle, option_.cipherType, option_.passwd, attachAbsPath, "cache"); + } else if (stateBeforeMigrate == EngineState::CACHEDB) { + attachAbsPath = GetDbDir(option_.subdir, DbType::MAIN) + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + errCode = SQLiteUtils::AttachNewDatabase(dbHandle, option_.cipherType, option_.passwd, attachAbsPath, "maindb"); + } else { + return -E_NOT_SUPPORT; + } + if (errCode != E_OK) { + LOGE("Attached database failed, errCode = [%d] engine state = [%d]", errCode, stateBeforeMigrate); + return errCode; + } + + return errCode; +} + +int SQLiteSingleVerStorageEngine::ReInit() +{ + return Init(); +} + +int SQLiteSingleVerStorageEngine::ReleaseExecutor(SQLiteSingleVerStorageExecutor *&handle) +{ + if (handle == nullptr) { + return E_OK; + } + StorageExecutor *databaseHandle = handle; + isCorrupted_ = isCorrupted_ || handle->GetCorruptedStatus(); + Recycle(databaseHandle); + handle = nullptr; + if (isCorrupted_) { + LOGE("Database is corrupted!"); + return -E_INVALID_PASSWD_OR_CORRUPTED_DB; // Externally imperceptible, used to terminate migration + } + return E_OK; +} + +int SQLiteSingleVerStorageEngine::FinishMigrateData(SQLiteSingleVerStorageExecutor *&handle, + EngineState stateBeforeMigrate) +{ + LOGI("Begin to finish migrate and reinit db state!"); + int errCode; + if (handle == nullptr) { + return -E_INVALID_ARGS; + } + + if (stateBeforeMigrate == EngineState::MAINDB) { + sqlite3 *dbHandle = nullptr; + errCode = handle->GetDbHandle(dbHandle); // use executor get sqlite3 handle to operating database + if (errCode != E_OK) { + LOGE("Get Db handle failed! errCode = [%d]", errCode); + return errCode; + } + + errCode = SQLiteUtils::ExecuteRawSQL(dbHandle, "DETACH 'cache'"); + if (errCode != E_OK) { + LOGE("Execute the SQLite detach failed:%d", errCode); + return errCode; + } + // delete cachedb + errCode = DBCommon::RemoveAllFilesOfDirectory(GetDbDir(option_.subdir, DbType::CACHE), false); + if (errCode != E_OK) { + LOGE("Remove files of cache database after detach:%d", errCode); + } + + SetEngineState(EngineState::MAINDB); + return errCode; + } + + errCode = ReleaseExecutor(handle); + if (errCode != E_OK) { + LOGE("Release executor for reopen database! errCode = [%d]", errCode); + return errCode; + } + + // close db for reinit this engine + Release(); + + // delete cache db + errCode = DBCommon::RemoveAllFilesOfDirectory(GetDbDir(option_.subdir, DbType::CACHE), false); + if (errCode != E_OK) { + LOGE("Remove files of cache database after release current db:%d", errCode); + return errCode; + } + + // reInit, it will reset engine state + errCode = ReInit(); + if (errCode != E_OK) { + LOGE("Reinit failed when finish migrate data! please try reopen kvstore! errCode = [%d]", errCode); + return errCode; + } + + return E_OK; +} + +int SQLiteSingleVerStorageEngine::InitExecuteMigrate(SQLiteSingleVerStorageExecutor *handle, + EngineState preMigrateState) +{ + // after attach main and cache need change operate data sql, changing state forbid operate database + SetEngineState(EngineState::MIGRATING); + + int errCode = E_OK; + // check if has been attach and attach cache and main for migrate + if (executorState_ == ExecutorState::MAINDB || executorState_ == ExecutorState::CACHEDB) { + errCode = AttachMainDbAndCacheDb(handle, preMigrateState); + if (errCode != E_OK) { + LOGE("[ExeMigrate] Attach main db and cache db failed!, errCode = [%d]", errCode); + // For lock state open db, can not attach main and cache + return errCode; + } + } else if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE || + // Has been attach, maybe ever crashed, need update version + executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + uint64_t maxVersion = 0; + errCode = handle->GetMaxVersionIncacheDb(maxVersion); + if (errCode != E_OK || maxVersion < CACHE_RECORD_DEFAULT_VERSION) { + maxVersion = CACHE_RECORD_DEFAULT_VERSION; + } + (void)cacheRecordVersion_.store(maxVersion + 1, std::memory_order_seq_cst); + } else { + return -E_UNEXPECTED_DATA; + } + + return errCode; +} + +int SQLiteSingleVerStorageEngine::ExecuteMigrate() +{ + EngineState preState = GetEngineState(); + std::lock_guard lock(migrateLock_); + if (preState == EngineState::MIGRATING || preState == EngineState::INVALID || + !OS::CheckPathExistence(GetDbDir(option_.subdir, DbType::CACHE) + "/" + DBConstant::SINGLE_VER_CACHE_STORE + + DBConstant::SQLITE_DB_EXTENSION)) { + LOGD("[SqlSingleVerEngine] Being single ver migrating or never create db! engine state [%u]", preState); + return E_OK; + } + + // Get write executor for migrate + int errCode = E_OK; + auto handle = static_cast(FindExecutor(true, OperatePerm::NORMAL_PERM, errCode)); + if (errCode != E_OK) { + LOGE("Migrate data fail, Can not get available executor, errCode = [%d]", errCode); + return errCode; + } + + isMigrating_.store(true); + LOGD("Migrate start."); + bool isNeedTriggerSync = false; + errCode = InitExecuteMigrate(handle, preState); + if (errCode != E_OK) { + LOGE("Init migrate data fail, errCode = [%d]", errCode); + goto END; + } + + LOGD("[SqlSingleVerEngine] Current engineState [%u] executorState [%u], begin to executing singleVer db migrate!", + static_cast(preState), static_cast(executorState_)); + // has been attached, Mark start of migration and it can migrate data + errCode = MigrateLocalData(handle); + if (errCode != E_OK) { + LOGE("Migrate local data fail, errCode = [%d]", errCode); + goto END; + } + + errCode = MigrateSyncData(handle, isNeedTriggerSync); + if (errCode != E_OK) { + LOGE("Migrate Sync data fail, errCode = [%d]", errCode); + goto END; + } + + SetEngineState(EngineState::ENGINE_BUSY); // temp forbid use handle and engine for detach and close executor + + // detach database and delete cachedb + errCode = FinishMigrateData(handle, preState); + if (errCode != E_OK) { + LOGE("Finish migrating data fail, errCode = [%d]", errCode); + goto END; + } + +END: // after FinishMigrateData, it will reset engine state + // there is no need cover the errCode + EndMigrate(handle, preState, errCode, isNeedTriggerSync); + isMigrating_.store(false); + LOGD("Migrate stop."); + return errCode; +} + +void SQLiteSingleVerStorageEngine::EndMigrate(SQLiteSingleVerStorageExecutor *&handle, EngineState stateBeforeMigrate, + int errCode, bool isNeedTriggerSync) +{ + LOGD("Finish migrating data! errCode = [%d]", errCode); + if (errCode != E_OK) { + SetEngineState(stateBeforeMigrate); + } + if (handle != nullptr) { + handle->ClearMigrateData(); + } + errCode = ReleaseExecutor(handle); + if (errCode != E_OK) { + LOGE("release executor after migrating! errCode = [%d]", errCode); + } + + errCode = AddSubscribeToMainDBInMigrate(); + if (errCode != E_OK) { + LOGE("Add subscribe trigger after migrate sync data failed: %d", errCode); + } + // Notify max timestamp offset for SyncEngine. + // When time change offset equals 0, SyncEngine can adjust local time offset according to max timestamp. + RuntimeContext::GetInstance()->NotifyTimestampChanged(0); + if (isNeedTriggerSync) { + commitNotifyFunc_(SQLITE_GENERAL_FINISH_MIGRATE_EVENT, nullptr); + } + return; +} + +bool SQLiteSingleVerStorageEngine::IsEngineCorrupted() const +{ + return isCorrupted_; +} + +StorageExecutor *SQLiteSingleVerStorageEngine::NewSQLiteStorageExecutor(sqlite3 *dbHandle, bool isWrite, bool isMemDb) +{ + auto executor = new (std::nothrow) SQLiteSingleVerStorageExecutor(dbHandle, isWrite, isMemDb, executorState_); + if (executor == nullptr) { + return executor; + } + executor->SetConflictResolvePolicy(option_.conflictReslovePolicy); + return executor; +} + +int SQLiteSingleVerStorageEngine::TryToOpenMainDatabase(bool isWrite, sqlite3 *&db) +{ + // Only could get the main database handle in the uninitialized and the main status. + if (GetEngineState() != EngineState::INVALID && GetEngineState() != EngineState::MAINDB) { + LOGE("[SQLiteSinStoreEng][GetMainHandle] Can only create new handle for state[%d]", GetEngineState()); + return -E_EKEYREVOKED; + } + + if (!option_.isMemDb) { + option_.uri = GetDbDir(option_.subdir, DbType::MAIN) + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + } + + OpenDbProperties optionTemp = option_; + if (!isWrite) { + optionTemp.createIfNecessary = false; + } + + int errCode = SQLiteUtils::OpenDatabase(optionTemp, db); + if (errCode != E_OK) { + if (errno == EKEYREVOKED) { + LOGI("Failed to open the main database for key revoked[%d]", errCode); + errCode = -E_EKEYREVOKED; + } + return errCode; + } + + executorState_ = ExecutorState::MAINDB; + // Set the engine state to main status for that the main database is valid. + SetEngineState(EngineState::MAINDB); + + if (OS::CheckPathExistence(GetDbDir(option_.subdir, DbType::CACHE) + "/" + DBConstant::SINGLE_VER_CACHE_STORE + + DBConstant::SQLITE_DB_EXTENSION)) { + // In status cacheDb crash + errCode = AttachMainDbAndCacheDb(db, EngineState::MAINDB); + if (errCode != E_OK) { + LOGE("[SingleVerEngine][GetMain] Attach main db and cache db failed!, errCode = [%d]", errCode); + return E_OK; // not care err to return, only use for print log + } + executorState_ = ExecutorState::MAIN_ATTACH_CACHE; + // cache and main existed together, can not read data, must execute migrate first + SetEngineState(EngineState::ATTACHING); + } + + return errCode; +} + +int SQLiteSingleVerStorageEngine::GetDbHandle(bool isWrite, const SecurityOption &secOpt, sqlite3 *&dbHandle) +{ + int errCode = TryToOpenMainDatabase(isWrite, dbHandle); + LOGD("Finish to open the main database, write[%d], label[%d], flag[%d], id[%.6s], errCode[%d]", isWrite, + secOpt.securityLabel, secOpt.securityFlag, DBCommon::TransferStringToHex(identifier_).c_str(), errCode); + if (!(ParamCheckUtils::IsS3SECEOpt(secOpt) && errCode == -E_EKEYREVOKED)) { + return errCode; + } + + std::string cacheDbPath = GetDbDir(option_.subdir, DbType::CACHE) + "/" + DBConstant::SINGLE_VER_CACHE_STORE + + DBConstant::SQLITE_DB_EXTENSION; + if (!isWrite || GetEngineState() != EngineState::INVALID || + OS::CheckPathExistence(cacheDbPath)) { + LOGI("[SQLiteSingleStorageEng][GetDbHandle] Only use for first create cache db! [%d] [%d]", + isWrite, GetEngineState()); + return -E_EKEYREVOKED; + } + + errCode = GetCacheDbHandle(dbHandle); + if (errCode != E_OK) { + LOGE("singleVerStorageEngine::GetDbHandle get cache handle fail! errCode = [%d]", errCode); + return errCode; + } + SetEngineState(CACHEDB); + executorState_ = ExecutorState::CACHEDB; + + ResetCacheRecordVersion(); + // Get handle means maindb file ekeyevoked, not need attach to + return errCode; +} + +namespace CacheDbSqls { +const std::string CREATE_CACHE_LOCAL_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS local_data(" \ + "key BLOB NOT NULL," \ + "value BLOB," \ + "timestamp INT," \ + "hash_key BLOB PRIMARY KEY NOT NULL," \ + "flag INT NOT NULL);"; + +const std::string CREATE_CACHE_SYNC_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS sync_data(" \ + "key BLOB NOT NULL," \ + "value BLOB," \ + "timestamp INT NOT NULL," \ + "flag INT NOT NULL," \ + "device BLOB," \ + "ori_device BLOB," \ + "hash_key BLOB NOT NULL," \ + "w_timestamp INT," \ + "version INT NOT NULL," \ + "PRIMARY Key(version, hash_key));"; +} + +// Warning: Use error passwd create cache database can not check, it will create error passwd cache db, +// And make migrate data failed! This cache db will not be open correctly. +int SQLiteSingleVerStorageEngine::GetCacheDbHandle(sqlite3 *&db) +{ + option_.uri = GetDbDir(option_.subdir, DbType::CACHE) + "/" + DBConstant::SINGLE_VER_CACHE_STORE + + DBConstant::SQLITE_DB_EXTENSION; + // creatTable + option_.sqls = { + CacheDbSqls::CREATE_CACHE_LOCAL_TABLE_SQL, + CacheDbSqls::CREATE_CACHE_SYNC_TABLE_SQL + }; + + if (!option_.createIfNecessary) { + std::string mainDbPtah = GetDbDir(option_.subdir, DbType::MAIN) + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION; + if (!OS::CheckPathExistence(mainDbPtah)) { // Whether to create a cacheDb is based on whether the mainDb exists + return -E_INVALID_DB; + } + } + + OpenDbProperties option = option_; // copy for no change it + option.createIfNecessary = true; + int errCode = SQLiteUtils::OpenDatabase(option, db); + if (errCode != E_OK) { + LOGE("Get CacheDb handle failed, errCode = [%d], errno = [%d]", errCode, errno); + return errCode; + } + return errCode; +} + +int SQLiteSingleVerStorageEngine::CheckDatabaseSecOpt(const SecurityOption &secOption) const +{ + if (!(secOption == option_.securityOpt) && + secOption.securityLabel != SecurityLabel::NOT_SET && + option_.securityOpt.securityLabel != SecurityLabel::NOT_SET) { + LOGE("SecurityOption mismatch, existed:[%d-%d] vs input:[%d-%d]", secOption.securityLabel, + secOption.securityFlag, option_.securityOpt.securityLabel, option_.securityOpt.securityFlag); + return -E_SECURITY_OPTION_CHECK_ERROR; + } + return E_OK; +} + +int SQLiteSingleVerStorageEngine::CreateNewDirsAndSetSecOpt() const +{ + std::vector dbDir {DBConstant::MAINDB_DIR, DBConstant::METADB_DIR, DBConstant::CACHEDB_DIR}; + for (const auto &item : dbDir) { + if (OS::CheckPathExistence(option_.subdir + "/" + item)) { + continue; + } + + // Dir and old db file not existed, it means that the database is newly created + // need create flag of database not incomplete + if (!OS::CheckPathExistence(option_.subdir + DBConstant::PATH_POSTFIX_DB_INCOMPLETE) && + !OS::CheckPathExistence(option_.subdir + "/" + DBConstant::SINGLE_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION) && + OS::CreateFileByFileName(option_.subdir + DBConstant::PATH_POSTFIX_DB_INCOMPLETE) != E_OK) { + LOGE("Fail to create the token of database incompleted! errCode = [E_SYSTEM_API_FAIL]"); + return -E_SYSTEM_API_FAIL; + } + + if (DBCommon::CreateDirectory(option_.subdir + "/" + item) != E_OK) { + LOGE("Create sub-directory for single ver failed, errno:%d", errno); + return -E_SYSTEM_API_FAIL; + } + + if (option_.securityOpt.securityLabel == NOT_SET) { + continue; + } + + SecurityOption option = option_.securityOpt; + if (item == DBConstant::METADB_DIR) { + option.securityLabel = ((option_.securityOpt.securityLabel >= SecurityLabel::S2) ? + SecurityLabel::S2 : option_.securityOpt.securityLabel); + option.securityFlag = SecurityFlag::ECE; + } + + int errCode = RuntimeContext::GetInstance()->SetSecurityOption(option_.subdir + "/" + item, option); + if (errCode != E_OK && errCode != -E_NOT_SUPPORT) { + LOGE("Set the security option of sub-directory failed[%d]", errCode); + return errCode; + } + } + return E_OK; +} + +int SQLiteSingleVerStorageEngine::GetExistedSecOption(SecurityOption &secOption) const +{ + // Check the existence of the database, include the origin database and the database in the 'main' directory. + auto mainDbDir = GetDbDir(option_.subdir, DbType::MAIN); + auto mainDbFilePath = mainDbDir + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + auto origDbFilePath = option_.subdir + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + if (!OS::CheckPathExistence(origDbFilePath) && !OS::CheckPathExistence(mainDbFilePath)) { + secOption = option_.securityOpt; + return E_OK; + } + + // the main database file has high priority of the security option. + int errCode; + if (OS::CheckPathExistence(mainDbFilePath)) { + errCode = GetPathSecurityOption(mainDbFilePath, secOption); + } else { + errCode = GetPathSecurityOption(origDbFilePath, secOption); + } + if (errCode != E_OK) { + secOption = SecurityOption(); + if (errCode == -E_NOT_SUPPORT) { + return E_OK; + } + LOGE("Get the security option of the existed database failed."); + } + return errCode; +} + +void SQLiteSingleVerStorageEngine::ClearCorruptedFlag() +{ + isCorrupted_ = false; +} + +int SQLiteSingleVerStorageEngine::PreCreateExecutor(bool isWrite) +{ + // Assume that create the write executor firstly and the write one we will not be released. + // If the write one would be released in the future, should take care the pass through. + if (!isWrite) { + return E_OK; + } + + if (option_.isMemDb) { + return E_OK; + } + + // Get the existed database secure option. + SecurityOption existedSecOpt; + int errCode = GetExistedSecOption(existedSecOpt); + if (errCode != E_OK) { + return errCode; + } + + errCode = CheckDatabaseSecOpt(existedSecOpt); + if (errCode != E_OK) { + return errCode; + } + + // Judge whether need update the security option of the engine. + // Should update the security in the import or rekey scene(inner). + if (!isNeedUpdateSecOpt_) { + option_.securityOpt = existedSecOpt; + } + + errCode = CreateNewDirsAndSetSecOpt(); + if (errCode != E_OK) { + return errCode; + } + + if (!isUpdated_) { + errCode = SQLiteSingleVerDatabaseUpgrader::TransferDatabasePath(option_.subdir, option_); + if (errCode != E_OK) { + LOGE("[PreCreateExecutor] Transfer Db file path failed[%d].", errCode); + return errCode; + } + } + + return E_OK; +} + +int SQLiteSingleVerStorageEngine::EndCreateExecutor(bool isWrite) +{ + if (option_.isMemDb || !isWrite) { + return E_OK; + } + + int errCode = SQLiteSingleVerDatabaseUpgrader::SetSecOption(option_.subdir, option_.securityOpt, + isNeedUpdateSecOpt_); + if (errCode != E_OK) { + if (errCode == -E_NOT_SUPPORT) { + option_.securityOpt = SecurityOption(); + errCode = E_OK; + } + LOGE("SetSecOption failed:%d", errCode); + return errCode; + } + + // after setting secOption, the database file operation ends + // database create completed, delete the token + if (OS::CheckPathExistence(option_.subdir + DBConstant::PATH_POSTFIX_DB_INCOMPLETE) && + OS::RemoveFile(option_.subdir + DBConstant::PATH_POSTFIX_DB_INCOMPLETE) != E_OK) { + LOGE("Finish to create the complete database, but delete token fail! errCode = [E_SYSTEM_API_FAIL]"); + return -E_SYSTEM_API_FAIL; + } + return errCode; +} + +int SQLiteSingleVerStorageEngine::TryAttachMetaDb(sqlite3 *&dbHandle, bool &isAttachMeta) +{ + // attach or not depend on its true secOpt, but it's not permit while option_.secOpt different from true secOpt + if ((!option_.isMemDb) && (ParamCheckUtils::IsS3SECEOpt(option_.securityOpt))) { + int errCode = AttachMetaDatabase(dbHandle, option_); + if (errCode != E_OK) { + (void)sqlite3_close_v2(dbHandle); + dbHandle = nullptr; + return errCode; + } + isAttachMeta = true; + } + return E_OK; +} + +int SQLiteSingleVerStorageEngine::CreateNewExecutor(bool isWrite, StorageExecutor *&handle) +{ + int errCode = PreCreateExecutor(isWrite); + if (errCode != E_OK) { + return errCode; + } + + sqlite3 *dbHandle = nullptr; + errCode = GetDbHandle(isWrite, option_.securityOpt, dbHandle); + if (errCode != E_OK) { + return errCode; + } + + bool isAttachMeta = false; + errCode = TryAttachMetaDb(dbHandle, isAttachMeta); + if (errCode != E_OK) { + return errCode; + } + + RegisterFunctionIfNeed(dbHandle); + errCode = Upgrade(dbHandle); + if (errCode != E_OK) { + (void)sqlite3_close_v2(dbHandle); + dbHandle = nullptr; + return errCode; + } + + errCode = EndCreateExecutor(isWrite); + if (errCode != E_OK) { + LOGE("After create executor, set security option incomplete!"); + (void)sqlite3_close_v2(dbHandle); + dbHandle = nullptr; + return errCode; + } + + handle = NewSQLiteStorageExecutor(dbHandle, isWrite, option_.isMemDb); + if (handle == nullptr) { + LOGE("New SQLiteStorageExecutor[%d] for the pool failed.", isWrite); + (void)sqlite3_close_v2(dbHandle); + dbHandle = nullptr; + return -E_OUT_OF_MEMORY; + } + if (isAttachMeta) { + SQLiteSingleVerStorageExecutor *singleVerHandle = static_cast(handle); + singleVerHandle->SetAttachMetaMode(isAttachMeta); + } + return E_OK; +} + +int SQLiteSingleVerStorageEngine::Upgrade(sqlite3 *db) +{ + if (isUpdated_ || GetEngineState() == CACHEDB) { + LOGI("Storage engine is in cache status or has been upgraded[%d]!", isUpdated_); + return E_OK; + } + + std::unique_ptr upgrader; + LOGD("[SqlSingleEngine][Upgrade] NewSchemaStrSize=%zu", option_.schema.size()); + if (option_.schema.empty()) { + upgrader = std::make_unique(db, option_.securityOpt, option_.isMemDb); + } else { + SchemaObject schema; + int errCode = schema.ParseFromSchemaString(option_.schema); + if (errCode != E_OK) { + LOGE("Upgrader failed while parsing the origin schema:%d", errCode); + return errCode; + } + upgrader = std::make_unique(db, schema, + option_.securityOpt, option_.isMemDb); + } + + std::string mainDbDir = GetDbDir(option_.subdir, DbType::MAIN); + std::string mainDbFilePath = mainDbDir + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + SecurityOption secOpt = option_.securityOpt; + int errCode = E_OK; + if (isNeedUpdateSecOpt_) { + errCode = GetPathSecurityOption(mainDbFilePath, secOpt); + if (errCode != E_OK) { + LOGI("[SingleVerStorageEngine::Upgrade] Failed to get the path security option, errCode = [%d]", errCode); + if (errCode != -E_NOT_SUPPORT) { + return errCode; + } + secOpt = SecurityOption(); + } + } + + upgrader->SetMetaUpgrade(secOpt, option_.securityOpt, option_.subdir); + upgrader->SetSubdir(option_.subdir); + errCode = upgrader->Upgrade(); + if (errCode != E_OK) { + LOGE("Single ver database upgrade failed:%d", errCode); + return errCode; + } + + LOGD("Finish upgrade single ver database!"); + isUpdated_ = true; // Identification to avoid repeated upgrades + return errCode; +} + +// Attention: This function should be called before "Upgrade". +// Attention: This function should be called for each executor on the sqlite3 handle that the executor binds to. +void SQLiteSingleVerStorageEngine::RegisterFunctionIfNeed(sqlite3 *dbHandle) const +{ + // This function should accept a sqlite3 handle with no perception of database classification. That is, if it is + // not a newly created database, the meta-Table should exist and can be accessed. + std::string schemaStr = option_.schema; + if (schemaStr.empty()) { + // If schema from GetKvStore::Option is empty, we have to try to load it from database. ReadOnly mode if exist; + int errCode = SQLiteUtils::GetSchema(dbHandle, schemaStr); + if (errCode != E_OK) { + LOGD("[SqlSinEngine] Can't get schema from db[%d], maybe it is just created or not a schema-db.", errCode); + } + } + if (!schemaStr.empty()) { + // This must be a Schema-Database, if it is Json-Schema, the Register will do nothing and return E_OK + int errCode = SQLiteUtils::RegisterFlatBufferFunction(dbHandle, schemaStr); + if (errCode != E_OK) { // Not very likely + // Just warning, if no index had been or need to be created, then put or kv-get can still use. + LOGW("[SqlSinEngine] RegisterFlatBufferExtractFunction fail, errCode = %d", errCode); + } + } + + // This function is used to update meta_data in triggers when it's attached to mainDB + int errCode = SQLiteUtils::RegisterMetaDataUpdateFunction(dbHandle); + if (errCode != E_OK) { + LOGW("[SqlSinEngine] RegisterMetaDataUpdateFunction fail, errCode = %d", errCode); + } +} + +int SQLiteSingleVerStorageEngine::AttachMetaDatabase(sqlite3 *dbHandle, const OpenDbProperties &option) const +{ + int errCode; + LOGD("SQLiteSingleVerStorageEngine begin attach metaDb!"); + std::string metaDbPath = option.subdir + "/" + DBConstant::METADB_DIR + "/" + + DBConstant::SINGLE_VER_META_STORE + DBConstant::SQLITE_DB_EXTENSION; + // attach metaDb may failed while createIfNecessary is false, here need to create metaDb first. + if (!option.createIfNecessary && !OS::CheckPathExistence(metaDbPath)) { + errCode = SQLiteUtils::CreateMetaDatabase(metaDbPath); + if (errCode != E_OK) { + return errCode; + } + } + CipherPassword passwd; + errCode = SQLiteUtils::AttachNewDatabase(dbHandle, option.cipherType, passwd, metaDbPath, "meta"); + if (errCode != E_OK) { + LOGE("AttachNewDatabase fail, errCode = %d", errCode); + } + return errCode; +} + +void SQLiteSingleVerStorageEngine::ResetCacheRecordVersion() +{ + (void)cacheRecordVersion_.store(CACHE_RECORD_DEFAULT_VERSION, std::memory_order_seq_cst); +} + +void SQLiteSingleVerStorageEngine::IncreaseCacheRecordVersion() +{ + (void)cacheRecordVersion_.fetch_add(1, std::memory_order_seq_cst); +} + +uint64_t SQLiteSingleVerStorageEngine::GetAndIncreaseCacheRecordVersion() +{ + return cacheRecordVersion_.fetch_add(1, std::memory_order_seq_cst); +} + +uint64_t SQLiteSingleVerStorageEngine::GetCacheRecordVersion() const +{ + return cacheRecordVersion_.load(std::memory_order_seq_cst); +} + +void SQLiteSingleVerStorageEngine::CommitAndReleaseNotifyData(SingleVerNaturalStoreCommitNotifyData *&committedData, + int eventType) const +{ + std::shared_lock lock(notifyMutex_); + if (commitNotifyFunc_ == nullptr) { + LOGE("commitNotifyFunc_ is nullptr, can't notify now."); + RefObject::DecObjRef(committedData); + committedData = nullptr; + return; + } + commitNotifyFunc_(eventType, static_cast(committedData)); + committedData = nullptr; + return; +} + +void SQLiteSingleVerStorageEngine::InitConflictNotifiedFlag(SingleVerNaturalStoreCommitNotifyData *&committedData) const +{ + if (committedData == nullptr) { + LOGI("[SQLiteSingleVerStorageEngine::InitConflictNotifiedFlag] committedData is null."); + return; + } + auto identifier = GetIdentifier(); + auto kvdb = KvDBManager::GetInstance()->FindKvDB(identifier); + if (kvdb == nullptr) { + LOGE("[SQLiteSingleVerStorageEngine::InitConflictNotifiedFlag] kvdb is null."); + return; + } + unsigned int conflictFlag = 0; + if (static_cast(kvdb)->GetRegisterFunctionCount(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ONLY) != 0) { + conflictFlag |= static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ONLY); + } + if (static_cast(kvdb)->GetRegisterFunctionCount(CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ORIG) != 0) { + conflictFlag |= static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ORIG); + } + if (static_cast(kvdb)->GetRegisterFunctionCount(CONFLICT_SINGLE_VERSION_NS_NATIVE_ALL) != 0) { + conflictFlag |= static_cast(SQLITE_GENERAL_NS_NATIVE_ALL); + } + RefObject::DecObjRef(kvdb); + LOGD("[SQLiteSingleVerStorageEngine::InitConflictNotifiedFlag] conflictFlag Flag: %u", conflictFlag); + committedData->SetConflictedNotifiedFlag(static_cast(conflictFlag)); +} + +void SQLiteSingleVerStorageEngine::SetMaxTimestamp(Timestamp maxTimestamp) const +{ + auto kvdbManager = KvDBManager::GetInstance(); + if (kvdbManager == nullptr) { + return; + } + auto identifier = GetIdentifier(); + auto kvdb = kvdbManager->FindKvDB(identifier); + if (kvdb == nullptr) { + LOGE("[SQLiteSingleVerStorageEngine::SetMaxTimestamp] kvdb is null."); + return; + } + + auto kvStore = static_cast(kvdb); + kvStore->SetMaxTimestamp(maxTimestamp); + RefObject::DecObjRef(kvdb); + return; +} + +void SQLiteSingleVerStorageEngine::CommitNotifyForMigrateCache(NotifyMigrateSyncData &syncData) const +{ + const auto &isRemote = syncData.isRemote; + const auto &isRemoveDeviceData = syncData.isRemoveDeviceData; + auto &committedData = syncData.committedData; + auto &entries = syncData.entries; + + // Put data. Including insert, update and delete. + if (!isRemoveDeviceData) { + if (committedData != nullptr) { + int eventType = isRemote ? SQLITE_GENERAL_NS_SYNC_EVENT : SQLITE_GENERAL_NS_PUT_EVENT; + CommitAndReleaseNotifyData(committedData, eventType); + } + return; + } + + // Remove device data. + if (entries.empty() || entries.size() > MAX_TOTAL_NOTIFY_ITEM_SIZE) { + return; + } + size_t totalSize = 0; + for (auto iter = entries.begin(); iter != entries.end();) { + auto &entry = *iter; + if (committedData == nullptr) { + committedData = new (std::nothrow) SingleVerNaturalStoreCommitNotifyData(); + if (committedData == nullptr) { + LOGE("Alloc committed notify data failed."); + return; + } + } + if (entry.key.size() > DBConstant::MAX_KEY_SIZE || entry.value.size() > DBConstant::MAX_VALUE_SIZE) { + iter++; + continue; + } + if (entry.key.size() + entry.value.size() + totalSize > MAX_TOTAL_NOTIFY_DATA_SIZE) { + CommitAndReleaseNotifyData(committedData, SQLITE_GENERAL_NS_SYNC_EVENT); + totalSize = 0; + continue; + } + totalSize += (entry.key.size() + entry.value.size()); + committedData->InsertCommittedData(std::move(entry), DataType::DELETE, false); + iter++; + } + if (committedData != nullptr) { + CommitAndReleaseNotifyData(committedData, SQLITE_GENERAL_NS_SYNC_EVENT); + } + return; +} + +// Cache subscribe when engine state is CACHE mode, and its will be applied at the beginning of migrate. +void SQLiteSingleVerStorageEngine::CacheSubscribe(const std::string &subscribeId, const QueryObject &query) +{ + std::lock_guard lock(subscribeMutex_); + subscribeQuery_[subscribeId] = query; +} +} diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..1418ae4bdb7d0cbb51321f8a68e2041382b22afc --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_SINGLE_VER_STORAGE_ENGINE_H +#define SQLITE_SINGLE_VER_STORAGE_ENGINE_H + +#include "macro_utils.h" +#include "sqlite_storage_engine.h" +#include "sqlite_single_ver_storage_executor.h" + +namespace DistributedDB { +enum SQLiteGeneralNSNotificationEventType { + SQLITE_GENERAL_NS_PUT_EVENT = 0x01, + SQLITE_GENERAL_NS_SYNC_EVENT = 0x02, + SQLITE_GENERAL_NS_LOCAL_PUT_EVENT = 0x04, + SQLITE_GENERAL_CONFLICT_EVENT = 0x08, // Conflict event + SQLITE_GENERAL_FINISH_MIGRATE_EVENT = 0x10, // Only trigger sync event +}; +enum SQLiteGeneralNSConflictType { + SQLITE_GENERAL_NS_FOREIGN_KEY_ONLY = 0x01, // sync conflict for same origin dev + SQLITE_GENERAL_NS_FOREIGN_KEY_ORIG = 0x02, // sync conflict for different origin dev + SQLITE_GENERAL_NS_NATIVE_ALL = 0x0c, // native conflict. +}; +class SQLiteSingleVerStorageEngine : public SQLiteStorageEngine { +public: + SQLiteSingleVerStorageEngine(); + ~SQLiteSingleVerStorageEngine() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleVerStorageEngine); + + void IncreaseCacheRecordVersion() override; + uint64_t GetCacheRecordVersion() const override; + uint64_t GetAndIncreaseCacheRecordVersion() override; + + int ExecuteMigrate() override; + bool IsEngineCorrupted() const override; + + const SecurityOption &GetSecurityOption() const + { + return option_.securityOpt; + } + + void SetNeedUpdateSecOption(bool flag) + { + isNeedUpdateSecOpt_ = flag; + } + + void CacheSubscribe(const std::string &subscribeId, const QueryObject &query); + +protected: + StorageExecutor *NewSQLiteStorageExecutor(sqlite3 *dbHandle, bool isWrite, bool isMemDb) override; + + int Upgrade(sqlite3 *db) override; + + int CreateNewExecutor(bool isWrite, StorageExecutor *&handle) override; + +private: + // For executor. + int PreCreateExecutor(bool isWrite); + int EndCreateExecutor(bool isWrite); + int ReInit() override; + int ReleaseExecutor(SQLiteSingleVerStorageExecutor *&handle); + int ReleaseHandleTransiently(SQLiteSingleVerStorageExecutor *&handle, uint64_t idleTime); + + // For migrate. + int MigrateLocalData(SQLiteSingleVerStorageExecutor *handle) const; + int MigrateSyncDataByVersion(SQLiteSingleVerStorageExecutor *&handle, + NotifyMigrateSyncData &syncData, uint64_t &curMigrateVer); + int MigrateSyncData(SQLiteSingleVerStorageExecutor *&handle, bool &isNeedTriggerSync); + int FinishMigrateData(SQLiteSingleVerStorageExecutor *&handle, EngineState stateBeforeMigrate); + int InitExecuteMigrate(SQLiteSingleVerStorageExecutor *handle, EngineState preMigrateState); + void EndMigrate(SQLiteSingleVerStorageExecutor *&handle, EngineState stateBeforeMigrate, int errCode, + bool isNeedTriggerSync); + void ResetCacheRecordVersion(); + void SetMaxTimestamp(Timestamp maxTimestamp) const; + int EraseDeviceWaterMark(SQLiteSingleVerStorageExecutor *&handle, const std::vector &dataItems); + + // For db. + int TryToOpenMainDatabase(bool isWrite, sqlite3 *&db); + int GetCacheDbHandle(sqlite3 *&db); + int GetDbHandle(bool isWrite, const SecurityOption &secOpt, sqlite3 *&dbHandle); + int AttachMetaDatabase(sqlite3 *dbHandle, const OpenDbProperties &option) const; + int AttachMainDbAndCacheDb(SQLiteSingleVerStorageExecutor *handle, EngineState stateBeforeMigrate); + int AttachMainDbAndCacheDb(sqlite3 *db, EngineState stateBeforeMigrate) const; + void RegisterFunctionIfNeed(sqlite3 *dbHandle) const; + int TryAttachMetaDb(sqlite3 *&dbHandle, bool &isAttachMeta); + + // For secOpt. + int CreateNewDirsAndSetSecOpt() const; + int CheckDatabaseSecOpt(const SecurityOption &secOption) const; + int GetExistedSecOption(SecurityOption &secOption) const; + + void ClearCorruptedFlag() override; + + // For commit notify. + void CommitAndReleaseNotifyData(SingleVerNaturalStoreCommitNotifyData *&committedData, int eventType) const; + void InitConflictNotifiedFlag(SingleVerNaturalStoreCommitNotifyData *&committedData) const; + void CommitNotifyForMigrateCache(NotifyMigrateSyncData &syncData) const; + + // For subscribe + int AddSubscribeToMainDBInMigrate(); + + mutable std::mutex migrateLock_; + std::atomic cacheRecordVersion_; + ExecutorState executorState_; + bool isCorrupted_; + bool isNeedUpdateSecOpt_; // update the option_ + + std::mutex subscribeMutex_; + std::map subscribeQuery_; +}; +} // namespace DistributedDB + +#endif // SQLITE_SINGLE_VER_STORAGE_ENGINE_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..63d10262046b8cdf0c43ba90875044bdc005b2dc --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.cpp @@ -0,0 +1,2168 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_storage_executor.h" + +#include + +#include "log_print.h" +#include "db_constant.h" +#include "db_common.h" +#include "db_errno.h" +#include "parcel.h" +#include "platform_specific.h" +#include "runtime_context.h" +#include "sqlite_single_ver_storage_executor_sql.h" + +namespace DistributedDB { +namespace { +void InitCommitNotifyDataKeyStatus(SingleVerNaturalStoreCommitNotifyData *committedData, const Key &hashKey, + const DataOperStatus &dataStatus) +{ + if (committedData == nullptr) { + return; + } + + ExistStatus existedStatus = ExistStatus::NONE; + if (dataStatus.preStatus == DataStatus::DELETED) { + existedStatus = ExistStatus::DELETED; + } else if (dataStatus.preStatus == DataStatus::EXISTED) { + existedStatus = ExistStatus::EXIST; + } + + committedData->InitKeyPropRecord(hashKey, existedStatus); +} + +int ResetOrRegetStmt(sqlite3 *db, sqlite3_stmt *&stmt, const std::string &sql) +{ + int errCode = E_OK; + SQLiteUtils::ResetStatement(stmt, false, errCode); + if (errCode != E_OK) { + LOGE("[ResetOrRegetStmt] reset stmt failed:%d.", errCode); + // Finish current statement and remade one + SQLiteUtils::ResetStatement(stmt, true, errCode); + errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + LOGE("[ResetOrRegetStmt] reget failed:%d.", errCode); + } + } + return errCode; +} +} + +SQLiteSingleVerStorageExecutor::SQLiteSingleVerStorageExecutor(sqlite3 *dbHandle, bool writable, bool isMemDb) + : SQLiteStorageExecutor(dbHandle, writable, isMemDb), + getSyncStatement_(nullptr), + getResultRowIdStatement_(nullptr), + getResultEntryStatement_(nullptr), + isTransactionOpen_(false), + attachMetaMode_(false), + executorState_(ExecutorState::INVALID), + maxTimestampInMainDB_(0), + migrateTimeOffset_(0), + isSyncMigrating_(false), + conflictResolvePolicy_(DEFAULT_LAST_WIN) +{} + +SQLiteSingleVerStorageExecutor::SQLiteSingleVerStorageExecutor(sqlite3 *dbHandle, bool writable, bool isMemDb, + ExecutorState executorState) + : SQLiteStorageExecutor(dbHandle, writable, isMemDb), + getSyncStatement_(nullptr), + getResultRowIdStatement_(nullptr), + getResultEntryStatement_(nullptr), + isTransactionOpen_(false), + attachMetaMode_(false), + executorState_(executorState), + maxTimestampInMainDB_(0), + migrateTimeOffset_(0), + isSyncMigrating_(false), + conflictResolvePolicy_(DEFAULT_LAST_WIN) +{} + +SQLiteSingleVerStorageExecutor::~SQLiteSingleVerStorageExecutor() +{ + if (isTransactionOpen_) { + Rollback(); + } + FinalizeAllStatements(); +} + +int SQLiteSingleVerStorageExecutor::GetKvData(SingleVerDataType type, const Key &key, Value &value, + Timestamp ×tamp) const +{ + std::string sql; + if (type == SingleVerDataType::LOCAL_TYPE) { + sql = SELECT_LOCAL_VALUE_TIMESTAMP_SQL; + } else if (type == SingleVerDataType::SYNC_TYPE) { + sql = SELECT_SYNC_VALUE_WTIMESTAMP_SQL; + } else if (type == SingleVerDataType::META_TYPE) { + if (attachMetaMode_) { + sql = SELECT_ATTACH_META_VALUE_SQL; + } else { + sql = SELECT_META_VALUE_SQL; + } + } else { + return -E_INVALID_ARGS; + } + + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // first arg. + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = -E_NOT_FOUND; + goto END; + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + goto END; + } + + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, value); // only one result. + + // get timestamp + if (type == SingleVerDataType::LOCAL_TYPE) { + timestamp = static_cast(sqlite3_column_int64(statement, GET_KV_RES_LOCAL_TIME_INDEX)); + } else if (type == SingleVerDataType::SYNC_TYPE) { + timestamp = static_cast(sqlite3_column_int64(statement, GET_KV_RES_SYNC_TIME_INDEX)); + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::BindPutKvData(sqlite3_stmt *statement, const Key &key, const Value &value, + Timestamp timestamp, SingleVerDataType type) +{ + int errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_KV_KEY_INDEX, key, false); + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindPutKv]Bind key error:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_KV_VAL_INDEX, value, true); + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindPutKv]Bind value error:%d", errCode); + return errCode; + } + + if (type == SingleVerDataType::LOCAL_TYPE) { + Key hashKey; + errCode = DBCommon::CalcValueHash(key, hashKey); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_LOCAL_HASH_KEY_INDEX, hashKey, false); + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindPutKv]Bind hash key error:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_LOCAL_TIMESTAMP_INDEX, timestamp); + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindPutKv]Bind timestamp error:%d", errCode); + return errCode; + } + } + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::GetKvDataByHashKey(const Key &hashKey, SingleVerRecord &result) const +{ + sqlite3_stmt *statement = nullptr; + std::vector devVect; + std::vector origDevVect; + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_SYNC_HASH_SQL, statement); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, hashKey, false); // bind the first arg hashkey. + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + result.hashKey = hashKey; + result.timestamp = static_cast(sqlite3_column_int64(statement, SYNC_RES_TIME_INDEX)); + result.writeTimestamp = static_cast(sqlite3_column_int64(statement, SYNC_RES_W_TIME_INDEX)); + result.flag = static_cast(sqlite3_column_int64(statement, SYNC_RES_FLAG_INDEX)); + // get key + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_KEY_INDEX, result.key); + if (errCode != E_OK) { + goto END; + } + // get value + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_VAL_INDEX, result.value); + if (errCode != E_OK) { + goto END; + } + // get device + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_DEVICE_INDEX, devVect); + if (errCode != E_OK) { + goto END; + } + result.device = std::string(devVect.begin(), devVect.end()); + // get original device + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_ORI_DEV_INDEX, origDevVect); + if (errCode != E_OK) { + goto END; + } + result.origDevice = std::string(origDevVect.begin(), origDevVect.end()); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = -E_NOT_FOUND; + goto END; + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::SaveKvData(SingleVerDataType type, const Key &key, const Value &value, + Timestamp timestamp) +{ + sqlite3_stmt *statement = nullptr; + std::string sql; + if (type == SingleVerDataType::LOCAL_TYPE) { + sql = (executorState_ == ExecutorState::CACHE_ATTACH_MAIN ? INSERT_LOCAL_SQL_FROM_CACHEHANDLE : + INSERT_LOCAL_SQL); + } else { + sql = (attachMetaMode_ ? INSERT_ATTACH_META_SQL : INSERT_META_SQL); + } + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = BindPutKvData(statement, key, value, timestamp, type); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::PutKvData(SingleVerDataType type, const Key &key, const Value &value, + Timestamp timestamp, SingleVerNaturalStoreCommitNotifyData *committedData) +{ + if (type != SingleVerDataType::LOCAL_TYPE && type != SingleVerDataType::META_TYPE) { + return -E_INVALID_ARGS; + } + // committedData is only for local data, not for meta data. + bool isLocal = (SingleVerDataType::LOCAL_TYPE == type); + Timestamp localTimestamp = 0; + Value readValue; + bool isExisted = CheckIfKeyExisted(key, isLocal, readValue, localTimestamp); + if (isLocal && committedData != nullptr) { + ExistStatus existedStatus = isExisted ? ExistStatus::EXIST : ExistStatus::NONE; + Key hashKey; + int innerErrCode = DBCommon::CalcValueHash(key, hashKey); + if (innerErrCode != E_OK) { + return innerErrCode; + } + committedData->InitKeyPropRecord(hashKey, existedStatus); + } + int errCode = SaveKvData(type, key, value, timestamp); + if (errCode != E_OK) { + return errCode; + } + + if (isLocal && committedData != nullptr) { + Entry entry = {key, value}; + committedData->InsertCommittedData(std::move(entry), isExisted ? DataType::UPDATE : DataType::INSERT, true); + } + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::GetEntries(SingleVerDataType type, const Key &keyPrefix, + std::vector &entries) const +{ + if ((type != SingleVerDataType::LOCAL_TYPE) && (type != SingleVerDataType::SYNC_TYPE)) { + return -E_INVALID_ARGS; + } + + std::string sql = (type == SingleVerDataType::SYNC_TYPE) ? SELECT_SYNC_PREFIX_SQL : SELECT_LOCAL_PREFIX_SQL; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + goto END; + } + + // bind the prefix key for the first and second args. + errCode = SQLiteUtils::BindPrefixKey(statement, 1, keyPrefix); // first argument is key + if (errCode != E_OK) { + goto END; + } + + errCode = StepForResultEntries(statement, entries); + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::GetEntries(QueryObject &queryObj, std::vector &entries) const +{ + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + + sqlite3_stmt *statement = nullptr; + errCode = helper.GetQuerySqlStatement(dbHandle_, false, statement); + if (errCode == E_OK) { + errCode = StepForResultEntries(statement, entries); + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::GetCount(QueryObject &queryObj, int &count) const +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + + if (!queryObj.IsCountValid()) { + LOGE("GetCount no need limit or orderby"); + return -E_INVALID_QUERY_FORMAT; + } + + std::string countSql; + errCode = helper.GetCountQuerySql(countSql); + if (errCode != E_OK) { + return errCode; + } + + sqlite3_stmt *countStatement = nullptr; + // get statement for count + errCode = helper.GetQuerySqlStatement(dbHandle_, countSql, countStatement); + if (errCode != E_OK) { + LOGE("Get count bind statement error:%d", errCode); + goto END; + } + // get count value + errCode = SQLiteUtils::StepWithRetry(countStatement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + uint64_t readCount = static_cast(sqlite3_column_int64(countStatement, 0)); + if (readCount > INT32_MAX) { + LOGW("total count is beyond the max count"); + count = 0; + errCode = -E_UNEXPECTED_DATA; + } else { + count = static_cast(readCount); + errCode = E_OK; + } + LOGD("Entry count in this result set is %d", count); + } else { + errCode = -E_UNEXPECTED_DATA; + } + +END: + SQLiteUtils::ResetStatement(countStatement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +void SQLiteSingleVerStorageExecutor::InitCurrentMaxStamp(Timestamp &maxStamp) +{ + if (dbHandle_ == nullptr) { + return; + } + std::string sql = ((executorState_ == ExecutorState::CACHE_ATTACH_MAIN) ? + SELECT_MAX_TIMESTAMP_SQL_FROM_CACHEHANDLE : SELECT_MAX_TIMESTAMP_SQL); + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + return; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + maxStamp = static_cast(sqlite3_column_int64(statement, 0)); // get the first column + } + SQLiteUtils::ResetStatement(statement, true, errCode); +} + +int SQLiteSingleVerStorageExecutor::PrepareForSyncDataByTime(Timestamp begin, Timestamp end, + sqlite3_stmt *&statement, bool getDeletedData) const +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + const std::string sql = (getDeletedData ? SELECT_SYNC_DELETED_ENTRIES_SQL : SELECT_SYNC_ENTRIES_SQL); + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + LOGE("Prepare the sync entries statement error:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_BEGIN_STAMP_INDEX, begin); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_END_STAMP_INDEX, end); + +ERROR: + if (errCode != E_OK) { + LOGE("Bind the timestamp for getting sync data error:%d", errCode); + SQLiteUtils::ResetStatement(statement, true, errCode); + } + + return CheckCorruptedStatus(errCode); +} + +void SQLiteSingleVerStorageExecutor::ReleaseContinueStatement() +{ + if (getSyncStatement_ != nullptr) { + int errCode = E_OK; + SQLiteUtils::ResetStatement(getSyncStatement_, true, errCode); + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + SetCorruptedStatus(); + } + } +} + +namespace { +int GetDataItemForSync(sqlite3_stmt *statement, DataItem &dataItem) +{ + dataItem.timestamp = static_cast(sqlite3_column_int64(statement, SYNC_RES_TIME_INDEX)); + dataItem.writeTimestamp = static_cast(sqlite3_column_int64(statement, SYNC_RES_W_TIME_INDEX)); + dataItem.flag = static_cast(sqlite3_column_int64(statement, SYNC_RES_FLAG_INDEX)); + dataItem.flag &= (~DataItem::LOCAL_FLAG); + std::vector devVect; + int errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_ORI_DEV_INDEX, devVect); + if (errCode != E_OK) { + return errCode; + } + dataItem.origDev = std::string(devVect.begin(), devVect.end()); + int keyIndex = SYNC_RES_KEY_INDEX; + // If the data has been deleted, just use the hash key for sync. + if ((dataItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG) { + keyIndex = SYNC_RES_HASH_KEY_INDEX; + } + errCode = SQLiteUtils::GetColumnBlobValue(statement, keyIndex, dataItem.key); + if (errCode != E_OK) { + return errCode; + } + return SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_VAL_INDEX, dataItem.value); +} +} + +int SQLiteSingleVerStorageExecutor::GetSyncDataItems(std::vector &dataItems, sqlite3_stmt *statement, + size_t appendLength, const DataSizeSpecInfo &dataSizeInfo) const +{ + int errCode; + size_t dataTotalSize = 0; + + do { + DataItem dataItem; + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = GetDataItemForSync(statement, dataItem); + if (errCode != E_OK) { + LOGE("GetDataItemForSync failed:%d", errCode); + return errCode; + } + } else { + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGD("Get sync data finished, size of packet:%zu, number of item:%zu", dataTotalSize, dataItems.size()); + errCode = -E_FINISHED; + } else { + LOGE("Get sync data error:%d", errCode); + } + break; + } + + // If dataTotalSize value is bigger than blockSize value , reserve the surplus data item. + dataTotalSize += GetDataItemSerialSize(dataItem, appendLength); + if ((dataTotalSize > dataSizeInfo.blockSize && !dataItems.empty()) || + dataItems.size() >= dataSizeInfo.packetSize) { + errCode = -E_UNFINISHED; + break; + } else { + dataItems.push_back(std::move(dataItem)); + } + } while (true); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::GetSyncDataByTimestamp(std::vector &dataItems, size_t appendLength, + Timestamp begin, Timestamp end, const DataSizeSpecInfo &dataSizeInfo) const +{ + sqlite3_stmt *statement = nullptr; + int errCode = PrepareForSyncDataByTime(begin, end, statement); + if (errCode != E_OK) { + return errCode; + } + + errCode = GetSyncDataItems(dataItems, statement, appendLength, dataSizeInfo); + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::GetDeletedSyncDataByTimestamp(std::vector &dataItems, size_t appendLength, + Timestamp begin, Timestamp end, const DataSizeSpecInfo &dataSizeInfo) const +{ + sqlite3_stmt *statement = nullptr; + int errCode = PrepareForSyncDataByTime(begin, end, statement, true); + if (errCode != E_OK) { + return errCode; + } + + errCode = GetSyncDataItems(dataItems, statement, appendLength, dataSizeInfo); + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +namespace { +int AppendDataItem(std::vector &dataItems, const DataItem &item, size_t &dataTotalSize, size_t appendLength, + const DataSizeSpecInfo &dataSizeInfo) +{ + // If dataTotalSize value is bigger than blockSize value , reserve the surplus data item. + size_t appendSize = dataTotalSize + SQLiteSingleVerStorageExecutor::GetDataItemSerialSize(item, appendLength); + if ((appendSize > dataSizeInfo.blockSize && !dataItems.empty()) || dataItems.size() >= dataSizeInfo.packetSize) { + return -E_UNFINISHED; + } + dataItems.push_back(item); + dataTotalSize = appendSize; + return E_OK; +} + +int GetFullDataStatement(sqlite3 *db, const std::pair &timeRange, sqlite3_stmt *&stmt) +{ + int errCode = SQLiteUtils::GetStatement(db, SELECT_SYNC_MODIFY_SQL, stmt); + if (errCode != E_OK) { + LOGE("Get statement failed. %d", errCode); + return errCode; + } + errCode = SQLiteUtils::BindInt64ToStatement(stmt, 1, timeRange.first); // 1 : Bind time rang index start + if (errCode != E_OK) { + LOGE("Bind time range to statement failed. %d", errCode); + goto ERR; + } + errCode = SQLiteUtils::BindInt64ToStatement(stmt, 2, timeRange.second); // 2 : Bind time rang index end + if (errCode != E_OK) { + LOGE("Bind time range to statement failed. %d", errCode); + goto ERR; + } + return E_OK; // do not release statement when success +ERR: + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; +} + +int GetQueryDataStatement(sqlite3 *db, QueryObject query, const std::pair &timeRange, + sqlite3_stmt *&stmt) +{ + int errCode = E_OK; + SqliteQueryHelper helper = query.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + return helper.GetQuerySyncStatement(db, timeRange.first, timeRange.second, stmt); +} + +int GetNextDataItem(sqlite3_stmt *stmt, bool isMemDB, DataItem &item) +{ + int errCode = SQLiteUtils::StepWithRetry(stmt, isMemDB); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = GetDataItemForSync(stmt, item); + } + return errCode; +} +} + +int SQLiteSingleVerStorageExecutor::GetSyncDataWithQuery(const QueryObject &query, size_t appendLength, + const DataSizeSpecInfo &dataSizeInfo, const std::pair &timeRange, + std::vector &dataItems) const +{ + sqlite3_stmt *fullStmt = nullptr; // statement for get all modified data in the time range + sqlite3_stmt *queryStmt = nullptr; // statement for get modified data which is matched query in the time range + int errCode = GetQueryDataStatement(dbHandle_, query, timeRange, queryStmt); + if (errCode != E_OK) { + LOGE("Get query matched data statement failed. %d", errCode); + goto END; + } + if (query.IsQueryOnlyByKey()) { + // Query sync by prefixKey only should not deal with REMOTE_DEVICE_DATA_MISS_QUERY. Get the data directly. + errCode = GetSyncDataItems(dataItems, queryStmt, appendLength, dataSizeInfo); + goto END; + } + errCode = GetFullDataStatement(dbHandle_, timeRange, fullStmt); + if (errCode != E_OK) { + LOGE("Get full changed data statement failed. %d", errCode); + goto END; + } + errCode = GetSyncDataWithQuery(fullStmt, queryStmt, appendLength, dataSizeInfo, dataItems); + if (errCode != E_OK && errCode != -E_UNFINISHED && errCode != -E_FINISHED) { + LOGE("Get sync data with query failed. %d", errCode); + } +END: + SQLiteUtils::ResetStatement(fullStmt, true, errCode); + SQLiteUtils::ResetStatement(queryStmt, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::GetSyncDataWithQuery(sqlite3_stmt *fullStmt, sqlite3_stmt *queryStmt, + size_t appendLength, const DataSizeSpecInfo &dataSizeInfo, std::vector &dataItems) const +{ + int errCode = E_OK; + size_t dataTotalSize = 0; + DataItem fullItem; + DataItem matchItem; + bool isFullItemFinished = false; + bool isMatchItemFinished = false; + while (!isFullItemFinished || !isMatchItemFinished) { + errCode = GetNextDataItem(queryStmt, isMemDb_, matchItem); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { // query finished + isMatchItemFinished = true; + } else if (errCode != E_OK) { // step failed or get data failed + LOGE("Get next query matched data failed. %d", errCode); + return errCode; + } + while (!isFullItemFinished) { + errCode = GetNextDataItem(fullStmt, isMemDb_, fullItem); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { // queryStmt is a subset of fullStmt + isFullItemFinished = true; + break; + } else if (errCode != E_OK) { // step failed or get data failed + LOGE("Get next changed data failed. %d", errCode); + return errCode; + } + bool matchData = true; + if (isMatchItemFinished || matchItem.key != fullItem.key) { + matchData = false; // got miss query data + DBCommon::CalcValueHash(fullItem.key, fullItem.key); // set and send key with hash_key + Value().swap(fullItem.value); // not send value when data miss query + fullItem.flag |= DataItem::REMOTE_DEVICE_DATA_MISS_QUERY; // mark with miss query flag + } + errCode = AppendDataItem(dataItems, fullItem, dataTotalSize, appendLength, dataSizeInfo); + if (errCode == -E_UNFINISHED) { + goto END; + } + if (matchData) { + break; // step to next match data + } + } + } +END: + LOGD("Get sync data finished, size of packet:%zu, number of item:%zu", dataTotalSize, dataItems.size()); + return (isFullItemFinished && isMatchItemFinished) ? -E_FINISHED : errCode; +} + +int SQLiteSingleVerStorageExecutor::OpenResultSet(const Key &keyPrefix, int &count) +{ + sqlite3_stmt *countStatement = nullptr; + if (InitResultSet(keyPrefix, countStatement) != E_OK) { + LOGE("Initialize result set stat failed."); + return -E_INVALID_DB; + } + + int errCode = StartTransaction(TransactType::DEFERRED); + if (errCode != E_OK) { + goto END; + } + + // get count value + errCode = SQLiteUtils::StepWithRetry(countStatement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + uint64_t readCount = static_cast(sqlite3_column_int64(countStatement, 0)); + if (readCount > INT32_MAX) { + LOGW("total count is beyond the max count"); + count = 0; + errCode = -E_UNEXPECTED_DATA; + } else { + count = static_cast(readCount); + errCode = E_OK; + } + LOGD("Entry count in this result set is %d", count); + } else { + errCode = -E_UNEXPECTED_DATA; + } + +END: + SQLiteUtils::ResetStatement(countStatement, true, errCode); + if (errCode != E_OK) { + CloseResultSet(); + } + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::OpenResultSet(QueryObject &queryObj, int &count) +{ + sqlite3_stmt *countStatement = nullptr; + int errCode = InitResultSet(queryObj, countStatement); + if (errCode != E_OK) { + LOGE("Initialize result set stat failed."); + return errCode; + } + + errCode = StartTransaction(TransactType::DEFERRED); + if (errCode != E_OK) { + goto END; + } + + // get count value + errCode = SQLiteUtils::StepWithRetry(countStatement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + uint64_t readCount = static_cast(sqlite3_column_int64(countStatement, 0)); + if (queryObj.HasLimit()) { + int limit = 0; + int offset = 0; + queryObj.GetLimitVal(limit, offset); + offset = (offset < 0) ? 0 : offset; + limit = (limit < 0) ? 0 : limit; + if (readCount <= static_cast(offset)) { + readCount = 0; + } else { + readCount = std::min(readCount - offset, static_cast(limit)); + } + } + + if (readCount > INT32_MAX) { + LOGW("total count is beyond the max count"); + count = 0; + errCode = -E_UNEXPECTED_DATA; + } else { + count = static_cast(readCount); + errCode = E_OK; + } + LOGD("Entry count in this result set is %d", count); + } else { + errCode = -E_UNEXPECTED_DATA; + } + +END: + SQLiteUtils::ResetStatement(countStatement, true, errCode); + if (errCode != E_OK) { + CloseResultSet(); + } + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::OpenResultSetForCacheRowIdMode(const Key &keyPrefix, + std::vector &rowIdCache, uint32_t cacheLimit, int &count) +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_SYNC_ROWID_PREFIX_SQL, getResultRowIdStatement_); + if (errCode != E_OK) { + LOGE("[SqlSinExe][OpenResSetRowId][PrefixKey] Get rowId stmt fail, errCode=%d", errCode); + return CheckCorruptedStatus(errCode); + } + errCode = SQLiteUtils::BindPrefixKey(getResultRowIdStatement_, 1, keyPrefix); // first argument index is 1 + if (errCode != E_OK) { + LOGE("[SqlSinExe][OpenResSetRowId][PrefixKey] Bind rowid stmt fail, errCode=%d", errCode); + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + return CheckCorruptedStatus(errCode); + } + errCode = OpenResultSetForCacheRowIdModeCommon(rowIdCache, cacheLimit, count); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::OpenResultSetForCacheRowIdMode(QueryObject &queryObj, + std::vector &rowIdCache, uint32_t cacheLimit, int &count) +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + + if (!queryObj.IsValid()) { + LOGE("[SqlSinExe][OpenResSetRowId][Query] query object not Valid"); + return -E_INVALID_QUERY_FORMAT; + } + + errCode = helper.GetQuerySqlStatement(dbHandle_, true, getResultRowIdStatement_); + if (errCode != E_OK) { + LOGE("[SqlSinExe][OpenResSetRowId][Query] Get Stmt fail, errCode=%d", errCode); + // The GetQuerySqlStatement does not self rollback(BAD...), so we have to reset the stmt here. + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + return errCode; + } + errCode = OpenResultSetForCacheRowIdModeCommon(rowIdCache, cacheLimit, count); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::ReloadResultSet(const Key &keyPrefix) +{ + int errCode = ResetOrRegetStmt(dbHandle_, getResultRowIdStatement_, SELECT_SYNC_ROWID_PREFIX_SQL); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + // No need to reset getResultEntryStatement_. Because the binding of it will be cleared in each get operation + errCode = SQLiteUtils::BindPrefixKey(getResultRowIdStatement_, 1, keyPrefix); // first argument is key + if (errCode != E_OK) { + LOGE("Rebind result set rowid statement of keyPrefix error:%d", errCode); + return CheckCorruptedStatus(errCode); + } + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::ReloadResultSet(QueryObject &queryObj) +{ + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + + if (!queryObj.IsValid()) { + return -E_INVALID_QUERY_FORMAT; + } + + std::string sql; + errCode = helper.GetQuerySql(sql, true); // only rowid sql + if (errCode != E_OK) { + return errCode; + } + + errCode = ResetOrRegetStmt(dbHandle_, getResultRowIdStatement_, sql); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + // No need to reset getResultEntryStatement_. Because the binding of it will be cleared in each get operation + // GetQuerySqlStatement will not alter getResultRowIdStatement_ if it is not null + errCode = helper.GetQuerySqlStatement(dbHandle_, true, getResultRowIdStatement_); + if (errCode != E_OK) { + LOGE("Rebind result set rowid statement of query error:%d", errCode); + return CheckCorruptedStatus(errCode); + } + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::ReloadResultSetForCacheRowIdMode(const Key &keyPrefix, + std::vector &rowIdCache, uint32_t cacheLimit, uint32_t cacheStartPos) +{ + int errCode = ReloadResultSet(keyPrefix); // Reuse this function(A convenience) + if (errCode != E_OK) { + return errCode; + } + int count = 0; // Ignored + errCode = ResultSetLoadRowIdCache(rowIdCache, cacheLimit, cacheStartPos, count); + if (errCode != E_OK) { + LOGE("[SqlSinExe][ReloadResSet][KeyPrefix] Load fail, errCode=%d", errCode); + } + // We can just return, no need to reset the statement + return errCode; +} + +int SQLiteSingleVerStorageExecutor::ReloadResultSetForCacheRowIdMode(QueryObject &queryObj, + std::vector &rowIdCache, uint32_t cacheLimit, uint32_t cacheStartPos) +{ + int errCode = ReloadResultSet(queryObj); // Reuse this function(A convenience) + if (errCode != E_OK) { + return errCode; + } + int count = 0; // Ignored + errCode = ResultSetLoadRowIdCache(rowIdCache, cacheLimit, cacheStartPos, count); + if (errCode != E_OK) { + LOGE("[SqlSinExe][ReloadResSet][Query] Load fail, errCode=%d", errCode); + } + // We can just return, no need to reset the statement + return errCode; +} + +int SQLiteSingleVerStorageExecutor::GetNextEntryFromResultSet(Key &key, Value &value, bool isCopy) +{ + if (getResultRowIdStatement_ == nullptr || getResultEntryStatement_ == nullptr) { + return -E_RESULT_SET_STATUS_INVALID; + } + + int errCode = SQLiteUtils::StepWithRetry(getResultRowIdStatement_, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + if (!isCopy) { + return E_OK; + } + int64_t rowId = sqlite3_column_int64(getResultRowIdStatement_, 0); + errCode = E_OK; + SQLiteUtils::ResetStatement(getResultEntryStatement_, false, errCode); + if (errCode != E_OK) { + LOGE("[SqlSinExe][GetNext] Reset result set entry statement fail, errCode=%d.", errCode); + return CheckCorruptedStatus(errCode); + } + + SQLiteUtils::BindInt64ToStatement(getResultEntryStatement_, 1, rowId); + errCode = SQLiteUtils::StepWithRetry(getResultEntryStatement_, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = SQLiteUtils::GetColumnBlobValue(getResultEntryStatement_, 0, key); + if (errCode != E_OK) { + LOGE("[SqlSinExe][GetNext] Get key failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + errCode = SQLiteUtils::GetColumnBlobValue(getResultEntryStatement_, 1, value); + if (errCode != E_OK) { + LOGE("[SqlSinExe][GetNext] Get value failed:%d", errCode); + return CheckCorruptedStatus(errCode); + } + return E_OK; + } else { + return -E_UNEXPECTED_DATA; + } + } + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + return -E_FINISHED; + } + + LOGE("SQLite step failed:%d", errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::GetEntryByRowId(int64_t rowId, Entry &entry) +{ + if (getResultEntryStatement_ == nullptr) { + return -E_RESULT_SET_STATUS_INVALID; + } + int errCode = E_OK; + SQLiteUtils::ResetStatement(getResultEntryStatement_, false, errCode); + if (errCode != E_OK) { + LOGE("[SqlSinExe][GetEntryByRowid] Reset result set entry statement fail, errCode=%d.", errCode); + return CheckCorruptedStatus(errCode); + } + SQLiteUtils::BindInt64ToStatement(getResultEntryStatement_, 1, rowId); + errCode = SQLiteUtils::StepWithRetry(getResultEntryStatement_, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = SQLiteUtils::GetColumnBlobValue(getResultEntryStatement_, 0, entry.key); + if (errCode != E_OK) { + LOGE("[SqlSinExe][GetEntryByRowid] Get key failed, errCode=%d.", errCode); + return CheckCorruptedStatus(errCode); + } + errCode = SQLiteUtils::GetColumnBlobValue(getResultEntryStatement_, 1, entry.value); + if (errCode != E_OK) { + LOGE("[SqlSinExe][GetEntryByRowid] Get value failed, errCode=%d.", errCode); + return CheckCorruptedStatus(errCode); + } + return E_OK; + } else { + LOGE("[SqlSinExe][GetEntryByRowid] Step failed, errCode=%d.", errCode); + return -E_UNEXPECTED_DATA; + } +} + +void SQLiteSingleVerStorageExecutor::CloseResultSet() +{ + int errCode = E_OK; + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + SetCorruptedStatus(); + } + SQLiteUtils::ResetStatement(getResultEntryStatement_, true, errCode); + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + SetCorruptedStatus(); + } + if (isTransactionOpen_) { + SQLiteUtils::RollbackTransaction(dbHandle_); + isTransactionOpen_ = false; + } +} + +int SQLiteSingleVerStorageExecutor::StartTransaction(TransactType type) +{ + if (dbHandle_ == nullptr) { + LOGE("Begin transaction failed, dbHandle is null."); + return -E_INVALID_DB; + } + int errCode = SQLiteUtils::BeginTransaction(dbHandle_, type); + if (errCode == E_OK) { + isTransactionOpen_ = true; + } else { + LOGE("Begin transaction failed, errCode = %d", errCode); + } + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::Commit() +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = SQLiteUtils::CommitTransaction(dbHandle_); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + isTransactionOpen_ = false; + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::Rollback() +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = SQLiteUtils::RollbackTransaction(dbHandle_); + if (errCode != E_OK) { + LOGE("sqlite single ver storage executor rollback fail! errCode = [%d]", errCode); + return CheckCorruptedStatus(errCode); + } + isTransactionOpen_ = false; + return E_OK; +} + +bool SQLiteSingleVerStorageExecutor::CheckIfKeyExisted(const Key &key, bool isLocal, + Value &value, Timestamp ×tamp) const +{ + // not local value, no need to get the value. + if (!isLocal) { + return false; + } + + int errCode = GetKvData(SingleVerDataType::LOCAL_TYPE, key, value, timestamp); + if (errCode != E_OK) { + return false; + } + return true; +} + +int SQLiteSingleVerStorageExecutor::GetDeviceIdentifier(PragmaEntryDeviceIdentifier *identifier) +{ + if (identifier == nullptr) { + return -E_INVALID_ARGS; + } + + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_ENTRY_DEVICE, statement); + if (errCode != E_OK) { + return errCode; + } + + int keyIndex = identifier->origDevice ? BIND_ORI_DEVICE_ID : BIND_PRE_DEVICE_ID; + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_KV_KEY_INDEX, identifier->key, false); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + std::vector deviceId; + errCode = SQLiteUtils::GetColumnBlobValue(statement, keyIndex, deviceId); + identifier->deviceIdentifier.assign(deviceId.begin(), deviceId.end()); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = -E_NOT_FOUND; + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +void SQLiteSingleVerStorageExecutor::PutIntoCommittedData(const DataItem &itemPut, const DataItem &itemGet, + const DataOperStatus &status, const Key &hashKey, SingleVerNaturalStoreCommitNotifyData *committedData) +{ + if (committedData == nullptr) { + return; + } + + Entry entry; + int errCode; + if (!status.isDeleted) { + entry.key = itemPut.key; + entry.value = itemPut.value; + DataType dataType = (status.preStatus == DataStatus::EXISTED) ? DataType::UPDATE : DataType::INSERT; + errCode = committedData->InsertCommittedData(std::move(entry), dataType, true); + } else { + entry.key = itemGet.key; + entry.value = itemGet.value; + errCode = committedData->InsertCommittedData(std::move(entry), DataType::DELETE, true); + } + + if (errCode != E_OK) { + LOGE("[SingleVerExe][PutCommitData]Insert failed:%d", errCode); + } +} + +int SQLiteSingleVerStorageExecutor::PrepareForSavingData(const std::string &readSql, const std::string &insertSql, + const std::string &updateSql, SaveRecordStatements &statements) const +{ + int errCode = SQLiteUtils::GetStatement(dbHandle_, readSql, statements.queryStatement); + if (errCode != E_OK) { + LOGE("Get query statement failed. errCode = [%d]", errCode); + goto ERR; + } + + errCode = SQLiteUtils::GetStatement(dbHandle_, insertSql, statements.insertStatement); + if (errCode != E_OK) { + LOGE("Get insert statement failed. errCode = [%d]", errCode); + goto ERR; + } + + errCode = SQLiteUtils::GetStatement(dbHandle_, updateSql, statements.updateStatement); + if (errCode != E_OK) { + LOGE("Get update statement failed. errCode = [%d]", errCode); + goto ERR; + } + return E_OK; +ERR: + (void)statements.ResetStatement(); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::PrepareForSavingData(SingleVerDataType type) +{ + int errCode = -E_NOT_SUPPORT; + if (type == SingleVerDataType::LOCAL_TYPE) { + // currently, Local type has not been optimized, so pass updateSql parameter with INSERT_LOCAL_SQL + errCode = PrepareForSavingData(SELECT_LOCAL_HASH_SQL, INSERT_LOCAL_SQL, INSERT_LOCAL_SQL, saveLocalStatements_); + } else if (type == SingleVerDataType::SYNC_TYPE) { + errCode = PrepareForSavingData(SELECT_SYNC_HASH_SQL, INSERT_SYNC_SQL, UPDATE_SYNC_SQL, saveSyncStatements_); + } + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::ResetForSavingData(SingleVerDataType type) +{ + int errCode = E_OK; + if (type == SingleVerDataType::LOCAL_TYPE) { + SQLiteUtils::ResetStatement(saveLocalStatements_.insertStatement, false, errCode); + SQLiteUtils::ResetStatement(saveLocalStatements_.updateStatement, false, errCode); + SQLiteUtils::ResetStatement(saveLocalStatements_.queryStatement, false, errCode); + } else if (type == SingleVerDataType::SYNC_TYPE) { + SQLiteUtils::ResetStatement(saveSyncStatements_.insertStatement, false, errCode); + SQLiteUtils::ResetStatement(saveSyncStatements_.updateStatement, false, errCode); + SQLiteUtils::ResetStatement(saveSyncStatements_.queryStatement, false, errCode); + } + return CheckCorruptedStatus(errCode); +} + +std::string SQLiteSingleVerStorageExecutor::GetOriginDevName(const DataItem &dataItem, + const std::string &origDevGet) +{ + if (((dataItem.flag & DataItem::LOCAL_FLAG) != 0) && dataItem.origDev.empty()) { + return origDevGet; + } + return dataItem.origDev; +} + +int SQLiteSingleVerStorageExecutor::SaveSyncDataToDatabase(const DataItem &dataItem, const Key &hashKey, + const std::string &origDev, const std::string &deviceName, bool isUpdate) +{ + if ((dataItem.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) == DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) { + LOGD("Find query data missing, erase local data."); + return EraseSyncData(hashKey); + } + auto statement = saveSyncStatements_.GetDataSaveStatement(isUpdate); + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + std::string devName = DBCommon::TransferHashString(deviceName); + int errCode = BindSavedSyncData(statement, dataItem, hashKey, {origDev, devName}, isUpdate); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + return errCode; +} + +DataOperStatus SQLiteSingleVerStorageExecutor::JudgeSyncSaveType(DataItem &dataItem, + const DataItem &itemGet, const std::string &devName, bool isHashKeyExisted, bool isPermitForceWrite) +{ + DataOperStatus status; + status.isDeleted = ((dataItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG || + (dataItem.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) == DataItem::REMOTE_DEVICE_DATA_MISS_QUERY); + if (isHashKeyExisted) { + if ((itemGet.flag & DataItem::DELETE_FLAG) != 0) { + status.preStatus = DataStatus::DELETED; + } else { + status.preStatus = DataStatus::EXISTED; + } + std::string deviceName = DBCommon::TransferHashString(devName); + if (itemGet.writeTimestamp >= dataItem.writeTimestamp) { + // for multi user mode, no permit to forcewrite + if ((!deviceName.empty()) && (itemGet.dev == deviceName) && isPermitForceWrite) { + LOGI("Force overwrite the data:%" PRIu64 " vs %" PRIu64, + itemGet.writeTimestamp, dataItem.writeTimestamp); + status.isDefeated = false; + dataItem.writeTimestamp = itemGet.writeTimestamp + 1; + dataItem.timestamp = itemGet.timestamp; + } else { + status.isDefeated = true; + } + } + } + return status; +} + +int SQLiteSingleVerStorageExecutor::GetSyncDataItemExt(const DataItem &dataItem, DataItem &itemGet, + const DataOperStatus &dataStatus) const +{ + if (dataStatus.preStatus != DataStatus::EXISTED) { + return E_OK; + } + auto statement = isSyncMigrating_ ? migrateSyncStatements_.queryStatement : saveSyncStatements_.queryStatement; + // only deleted item need origin value. + int errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_KEY_INDEX, itemGet.key); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_VAL_INDEX, itemGet.value); + if (errCode != E_OK) { + LOGE("Get column value data failed:%d", errCode); + } + + return errCode; +} + +int SQLiteSingleVerStorageExecutor::ResetSaveSyncStatements(int errCode) +{ + SQLiteUtils::ResetStatement(saveSyncStatements_.insertStatement, false, errCode); + SQLiteUtils::ResetStatement(saveSyncStatements_.updateStatement, false, errCode); + SQLiteUtils::ResetStatement(saveSyncStatements_.queryStatement, false, errCode); + return CheckCorruptedStatus(errCode); +} + +namespace { + inline bool IsNeedIgnoredData(const DataItem &itemPut, const DataItem &itemGet, + const DeviceInfo &devInfo, bool isHashKeyExisted, int policy) + { + // deny the data synced from other dev which the origin dev is current or the existed value is current dev data. + return (((itemGet.origDev.empty() && isHashKeyExisted) || itemPut.origDev.empty()) && + (!devInfo.isLocal && policy == DENY_OTHER_DEV_AMEND_CUR_DEV_DATA)); + } +} + +int SQLiteSingleVerStorageExecutor::PrepareForNotifyConflictAndObserver(DataItem &dataItem, + const DeviceInfo &deviceInfo, NotifyConflictAndObserverData ¬ify, bool isPermitForceWrite) +{ + // Check sava data existed info + int errCode = GetSyncDataItemPre(dataItem, notify.getData, notify.hashKey); + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + LOGD("[SingleVerExe][PrepareForNotifyConflictAndObserver] failed:%d", errCode); + if (isSyncMigrating_) { + ResetForMigrateCacheData(); + return errCode; + } + return ResetSaveSyncStatements(errCode); + } + + bool isHashKeyExisted = (errCode != -E_NOT_FOUND); + if (IsNeedIgnoredData(dataItem, notify.getData, deviceInfo, isHashKeyExisted, conflictResolvePolicy_)) { + LOGD("[SingleVerExe] Ignore the sync data."); + if (isSyncMigrating_) { + ResetForMigrateCacheData(); + return -E_IGNORE_DATA; + } + return ResetSaveSyncStatements(-E_IGNORE_DATA); + } + + notify.dataStatus = JudgeSyncSaveType(dataItem, notify.getData, deviceInfo.deviceName, isHashKeyExisted, + isPermitForceWrite); + InitCommitNotifyDataKeyStatus(notify.committedData, notify.hashKey, notify.dataStatus); + + // Nonexistent data, but deleted by local. + if ((notify.dataStatus.preStatus == DataStatus::DELETED || notify.dataStatus.preStatus == DataStatus::NOEXISTED) && + (dataItem.flag & DataItem::DELETE_FLAG) != 0 && + (dataItem.flag & DataItem::LOCAL_FLAG) != 0) { + // For delete item in cacheDB, which not in mainDB. Cannot notify, but this is not error. + errCode = -E_NOT_FOUND; + LOGD("Nonexistent data, but deleted by local"); + if (isSyncMigrating_) { + ResetForMigrateCacheData(); + return errCode; + } + return ResetSaveSyncStatements(errCode); + } + + // get key and value from ori database + errCode = GetSyncDataItemExt(dataItem, notify.getData, notify.dataStatus); + if (errCode != E_OK) { + LOGD("GetSyncDataItemExt failed:%d", errCode); + if (isSyncMigrating_) { + ResetForMigrateCacheData(); + return errCode; + } + return ResetSaveSyncStatements(errCode); + } + + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::SaveSyncDataItem(DataItem &dataItem, const DeviceInfo &deviceInfo, + Timestamp &maxStamp, SingleVerNaturalStoreCommitNotifyData *committedData, bool isPermitForceWrite) +{ + NotifyConflictAndObserverData notify = { + .committedData = committedData + }; + + int errCode = PrepareForNotifyConflictAndObserver(dataItem, deviceInfo, notify, isPermitForceWrite); + if (errCode != E_OK) { + if (errCode == -E_IGNORE_DATA) { + errCode = E_OK; + } + return errCode; + } + + PutConflictData(dataItem, notify.getData, deviceInfo, notify.dataStatus, committedData); + if (notify.dataStatus.isDefeated) { + LOGD("Data status is defeated:%d", errCode); + return ResetSaveSyncStatements(errCode); + } + + bool isUpdate = (notify.dataStatus.preStatus != DataStatus::NOEXISTED); + std::string origDev = GetOriginDevName(dataItem, notify.getData.origDev); + errCode = SaveSyncDataToDatabase(dataItem, notify.hashKey, origDev, deviceInfo.deviceName, isUpdate); + if (errCode == E_OK) { + PutIntoCommittedData(dataItem, notify.getData, notify.dataStatus, notify.hashKey, committedData); + maxStamp = std::max(dataItem.timestamp, maxStamp); + } else { + LOGE("Save sync data to db failed:%d", errCode); + } + return ResetSaveSyncStatements(errCode); +} + +int SQLiteSingleVerStorageExecutor::GetAllMetaKeys(std::vector &keys) const +{ + sqlite3_stmt *statement = nullptr; + const std::string &sqlStr = (attachMetaMode_ ? SELECT_ATTACH_ALL_META_KEYS : SELECT_ALL_META_KEYS); + int errCode = SQLiteUtils::GetStatement(dbHandle_, sqlStr, statement); + if (errCode != E_OK) { + LOGE("[SingleVerExe][GetAllKey] Get statement failed:%d", errCode); + return errCode; + } + + errCode = GetAllKeys(statement, keys); + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::GetAllSyncedEntries(const std::string &deviceName, + std::vector &entries) const +{ + sqlite3_stmt *statement = nullptr; + std::string sql = (executorState_ == ExecutorState::CACHE_ATTACH_MAIN ? + SELECT_ALL_SYNC_ENTRIES_BY_DEV_FROM_CACHEHANDLE : SELECT_ALL_SYNC_ENTRIES_BY_DEV); + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + LOGE("Get all entries statement failed:%d", errCode); + return errCode; + } + + // When removing device data in cache mode, key is "remove", value is deviceID's hash string. + // Therefore, no need to transfer hash string when migrating. + std::string devName = isSyncMigrating_ ? deviceName : DBCommon::TransferHashString(deviceName); + std::vector devVect(devName.begin(), devName.end()); + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, devVect, true); // bind the 1st to device. + if (errCode != E_OK) { + LOGE("Failed to bind the synced device for all entries:%d", errCode); + } else { + errCode = GetAllEntries(statement, entries); + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::GetAllEntries(sqlite3_stmt *statement, std::vector &entries) const +{ + if (statement == nullptr) { + return -E_INVALID_DB; + } + int errCode; + do { + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + Entry entry; + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, entry.key); // No.0 is the key + if (errCode != E_OK) { + break; + } + errCode = SQLiteUtils::GetColumnBlobValue(statement, 1, entry.value); // No.1 is the value + if (errCode != E_OK) { + break; + } + + entries.push_back(std::move(entry)); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else { + LOGE("SQLite step for all entries failed:%d", errCode); + break; + } + } while (true); + + return errCode; +} + +int SQLiteSingleVerStorageExecutor::GetAllKeys(sqlite3_stmt *statement, std::vector &keys) const +{ + if (statement == nullptr) { + return -E_INVALID_DB; + } + int errCode; + do { + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + Key key; + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, key); + if (errCode != E_OK) { + break; + } + + keys.push_back(std::move(key)); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else { + LOGE("SQLite step for getting all keys failed:%d", errCode); + break; + } + } while (true); + + return errCode; +} + +int SQLiteSingleVerStorageExecutor::BindSavedSyncData(sqlite3_stmt *statement, const DataItem &dataItem, + const Key &hashKey, const SyncDataDevices &devices, bool isUpdate) +{ + const int hashKeyIndex = isUpdate ? BIND_SYNC_UPDATE_HASH_KEY_INDEX : BIND_SYNC_HASH_KEY_INDEX; + int errCode = SQLiteUtils::BindBlobToStatement(statement, hashKeyIndex, hashKey, false); + if (errCode != E_OK) { + LOGE("Bind saved sync data hash key failed:%d", errCode); + return errCode; + } + + // if delete flag is set, just use the hash key instead of the key + if ((dataItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG) { + errCode = SQLiteUtils::MapSQLiteErrno(sqlite3_bind_zeroblob(statement, BIND_SYNC_KEY_INDEX, -1)); + } else { + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_SYNC_KEY_INDEX, dataItem.key, false); + } + + if (errCode != E_OK) { + LOGE("Bind saved sync data key failed:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_SYNC_VAL_INDEX, dataItem.value, true); + if (errCode != E_OK) { + LOGE("Bind saved sync data value failed:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_SYNC_STAMP_INDEX, dataItem.timestamp); + if (errCode != E_OK) { + LOGE("Bind saved sync data stamp failed:%d", errCode); + return errCode; + } + + const int writeTimeIndex = isUpdate ? BIND_SYNC_UPDATE_W_TIME_INDEX : BIND_SYNC_W_TIME_INDEX; + errCode = SQLiteUtils::BindInt64ToStatement(statement, writeTimeIndex, dataItem.writeTimestamp); + LOGD("Write timestamp:%" PRIu64 " timestamp:%" PRIu64 ", %" PRIu64, + dataItem.writeTimestamp, dataItem.timestamp, dataItem.flag); + if (errCode != E_OK) { + LOGE("Bind saved sync data write stamp failed:%d", errCode); + return errCode; + } + + return BindDevForSavedSyncData(statement, dataItem, devices.origDev, devices.dev); +} + +void SQLiteSingleVerStorageExecutor::PutConflictData(const DataItem &itemPut, const DataItem &itemGet, + const DeviceInfo &deviceInfo, const DataOperStatus &dataStatus, + SingleVerNaturalStoreCommitNotifyData *commitData) +{ + if (commitData == nullptr) { + return; + } + + bool conflictNotifyMatch = commitData->IsConflictedNotifyMatched(itemPut, itemGet); + if (!conflictNotifyMatch) { + return; + } + + if (dataStatus.preStatus == DataStatus::NOEXISTED || + ((dataStatus.preStatus == DataStatus::DELETED) && dataStatus.isDeleted)) { + return; + } + + Key origKey; + if ((itemPut.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG || + (itemPut.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) == DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) { + origKey = itemGet.key; + } else { + origKey = itemPut.key; + } + + // insert db original entry + std::vector getDevVect(itemGet.dev.begin(), itemGet.dev.end()); + DataItemInfo orgItemInfo = {itemGet, true, getDevVect}; + orgItemInfo.dataItem.key = origKey; + commitData->InsertConflictedItem(orgItemInfo, true); + + // insert conflict entry + std::string putDeviceName = DBCommon::TransferHashString(deviceInfo.deviceName); + std::vector putDevVect(putDeviceName.begin(), putDeviceName.end()); + + DataItemInfo newItemInfo = {itemPut, deviceInfo.isLocal, putDevVect}; + newItemInfo.dataItem.key = origKey; + commitData->InsertConflictedItem(newItemInfo, false); +} + +int SQLiteSingleVerStorageExecutor::Reset() +{ + if (isTransactionOpen_) { + Rollback(); + } + + int errCode = ResetForSavingData(SingleVerDataType::SYNC_TYPE); + if (errCode != E_OK) { + LOGE("Finalize the sync resources for saving sync data failed: %d", errCode); + } + + errCode = ResetForSavingData(SingleVerDataType::LOCAL_TYPE); + if (errCode != E_OK) { + LOGE("Finalize the local resources for saving sync data failed: %d", errCode); + } + return SQLiteStorageExecutor::Reset(); +} + +int SQLiteSingleVerStorageExecutor::GetSyncDataItemPre(const DataItem &itemPut, DataItem &itemGet, + Key &hashKey) const +{ + if (isSyncMigrating_) { + hashKey = itemPut.hashKey; + } else if ((itemPut.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG || + ((itemPut.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) == DataItem::REMOTE_DEVICE_DATA_MISS_QUERY)) { + hashKey = itemPut.key; + } else { + int errCode = DBCommon::CalcValueHash(itemPut.key, hashKey); + if (errCode != E_OK) { + return errCode; + } + } + + return GetSyncDataPreByHashKey(hashKey, itemGet); +} + +int SQLiteSingleVerStorageExecutor::GetSyncDataPreByHashKey(const Key &hashKey, DataItem &itemGet) const +{ + auto statement = isSyncMigrating_ ? migrateSyncStatements_.queryStatement : saveSyncStatements_.queryStatement; + int errCode = SQLiteUtils::BindBlobToStatement(statement, 1, hashKey, false); // 1st arg. + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { // no find the key + errCode = -E_NOT_FOUND; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + itemGet.timestamp = static_cast(sqlite3_column_int64(statement, SYNC_RES_TIME_INDEX)); + itemGet.writeTimestamp = static_cast(sqlite3_column_int64(statement, SYNC_RES_W_TIME_INDEX)); + itemGet.flag = static_cast(sqlite3_column_int64(statement, SYNC_RES_FLAG_INDEX)); + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_KEY_INDEX, itemGet.key); + if (errCode != E_OK) { + return errCode; + } + std::vector devVect; + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_DEVICE_INDEX, devVect); + if (errCode != E_OK) { + return errCode; + } + + std::vector origDevVect; + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_ORI_DEV_INDEX, origDevVect); + if (errCode != E_OK) { + return errCode; + } + itemGet.dev.assign(devVect.begin(), devVect.end()); + itemGet.origDev.assign(origDevVect.begin(), origDevVect.end()); + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::DeleteLocalDataInner(SingleVerNaturalStoreCommitNotifyData *committedData, + const Key &key, const Value &value) +{ + if (committedData != nullptr) { + Key hashKey; + int innerErrCode = DBCommon::CalcValueHash(key, hashKey); + if (innerErrCode != E_OK) { + return innerErrCode; + } + committedData->InitKeyPropRecord(hashKey, ExistStatus::EXIST); + } + + std::string sql = DELETE_LOCAL_SQL; + if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + sql = DELETE_LOCAL_SQL_FROM_CACHEHANDLE; + } + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); + if (errCode != E_OK) { + LOGE("Bind the key error(%d) when delete kv data.", errCode); + goto ERROR; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + if (sqlite3_changes(dbHandle_) > 0) { + if (committedData != nullptr) { + Entry entry = {key, value}; + committedData->InsertCommittedData(std::move(entry), DataType::DELETE, true); + } else { + LOGE("DeleteLocalKvData failed to do commit notify because of OOM."); + } + errCode = E_OK; + } + } + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::DeleteLocalKvData(const Key &key, + SingleVerNaturalStoreCommitNotifyData *committedData, Value &value, Timestamp ×tamp) +{ + int errCode = GetKvData(SingleVerDataType::LOCAL_TYPE, key, value, timestamp); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + return DeleteLocalDataInner(committedData, key, value); +} + +int SQLiteSingleVerStorageExecutor::EraseSyncData(const Key &hashKey) +{ + sqlite3_stmt *stmt = nullptr; + std::string sql = (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) ? + DELETE_SYNC_DATA_WITH_HASHKEY_FROM_CACHEHANDLE : DELETE_SYNC_DATA_WITH_HASHKEY; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, stmt); + if (errCode != E_OK) { + LOGE("get erase statement failed:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(stmt, 1, hashKey, false); + if (errCode != E_OK) { + LOGE("bind hashKey failed:%d", errCode); + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(stmt, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } else { + LOGE("erase data failed:%d", errCode); + } +END: + SQLiteUtils::ResetStatement(stmt, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::RemoveDeviceData(const std::string &deviceName) +{ + // Transfer the device name. + std::string devName = DBCommon::TransferHashString(deviceName); + sqlite3_stmt *statement = nullptr; + std::vector devVect(devName.begin(), devName.end()); + + int errCode = SQLiteUtils::GetStatement(dbHandle_, REMOVE_DEV_DATA_SQL, statement); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, devVect, true); // only one arg. + if (errCode != E_OK) { + LOGE("Failed to bind the removed device:%d", errCode); + goto ERROR; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGE("Failed to execute rm the device synced data:%d", errCode); + } else { + errCode = E_OK; + } + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::StepForResultEntries(sqlite3_stmt *statement, std::vector &entries) const +{ + entries.clear(); + entries.shrink_to_fit(); + Entry entry; + int errCode = E_OK; + do { + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, entry.key); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::GetColumnBlobValue(statement, 1, entry.value); + if (errCode != E_OK) { + return errCode; + } + + entries.push_back(std::move(entry)); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else { + LOGE("SQLite step failed:%d", errCode); + return errCode; + } + } while (true); + + // if select no result, return the -E_NOT_FOUND. + if (entries.empty()) { + errCode = -E_NOT_FOUND; + } + + return errCode; +} + +int SQLiteSingleVerStorageExecutor::BindDevForSavedSyncData(sqlite3_stmt *statement, const DataItem &dataItem, + const std::string &origDev, const std::string &deviceName) +{ + int errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_SYNC_FLAG_INDEX, + static_cast(dataItem.flag)); + if (errCode != E_OK) { + LOGE("Bind saved sync data flag failed:%d", errCode); + return errCode; + } + + std::vector devVect(deviceName.begin(), deviceName.end()); + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_SYNC_DEV_INDEX, devVect, true); + if (errCode != E_OK) { + LOGE("Bind dev for sync data failed:%d", errCode); + return errCode; + } + + std::vector origDevVect(origDev.begin(), origDev.end()); + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_SYNC_ORI_DEV_INDEX, origDevVect, true); + if (errCode != E_OK) { + LOGE("Bind orig dev for sync data failed:%d", errCode); + } + + return errCode; +} + +size_t SQLiteSingleVerStorageExecutor::GetDataItemSerialSize(const DataItem &item, size_t appendLen) +{ + // timestamp and local flag: 3 * uint64_t, version(uint32_t), key, value, origin dev and the padding size. + // the size would not be very large. + static const size_t maxOrigDevLength = 40; + size_t devLength = std::max(maxOrigDevLength, item.origDev.size()); + size_t dataSize = (Parcel::GetUInt64Len() * 3 + Parcel::GetUInt32Len() + Parcel::GetVectorCharLen(item.key) + + Parcel::GetVectorCharLen(item.value) + devLength + appendLen); + + return dataSize; +} + +int SQLiteSingleVerStorageExecutor::InitResultSet(const Key &keyPrefix, sqlite3_stmt *&countStmt) +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + // bind statement for count + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_COUNT_SYNC_PREFIX_SQL, countStmt); + if (errCode != E_OK) { + LOGE("Get count statement for resultset error:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindPrefixKey(countStmt, 1, keyPrefix); // first argument is key + if (errCode != E_OK) { + LOGE("Bind count key error:%d", errCode); + goto ERROR; + } + // bind statement for result set + errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_SYNC_ROWID_PREFIX_SQL, getResultRowIdStatement_); + if (errCode != E_OK) { + LOGE("Get result set rowid statement error:%d", errCode); + goto ERROR; + } + + errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_SYNC_DATA_BY_ROWID_SQL, getResultEntryStatement_); + if (errCode != E_OK) { + LOGE("Get result set entry statement error:%d", errCode); + goto ERROR; + } + + errCode = SQLiteUtils::BindPrefixKey(getResultRowIdStatement_, 1, keyPrefix); // first argument is key + if (errCode != E_OK) { + LOGE("Bind result set rowid statement error:%d", errCode); + goto ERROR; + } + return E_OK; + +ERROR: + SQLiteUtils::ResetStatement(countStmt, true, errCode); + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + SQLiteUtils::ResetStatement(getResultEntryStatement_, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::InitResultSetCount(QueryObject &queryObj, sqlite3_stmt *&countStmt) +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + + errCode = helper.GetCountSqlStatement(dbHandle_, countStmt); + if (errCode != E_OK) { + LOGE("Get count bind statement error:%d", errCode); + SQLiteUtils::ResetStatement(countStmt, true, errCode); + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::InitResultSetContent(QueryObject &queryObj) +{ + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + + // bind statement for result set + errCode = helper.GetQuerySqlStatement(dbHandle_, true, getResultRowIdStatement_); + if (errCode != E_OK) { + LOGE("[SqlSinExe][InitResSetContent] Bind result set rowid statement of query error:%d", errCode); + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + return errCode; + } + errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_SYNC_DATA_BY_ROWID_SQL, getResultEntryStatement_); + if (errCode != E_OK) { + LOGE("[SqlSinExe][InitResSetContent] Get result set entry statement of query error:%d", errCode); + return CheckCorruptedStatus(errCode); + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::InitResultSet(QueryObject &queryObj, sqlite3_stmt *&countStmt) +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); + if (errCode != E_OK) { + return errCode; + } + + if (!queryObj.IsValid()) { + return -E_INVALID_QUERY_FORMAT; + } + + errCode = InitResultSetCount(queryObj, countStmt); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + errCode = InitResultSetContent(queryObj); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(countStmt, true, errCode); + } + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::UpdateLocalDataTimestamp(Timestamp timestamp) +{ + const std::string updateSql = "UPDATE local_data SET timestamp="; + std::string sql = updateSql + std::to_string(timestamp) + " WHERE timestamp=0;"; + int errCode = SQLiteUtils::ExecuteRawSQL(dbHandle_, sql); + return CheckCorruptedStatus(errCode); +} + +void SQLiteSingleVerStorageExecutor::SetAttachMetaMode(bool attachMetaMode) +{ + attachMetaMode_ = attachMetaMode; +} + +int SQLiteSingleVerStorageExecutor::GetOneRawDataItem(sqlite3_stmt *statement, DataItem &dataItem, + uint64_t &verInCurCacheDb, bool isCacheDb) const +{ + int errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_KEY_INDEX, dataItem.key); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_VAL_INDEX, dataItem.value); + if (errCode != E_OK) { + return errCode; + } + + dataItem.timestamp = static_cast(sqlite3_column_int64(statement, SYNC_RES_TIME_INDEX)); + dataItem.flag = static_cast(sqlite3_column_int64(statement, SYNC_RES_FLAG_INDEX)); + + std::vector devVect; + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_DEVICE_INDEX, devVect); + if (errCode != E_OK) { + return errCode; + } + dataItem.dev = std::string(devVect.begin(), devVect.end()); + + devVect.clear(); + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_ORI_DEV_INDEX, devVect); + if (errCode != E_OK) { + return errCode; + } + dataItem.origDev = std::string(devVect.begin(), devVect.end()); + + errCode = SQLiteUtils::GetColumnBlobValue(statement, SYNC_RES_HASH_KEY_INDEX, dataItem.hashKey); + if (errCode != E_OK) { + return errCode; + } + dataItem.writeTimestamp = static_cast(sqlite3_column_int64(statement, SYNC_RES_W_TIME_INDEX)); + if (errCode != E_OK) { + return errCode; + } + if (isCacheDb) { + verInCurCacheDb = static_cast(sqlite3_column_int64(statement, SYNC_RES_VERSION_INDEX)); + } + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::GetAllDataItems(sqlite3_stmt *statement, std::vector &dataItems, + uint64_t &verInCurCacheDb, bool isCacheDb) const +{ + dataItems.clear(); + dataItems.shrink_to_fit(); + DataItem dataItem; + int errCode; + do { + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = GetOneRawDataItem(statement, dataItem, verInCurCacheDb, isCacheDb); + if (errCode != E_OK) { + return errCode; + } + dataItems.push_back(std::move(dataItem)); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else { + LOGE("SQLite step failed:%d", errCode); + break; + } + } while (true); + + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::OpenResultSetForCacheRowIdModeCommon(std::vector &rowIdCache, + uint32_t cacheLimit, int &count) +{ + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_SYNC_DATA_BY_ROWID_SQL, getResultEntryStatement_); + if (errCode != E_OK) { + LOGE("[SqlSinExe][OpenResSetRowId][Common] Get entry stmt fail, errCode=%d", errCode); + return CheckCorruptedStatus(errCode); + } + errCode = StartTransaction(TransactType::DEFERRED); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(getResultEntryStatement_, true, errCode); + return CheckCorruptedStatus(errCode); + } + // Now Ready To Execute + errCode = ResultSetLoadRowIdCache(rowIdCache, cacheLimit, 0, count); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(getResultEntryStatement_, true, errCode); + Rollback(); + return CheckCorruptedStatus(errCode); + } + // Consider finalize getResultRowIdStatement_ here if count equal to size of rowIdCache. + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::ResultSetLoadRowIdCache(std::vector &rowIdCache, uint32_t cacheLimit, + uint32_t cacheStartPos, int &count) +{ + rowIdCache.clear(); + count = 0; + while (true) { + int errCode = SQLiteUtils::StepWithRetry(getResultRowIdStatement_, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + if (count >= static_cast(cacheStartPos) && rowIdCache.size() < cacheLimit) { + // If we can start cache, and, if we can still cache + int64_t rowid = sqlite3_column_int64(getResultRowIdStatement_, 0); + rowIdCache.push_back(rowid); + } + // Always increase the count + count++; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + break; + } else { + LOGE("[SqlSinExe][ResSetLoadCache] Step fail, errCode=%d", errCode); + rowIdCache.clear(); + count = 0; + return CheckCorruptedStatus(errCode); + } + } + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::SaveRecordStatements::ResetStatement() +{ + int errCode = E_OK; + SQLiteUtils::ResetStatement(insertStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize insert statements failed, error: %d", errCode); + } + + SQLiteUtils::ResetStatement(updateStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize update statements failed, error: %d", errCode); + } + + SQLiteUtils::ResetStatement(queryStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize query statement failed, error: %d", errCode); + } + return errCode; +} + +void SQLiteSingleVerStorageExecutor::FinalizeAllStatements() +{ + int errCode = saveLocalStatements_.ResetStatement(); + if (errCode != E_OK) { + LOGE("Finalize saveLocal statements failed, error: %d", errCode); + } + + errCode = saveSyncStatements_.ResetStatement(); + if (errCode != E_OK) { + LOGE("Finalize saveSync statement failed, error: %d", errCode); + } + + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize getResultRowIdStatement_ failed, error: %d", errCode); + } + + SQLiteUtils::ResetStatement(getResultEntryStatement_, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize getResultEntryStatement_ failed, error: %d", errCode); + } + + errCode = migrateSyncStatements_.ResetStatement(); + if (errCode != E_OK) { + LOGE("Finalize migrateSync statements failed, error: %d", errCode); + } + + ReleaseContinueStatement(); +} + +void SQLiteSingleVerStorageExecutor::SetConflictResolvePolicy(int policy) +{ + if (policy == DENY_OTHER_DEV_AMEND_CUR_DEV_DATA || policy == DEFAULT_LAST_WIN) { + conflictResolvePolicy_ = policy; + } +} + +int SQLiteSingleVerStorageExecutor::CheckIntegrity() const +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + return SQLiteUtils::CheckIntegrity(dbHandle_, CHECK_DB_INTEGRITY_SQL); +} + +int SQLiteSingleVerStorageExecutor::ForceCheckPoint() const +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + SQLiteUtils::ExecuteCheckPoint(dbHandle_); + return E_OK; +} + +uint64_t SQLiteSingleVerStorageExecutor::GetLogFileSize() const +{ + if (isMemDb_) { + return 0; + } + + const char *fileName = sqlite3_db_filename(dbHandle_, "main"); + if (fileName == nullptr) { + return 0; + } + std::string walName = std::string(fileName) + "-wal"; + uint64_t fileSize = 0; + int result = OS::CalFileSize(std::string(walName), fileSize); + if (result != E_OK) { + return 0; + } + return fileSize; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.h new file mode 100644 index 0000000000000000000000000000000000000000..ddc9154c151480a28e00514b3760347afb372db4 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.h @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_SINGLE_VER_STORAGE_EXECUTOR_H +#define SQLITE_SINGLE_VER_STORAGE_EXECUTOR_H + +#include "macro_utils.h" +#include "db_types.h" +#include "query_object.h" +#include "sqlite_utils.h" +#include "sqlite_storage_executor.h" +#include "single_ver_natural_store_commit_notify_data.h" + +namespace DistributedDB { +enum class SingleVerDataType { + META_TYPE, + LOCAL_TYPE, + SYNC_TYPE, +}; + +enum class DataStatus { + NOEXISTED, + DELETED, + EXISTED, +}; + +enum class ExecutorState { + INVALID = -1, + MAINDB, + CACHEDB, + MAIN_ATTACH_CACHE, // After process crash and cacheDb existed + CACHE_ATTACH_MAIN, // while cacheDb migrating to mainDb +}; + +struct DataOperStatus { + DataStatus preStatus = DataStatus::NOEXISTED; + bool isDeleted = false; + bool isDefeated = false; // whether the put data is defeated. +}; + +struct SingleVerRecord { + Key key; + Value value; + Timestamp timestamp = 0; + uint64_t flag = 0; + std::string device; + std::string origDevice; + Key hashKey; + Timestamp writeTimestamp = 0; +}; + +struct DeviceInfo { + bool isLocal = false; + std::string deviceName; +}; + +struct LocalDataItem { + Key key; + Value value; + Timestamp timestamp = 0; + Key hashKey; + uint64_t flag = 0; +}; + +struct NotifyConflictAndObserverData { + SingleVerNaturalStoreCommitNotifyData *committedData = nullptr; + DataItem getData; + Key hashKey; + DataOperStatus dataStatus; +}; + +struct NotifyMigrateSyncData { + bool isRemote = false; + bool isRemoveDeviceData = false; + bool isPermitForceWrite = true; + SingleVerNaturalStoreCommitNotifyData *committedData = nullptr; + std::vector entries{}; +}; + +struct SyncDataDevices { + std::string origDev; + std::string dev; +}; + +class SQLiteSingleVerStorageExecutor : public SQLiteStorageExecutor { +public: + SQLiteSingleVerStorageExecutor(sqlite3 *dbHandle, bool writable, bool isMemDb); + SQLiteSingleVerStorageExecutor(sqlite3 *dbHandle, bool writable, bool isMemDb, ExecutorState executorState); + ~SQLiteSingleVerStorageExecutor() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleVerStorageExecutor); + + // Get the Kv data according the type(sync, meta, local data). + int GetKvData(SingleVerDataType type, const Key &key, Value &value, Timestamp ×tamp) const; + + // Get the sync data record by hash key. + int GetKvDataByHashKey(const Key &hashKey, SingleVerRecord &result) const; + + // Put the Kv data according the type(meta and the local data). + int PutKvData(SingleVerDataType type, const Key &key, const Value &value, + Timestamp timestamp, SingleVerNaturalStoreCommitNotifyData *committedData); + + int GetEntries(SingleVerDataType type, const Key &keyPrefix, std::vector &entries) const; + + int GetEntries(QueryObject &queryObj, std::vector &entries) const; + + int GetCount(QueryObject &queryObj, int &count) const; + + // Get all the meta keys. + int GetAllMetaKeys(std::vector &keys) const; + + int GetAllSyncedEntries(const std::string &deviceName, std::vector &entries) const; + + int SaveSyncDataItem(DataItem &dataItem, const DeviceInfo &deviceInfo, + Timestamp &maxStamp, SingleVerNaturalStoreCommitNotifyData *committedData, bool isPermitForceWrite = true); + + int DeleteLocalKvData(const Key &key, SingleVerNaturalStoreCommitNotifyData *committedData, Value &value, + Timestamp ×tamp); + + // delete a row data by hashKey, with no tombstone left. + int EraseSyncData(const Key &hashKey); + + int RemoveDeviceData(const std::string &deviceName); + + int RemoveDeviceDataInCacheMode(const std::string &deviceName, bool isNeedNotify, uint64_t recordVersion) const; + + void InitCurrentMaxStamp(Timestamp &maxStamp); + + void ReleaseContinueStatement(); + + int GetSyncDataByTimestamp(std::vector &dataItems, size_t appendedLength, Timestamp begin, + Timestamp end, const DataSizeSpecInfo &dataSizeInfo) const; + int GetDeletedSyncDataByTimestamp(std::vector &dataItems, size_t appendedLength, Timestamp begin, + Timestamp end, const DataSizeSpecInfo &dataSizeInfo) const; + + int GetDeviceIdentifier(PragmaEntryDeviceIdentifier *identifier); + + int OpenResultSet(const Key &keyPrefix, int &count); + + int OpenResultSet(QueryObject &queryObj, int &count); + + int OpenResultSetForCacheRowIdMode(const Key &keyPrefix, std::vector &rowIdCache, + uint32_t cacheLimit, int &count); + + int OpenResultSetForCacheRowIdMode(QueryObject &queryObj, std::vector &rowIdCache, + uint32_t cacheLimit, int &count); + + int ReloadResultSet(const Key &keyPrefix); + + int ReloadResultSet(QueryObject &queryObj); + + int ReloadResultSetForCacheRowIdMode(const Key &keyPrefix, std::vector &rowIdCache, + uint32_t cacheLimit, uint32_t cacheStartPos); + + int ReloadResultSetForCacheRowIdMode(QueryObject &queryObj, std::vector &rowIdCache, + uint32_t cacheLimit, uint32_t cacheStartPos); + + int GetNextEntryFromResultSet(Key &key, Value &value, bool isCopy); + + int GetEntryByRowId(int64_t rowId, Entry &entry); + + void CloseResultSet(); + + int StartTransaction(TransactType type); + + int Commit(); + + int Rollback(); + + bool CheckIfKeyExisted(const Key &key, bool isLocal, Value &value, Timestamp ×tamp) const; + + int PrepareForSavingData(SingleVerDataType type); + + int ResetForSavingData(SingleVerDataType type); + + int Reset() override; + + int UpdateLocalDataTimestamp(Timestamp timestamp); + + void SetAttachMetaMode(bool attachMetaMode); + + int PutLocalDataToCacheDB(const LocalDataItem &dataItem) const; + + int SaveSyncDataItemInCacheMode(DataItem &dataItem, const DeviceInfo &deviceInfo, Timestamp &maxStamp, + uint64_t recordVersion, const QueryObject &query); + + int PrepareForSavingCacheData(SingleVerDataType type); + int ResetForSavingCacheData(SingleVerDataType type); + + int MigrateLocalData(); + + int MigrateSyncDataByVersion(uint64_t recordVer, NotifyMigrateSyncData &syncData, + std::vector &dataItems); + int GetMinVersionCacheData(std::vector &dataItems, uint64_t &maxVerIncurCacheDb) const; + + int GetMaxVersionIncacheDb(uint64_t &maxVersion) const; + int AttachMainDbAndCacheDb(CipherType type, const CipherPassword &passwd, + const std::string &attachDbAbsPath, EngineState engineState); + + // Clear migrating data. + void ClearMigrateData(); + + // Get current max timestamp. + int GetMaxTimestampDuringMigrating(Timestamp &maxTimestamp) const; + + void SetConflictResolvePolicy(int policy); + + // Delete multiple meta data records in a transaction. + int DeleteMetaData(const std::vector &keys); + // Delete multiple meta data records with key prefix in a transaction. + int DeleteMetaDataByPrefixKey(const Key &keyPrefix); + + int CheckIntegrity() const; + + int CheckQueryObjectLegal(QueryObject &queryObj) const; + + int CheckDataWithQuery(QueryObject query, std::vector &dataItems, const DeviceInfo &deviceInfo); + + static size_t GetDataItemSerialSize(const DataItem &item, size_t appendLen); + + int AddSubscribeTrigger(QueryObject &query, const std::string &subscribeId); + + int RemoveSubscribeTrigger(const std::vector &subscribeIds); + + int RemoveSubscribeTriggerWaterMark(const std::vector &subscribeIds); + + int GetTriggers(const std::string &namePreFix, std::vector &triggerNames); + + int RemoveTrigger(const std::vector &triggers); + + int GetSyncDataWithQuery(const QueryObject &query, size_t appendLength, const DataSizeSpecInfo &dataSizeInfo, + const std::pair &timeRange, std::vector &dataItems) const; + + int ForceCheckPoint() const; + + uint64_t GetLogFileSize() const; + +private: + struct SaveRecordStatements { + sqlite3_stmt *queryStatement = nullptr; + sqlite3_stmt *insertStatement = nullptr; + sqlite3_stmt *updateStatement = nullptr; + + int ResetStatement(); + + inline sqlite3_stmt *GetDataSaveStatement(bool isUpdate) const + { + return isUpdate ? updateStatement : insertStatement; + } + }; + + void PutIntoCommittedData(const DataItem &itemPut, const DataItem &itemGet, const DataOperStatus &status, + const Key &hashKey, SingleVerNaturalStoreCommitNotifyData *committedData); + + static int BindSavedSyncData(sqlite3_stmt *statement, const DataItem &dataItem, const Key &hashKey, + const SyncDataDevices &devices, bool isUpdate); + + static int BindDevForSavedSyncData(sqlite3_stmt *statement, const DataItem &dataItem, const std::string &origDev, + const std::string &deviceName); + + static void PutConflictData(const DataItem &itemPut, const DataItem &itemGet, const DeviceInfo &deviceInfo, + const DataOperStatus &dataStatus, SingleVerNaturalStoreCommitNotifyData *commitData); + + static DataOperStatus JudgeSyncSaveType(DataItem &dataItem, const DataItem &itemGet, + const std::string &devName, bool isHashKeyExisted, bool isPermitForceWrite = true); + + static std::string GetOriginDevName(const DataItem &dataItem, const std::string &origDevGet); + + int GetSyncDataItemPre(const DataItem &itemPut, DataItem &itemGet, Key &hashKey) const; + + int GetSyncDataItemExt(const DataItem &dataItem, DataItem &itemGet, const DataOperStatus &dataStatus) const; + + int GetSyncDataPreByHashKey(const Key &hashKey, DataItem &itemGet) const; + + int PrepareForSyncDataByTime(Timestamp begin, Timestamp end, sqlite3_stmt *&statement, bool getDeletedData = false) + const; + + int StepForResultEntries(sqlite3_stmt *statement, std::vector &entries) const; + + int InitResultSet(const Key &keyPrefix, sqlite3_stmt *&countStmt); + + int InitResultSetCount(QueryObject &queryObj, sqlite3_stmt *&countStmt); + + int InitResultSetContent(QueryObject &queryObj); + + int InitResultSet(QueryObject &queryObj, sqlite3_stmt *&countStmt); + + int GetAllKeys(sqlite3_stmt *statement, std::vector &keys) const; + + int GetAllEntries(sqlite3_stmt *statement, std::vector &entries) const; + + int BindPutKvData(sqlite3_stmt *statement, const Key &key, const Value &value, Timestamp timestamp, + SingleVerDataType type); + + int SaveSyncDataToDatabase(const DataItem &dataItem, const Key &hashKey, const std::string &origDev, + const std::string &deviceName, bool isUpdate); + + int SaveKvData(SingleVerDataType type, const Key &key, const Value &value, Timestamp timestamp); + + int DeleteLocalDataInner(SingleVerNaturalStoreCommitNotifyData *committedData, + const Key &key, const Value &value); + + int PrepareForSavingData(const std::string &readSql, const std::string &insertSql, + const std::string &updateSql, SaveRecordStatements &statements) const; + + int OpenResultSetForCacheRowIdModeCommon(std::vector &rowIdCache, uint32_t cacheLimit, int &count); + + int ResultSetLoadRowIdCache(std::vector &rowIdCache, uint32_t cacheLimit, + uint32_t cacheStartPos, int &count); + + void FinalizeAllStatements(); + int ResetSaveSyncStatements(int errCode); + + int BindSyncDataInCacheMode(sqlite3_stmt *statement, + const DataItem &dataItem, const Key &hashKey, uint64_t recordVersion) const; + + int BindPrimaryKeySyncDataInCacheMode( + sqlite3_stmt *statement, const Key &hashKey, uint64_t recordVersion) const; + + int BindTimestampSyncDataInCacheMode(sqlite3_stmt *statement, const DataItem &dataItem) const; + + int BindDevSyncDataInCacheMode(sqlite3_stmt *statement, + const std::string &origDev, const std::string &deviceName) const; + + int SaveSyncDataToCacheDatabase(const DataItem &dataItem, const Key &hashKey, uint64_t recordVersion) const; + + int GetOneRawDataItem(sqlite3_stmt *statement, DataItem &dataItem, + uint64_t &verInCurCacheDb, bool isCacheDb) const; + int GetAllDataItems(sqlite3_stmt *statement, std::vector &dataItems, + uint64_t &verInCurCacheDb, bool isCacheDb) const; + int DelCacheDbDataByVersion(uint64_t version) const; + + // use for migrating data + int BindLocalDataInCacheMode(sqlite3_stmt *statement, const LocalDataItem &dataItem) const; + + // Process timestamp for syncdata in cacheDB when migrating. + int ProcessTimestampForSyncDataInCacheDB(std::vector &dataItems); + + // Get migrateTimeOffset_. + int InitMigrateTimestampOffset(); + + // Get min timestamp of local data in sync_data, cacheDB. + int GetMinTimestampInCacheDB(Timestamp &minStamp) const; + + // Prepare conflict notify and commit notify data. + int PrepareForNotifyConflictAndObserver(DataItem &dataItem, const DeviceInfo &deviceInfo, + NotifyConflictAndObserverData ¬ify, bool isPermitForceWrite = true); + + // Put observer and conflict data into commit notify when migrating cacheDB. + int PutIntoConflictAndCommitForMigrateCache(DataItem &dataItem, const DeviceInfo &deviceInfo, + NotifyConflictAndObserverData ¬ify, bool isPermitForceWrite); + + int MigrateDataItems(std::vector &dataItems, NotifyMigrateSyncData &syncData); + + int MigrateDataItem(DataItem &dataItem, NotifyMigrateSyncData &syncData); + + int GetEntriesForNotifyRemoveDevData(const DataItem &item, std::vector &entries) const; + + // Reset migrateSyncStatements_. + int ResetForMigrateCacheData(); + + // Init migrating data. + int InitMigrateData(); + + int MigrateRmDevData(const DataItem &dataItem) const; + int VacuumLocalData() const; + + int GetSyncDataItems(std::vector &dataItems, sqlite3_stmt *statement, + size_t appendLength, const DataSizeSpecInfo &dataSizeInfo) const; + + int GetSyncDataWithQuery(sqlite3_stmt *fullStmt, sqlite3_stmt *queryStmt, + size_t appendLength, const DataSizeSpecInfo &dataSizeInfo, std::vector &dataItems) const; + + int CheckMissQueryDataItems(sqlite3_stmt *&stmt, const SqliteQueryHelper &helper, const DeviceInfo &deviceInfo, + std::vector &dataItems); + + int CheckDataWithQuery(std::vector &dataItems); + + int GetExpandedCheckSql(QueryObject query, DataItem &dataItem); + + int CheckMissQueryDataItem(sqlite3_stmt *stmt, const std::string &deviceName, DataItem &item); + + sqlite3_stmt *getSyncStatement_; + sqlite3_stmt *getResultRowIdStatement_; + sqlite3_stmt *getResultEntryStatement_; + SaveRecordStatements saveSyncStatements_; + SaveRecordStatements saveLocalStatements_; + + // Used for migrating sync_data. + SaveRecordStatements migrateSyncStatements_; + bool isTransactionOpen_; + bool attachMetaMode_; // true for attach meta mode + ExecutorState executorState_; + + // Max timestamp in mainDB. Used for migrating. + Timestamp maxTimestampInMainDB_; + + // The offset between min timestamp in cacheDB and max timestamp in mainDB. Used for migrating. + TimeOffset migrateTimeOffset_; + + // Migrating sync flag. When the flag is true, mainDB and cacheDB are attached, migrateSyncStatements_ is set, + // maxTimestampInMainDB_ and migrateTimeOffset_ is meaningful. + bool isSyncMigrating_; + int conflictResolvePolicy_; +}; +} // namespace DistributedDB + +#endif // SQLITE_SINGLE_VER_STORAGE_EXECUTOR_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_cache.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_cache.cpp new file mode 100644 index 0000000000000000000000000000000000000000..62e8f57afa78486ba1f9e7ee72a903694d7615dc --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_cache.cpp @@ -0,0 +1,995 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_storage_executor.h" + +#include + +#include "log_print.h" +#include "db_constant.h" +#include "db_common.h" +#include "db_errno.h" +#include "parcel.h" +#include "runtime_context.h" +#include "sqlite_single_ver_storage_executor_sql.h" + +namespace DistributedDB { +int SQLiteSingleVerStorageExecutor::PrepareForSavingCacheData(SingleVerDataType type) +{ + int errCode = -E_NOT_SUPPORT; + if (type == SingleVerDataType::LOCAL_TYPE) { + std::string insertLocalSql = ((executorState_ == ExecutorState::CACHE_ATTACH_MAIN) ? + INSERT_LOCAL_SQL_FROM_CACHEHANDLE : INSERT_CACHE_LOCAL_SQL); + std::string updateLocalSql = ((executorState_ == ExecutorState::CACHE_ATTACH_MAIN) ? + UPDATE_LOCAL_SQL_FROM_CACHEHANDLE : UPDATE_CACHE_LOCAL_SQL); + errCode = PrepareForSavingData(SELECT_CACHE_LOCAL_HASH_SQL, insertLocalSql, updateLocalSql, + saveLocalStatements_); + } else if (type == SingleVerDataType::SYNC_TYPE) { + std::string insertSyncSql = ((executorState_ == ExecutorState::MAIN_ATTACH_CACHE) ? + INSERT_CACHE_SYNC_SQL_FROM_MAINHANDLE : INSERT_CACHE_SYNC_SQL); + std::string updateSyncSql = ((executorState_ == ExecutorState::MAIN_ATTACH_CACHE) ? + UPDATE_CACHE_SYNC_SQL_FROM_MAINHANDLE : UPDATE_CACHE_SYNC_SQL); + std::string selectSyncHashSql = ((executorState_ == ExecutorState::MAIN_ATTACH_CACHE) ? + SELECT_CACHE_SYNC_HASH_SQL_FROM_MAINHANDLE : SELECT_CACHE_SYNC_HASH_SQL); + errCode = PrepareForSavingData(selectSyncHashSql, insertSyncSql, updateSyncSql, saveSyncStatements_); + } + if (errCode != E_OK) { + LOGE("Prepare to save sync cache data failed:%d", errCode); + } + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::ResetForSavingCacheData(SingleVerDataType type) +{ + int errCode = E_OK; + if (type == SingleVerDataType::LOCAL_TYPE) { + SQLiteUtils::ResetStatement(saveLocalStatements_.insertStatement, false, errCode); + SQLiteUtils::ResetStatement(saveLocalStatements_.updateStatement, false, errCode); + SQLiteUtils::ResetStatement(saveLocalStatements_.queryStatement, false, errCode); + } else if (type == SingleVerDataType::SYNC_TYPE) { + SQLiteUtils::ResetStatement(saveSyncStatements_.insertStatement, false, errCode); + SQLiteUtils::ResetStatement(saveSyncStatements_.updateStatement, false, errCode); + SQLiteUtils::ResetStatement(saveSyncStatements_.queryStatement, false, errCode); + } + + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::ResetForMigrateCacheData() +{ + int errCode = E_OK; + SQLiteUtils::ResetStatement(migrateSyncStatements_.insertStatement, false, errCode); + SQLiteUtils::ResetStatement(migrateSyncStatements_.updateStatement, false, errCode); + SQLiteUtils::ResetStatement(migrateSyncStatements_.queryStatement, false, errCode); + + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::RemoveDeviceDataInCacheMode(const std::string &deviceName, + bool isNeedNotify, uint64_t recordVersion) const +{ + // Transfer the device name. + std::string devName = DBCommon::TransferHashString(deviceName); + std::vector devVect(devName.begin(), devName.end()); + + Key hashKey; + int errCode = DBCommon::CalcValueHash(REMOVE_DEVICE_DATA_KEY, hashKey); + if (errCode != E_OK) { + return errCode; + } + + DataItem dataItem; + dataItem.key = REMOVE_DEVICE_DATA_KEY; + dataItem.value = devVect; + if (isNeedNotify) { + dataItem.flag = DataItem::REMOVE_DEVICE_DATA_NOTIFY_FLAG; + } else { + dataItem.flag = DataItem::REMOVE_DEVICE_DATA_FLAG; + } + + sqlite3_stmt *statement = nullptr; + std::string sql = (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) ? + INSERT_CACHE_SYNC_SQL_FROM_MAINHANDLE : INSERT_CACHE_SYNC_SQL; + errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = BindSyncDataInCacheMode(statement, dataItem, hashKey, recordVersion); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGE("Failed to execute rm the device synced data:%d", errCode); + } else { + errCode = E_OK; + } + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::GetMinVersionCacheData( + std::vector &dataItems, uint64_t &minVerIncurCacheDb) const +{ + std::string sql; + if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) { + sql = MIGRATE_SELECT_MIN_VER_CACHEDATA_FROM_MAINHANDLE; + } else if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + sql = MIGRATE_SELECT_MIN_VER_CACHEDATA_FROM_CACHEHANDLE; + } else { + return -E_INVALID_ARGS; + } + + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + LOGE("GetStatement fail when get min version cache data! errCode = [%d]", errCode); + goto END; + } + + errCode = GetAllDataItems(statement, dataItems, minVerIncurCacheDb, true); + if (errCode != E_OK) { + LOGE("Failed to get all the data items by the min version:[%d]", errCode); + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::MigrateRmDevData(const DataItem &dataItem) const +{ + if (dataItem.key != REMOVE_DEVICE_DATA_KEY) { + LOGE("This item not means remove devices data, can not continue exe!"); + return -E_INVALID_ARGS; + } + + std::string sql; + if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) { + sql = REMOVE_DEV_DATA_SQL; + } else if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + sql = REMOVE_DEV_DATA_SQL_FROM_CACHEHANDLE; + } else { + return -E_INVALID_ARGS; + } + + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + LOGE("GetStatement fail when remove device data migrating-data to main! errCode = [%d]", errCode); + return CheckCorruptedStatus(errCode); + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, dataItem.value, true); + if (errCode != E_OK) { + LOGE("[singerVerExecutor][MiRmData] Bind dev for sync data failed:%d", errCode); + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::AttachMainDbAndCacheDb(CipherType type, const CipherPassword &passwd, + const std::string &attachDbAbsPath, EngineState engineState) +{ + std::string attachAsName; + if (engineState == EngineState::MAINDB) { + attachAsName = "cache"; + } else if (engineState == EngineState::CACHEDB) { + attachAsName = "maindb"; + } else if (engineState == EngineState::ATTACHING) { + executorState_ = ExecutorState::MAIN_ATTACH_CACHE; + return E_OK; + } else { + return -E_INVALID_ARGS; + } + + int errCode = SQLiteUtils::AttachNewDatabase(dbHandle_, type, passwd, attachDbAbsPath, attachAsName); + if (errCode != E_OK) { + LOGE("handle attach to [%s] fail! errCode = [%d]", attachAsName.c_str(), errCode); + return CheckCorruptedStatus(errCode); + } + + if (engineState == EngineState::MAINDB) { + executorState_ = ExecutorState::MAIN_ATTACH_CACHE; + } else if (engineState == EngineState::CACHEDB) { + executorState_ = ExecutorState::CACHE_ATTACH_MAIN; + } else { + return -E_INVALID_ARGS; + } + LOGD("[singleVerExecutor][attachDb] current engineState[%u], executorState[%u]", static_cast(engineState), + static_cast(executorState_)); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::GetMaxVersionIncacheDb(uint64_t &maxVersion) const +{ + sqlite3_stmt *statement = nullptr; + std::string sql; + if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) { + sql = GET_MAX_VER_CACHEDATA_FROM_MAINHANDLE; + } else if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + sql = GET_MAX_VER_CACHEDATA_FROM_CACHEHANDLE; + } else { + return -E_INVALID_ARGS; + } + + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + LOGE("GetStatement fail when get max version in cache db! errCode = [%d]", errCode); + goto END; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + maxVersion = static_cast(sqlite3_column_int64(statement, 0)); + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else { + LOGE("SQLite step failed:%d", errCode); + break; + } + } while (true); + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::MigrateDataItem(DataItem &dataItem, NotifyMigrateSyncData &syncData) +{ + // Put or delete. Prepare notify data here. + NotifyConflictAndObserverData notify; + notify.committedData = syncData.committedData; + int errCode = PutIntoConflictAndCommitForMigrateCache(dataItem, {dataItem.dev.empty(), dataItem.dev}, notify, + syncData.isPermitForceWrite); + if (errCode != E_OK) { + ResetForMigrateCacheData(); + LOGE("PutIntoConflictAndCommitForMigrateCache failed, errCode = %d", errCode); + return errCode; + } + // after solving conflict, the item should not be saved into mainDB + if (notify.dataStatus.isDefeated) { + LOGD("Data status is defeated:%d", errCode); + return errCode; + } + bool isUpdate = notify.dataStatus.preStatus != DataStatus::NOEXISTED; + sqlite3_stmt *statement = migrateSyncStatements_.GetDataSaveStatement(isUpdate); + if (statement == nullptr) { + LOGE("GetStatement fail when put migrating-data to main! "); + return -E_INVALID_ARGS; + } + + if ((dataItem.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) != 0) { + errCode = EraseSyncData(dataItem.key); + goto END; + } + + errCode = BindSavedSyncData(statement, dataItem, dataItem.hashKey, { dataItem.origDev, dataItem.dev }, isUpdate); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } else { + LOGD("StepWithRetry fail when put migrating-data to main!"); + } +END: + ResetForMigrateCacheData(); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::CheckDataWithQuery(std::vector &dataItems) +{ + int errCode = E_OK; + sqlite3_stmt *stmt = nullptr; + for (auto &item : dataItems) { + if ((item.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) == 0) { + continue; + } + std::string sql; + DBCommon::VectorToString(item.value, sql); + if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + static const std::string SYNC_DATA_TABLE = "sync_data"; + static const std::string SYNC_DATA_TABLE_MAIN = "maindb.sync_data"; + std::string::size_type startPos = sql.find(SYNC_DATA_TABLE); + if (startPos != std::string::npos) { + sql.replace(startPos, SYNC_DATA_TABLE.length(), SYNC_DATA_TABLE_MAIN); + } + } + errCode = SQLiteUtils::GetStatement(dbHandle_, sql, stmt); + if (errCode != E_OK) { + LOGE("Get Check miss query data statement failed. %d", errCode); + return errCode; + } + + errCode = CheckMissQueryDataItem(stmt, item.dev, item); + if (errCode != E_OK) { + LOGE("Check miss query data item failed. %d", errCode); + break; + } + SQLiteUtils::ResetStatement(stmt, true, errCode); + } + SQLiteUtils::ResetStatement(stmt, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::MigrateDataItems(std::vector &dataItems, NotifyMigrateSyncData &syncData) +{ + syncData.isRemote = ((dataItems[0].flag & DataItem::LOCAL_FLAG) == 0); + syncData.isRemoveDeviceData = (dataItems[0].flag & DataItem::REMOVE_DEVICE_DATA_FLAG) != 0 || + (dataItems[0].flag & DataItem::REMOVE_DEVICE_DATA_NOTIFY_FLAG) != 0; + + int errCode = CheckDataWithQuery(dataItems); + if (errCode != E_OK) { + LOGE("Check migrate data with query failed! errCode = [%d]", errCode); + goto END; + } + + for (auto &item : dataItems) { + // Remove device data owns one version itself. + // Get entry here. Prepare notify data in storageEngine. + if (syncData.isRemoveDeviceData) { + errCode = GetEntriesForNotifyRemoveDevData(item, syncData.entries); + if (errCode != E_OK) { + LOGE("Failed to get remove devices data"); + return errCode; + } + errCode = MigrateRmDevData(item); + LOGI("[PutMigratingDataToMain]Execute remove devices data! errCode = [%d]", errCode); + if (errCode != E_OK) { + break; + } + continue; + } + + if (item.neglect) { // Do not save this record if it is neglected + continue; + } + + errCode = MigrateDataItem(item, syncData); + if (errCode != E_OK) { + LOGE("Migrate data item to main db failed! errCode = [%d]", errCode); + break; + } + } +END: + ResetForMigrateCacheData(); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::MigrateSyncDataByVersion(uint64_t recordVer, NotifyMigrateSyncData &syncData, + std::vector &dataItems) +{ + int errCode = StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + return errCode; + } + + // Init migrate data. + errCode = InitMigrateData(); + if (errCode != E_OK) { + LOGE("Init migrate data failed, errCode = [%d]", errCode); + goto END; + } + + // fix dataItem timestamp for migrate + errCode = ProcessTimestampForSyncDataInCacheDB(dataItems); + if (errCode != E_OK) { + LOGE("Change the time stamp for migrate failed! errCode = [%d]", errCode); + goto END; + } + + errCode = MigrateDataItems(dataItems, syncData); + if (errCode != E_OK) { + goto END; + } + + // delete recordVersion data + errCode = DelCacheDbDataByVersion(recordVer); + if (errCode != E_OK) { + LOGE("Delete the migrated data in cacheDb! errCode = [%d]", errCode); + goto END; + } + + errCode = Commit(); + if (errCode != E_OK) { + LOGE("Commit data error and rollback, errCode = [%d]", errCode); + goto END; + } + return E_OK; +END: + Rollback(); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::DelCacheDbDataByVersion(uint64_t version) const +{ + std::string sql; + if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) { + sql = MIGRATE_DEL_DATA_BY_VERSION_FROM_MAINHANDLE; + } else if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + sql = MIGRATE_DEL_DATA_BY_VERSION_FROM_CACHEHANDLE; + } else { + return -E_INVALID_ARGS; + } + + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + LOGE("GetStatement fail when delete cache data by version! errCode = [%d]", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, 1, static_cast(version)); + if (errCode != E_OK) { + LOGE("[SingleVerExe] Bind destDbNickName error:[%d]", errCode); + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::VacuumLocalData() const +{ + std::string sql; + if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) { + sql = MIGRATE_VACUUM_LOCAL_SQL_FROM_MAINHANDLE; + } else if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + sql = MIGRATE_VACUUM_LOCAL_SQL_FROM_CACHEHANDLE; + } else { + return -E_INVALID_ARGS; + } + + int errCode = SQLiteUtils::ExecuteRawSQL(dbHandle_, sql); + if (errCode != E_OK) { + LOGE("SQLite sync mode failed: %d", errCode); + } + + return CheckCorruptedStatus(errCode); +} + +// The local table data is only for local reading and writing, which can be sensed by itself. +// The current migration process does not provide callback subscription function. +int SQLiteSingleVerStorageExecutor::MigrateLocalData() +{ + // Nick name "main" represent current database(dbhande) in sqlite grammar + std::string migrateLocaldataSql; + if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) { + migrateLocaldataSql = MIGRATE_LOCAL_SQL_FROM_MAINHANDLE; + } else if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + migrateLocaldataSql = MIGRATE_LOCAL_SQL_FROM_CACHEHANDLE; + } else { + return -E_INVALID_ARGS; + } + + int errCode = SQLiteUtils::ExecuteRawSQL(dbHandle_, migrateLocaldataSql); + if (errCode != E_OK) { + LOGW("Failed to migrate the local data:%d", errCode); + return CheckCorruptedStatus(errCode); + } + + return VacuumLocalData(); +} + +int SQLiteSingleVerStorageExecutor::BindSyncDataInCacheMode(sqlite3_stmt *statement, + const DataItem &dataItem, const Key &hashKey, uint64_t recordVersion) const +{ + int errCode = BindPrimaryKeySyncDataInCacheMode(statement, hashKey, recordVersion); + if (errCode != E_OK) { + LOGE("Bind saved sync data primary key failed:%d", errCode); + return errCode; + } + + // if delete flag is set, just use the hash key instead of the key + if ((dataItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG) { + errCode = SQLiteUtils::MapSQLiteErrno(sqlite3_bind_zeroblob(statement, BIND_CACHE_SYNC_KEY_INDEX, -1)); + } else { + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_CACHE_SYNC_KEY_INDEX, dataItem.key, false); + } + + if (errCode != E_OK) { + LOGE("Bind saved sync data key failed:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_CACHE_SYNC_VAL_INDEX, dataItem.value, true); + if (errCode != E_OK) { + LOGE("Bind saved sync data value failed:%d", errCode); + return errCode; + } + + LOGD("Write timestamp:%" PRIu64 " timestamp:%" PRIu64 ", flag:%" PRIu64 ", version:%" PRIu64, + dataItem.writeTimestamp, dataItem.timestamp, dataItem.flag, recordVersion); + errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_CACHE_SYNC_FLAG_INDEX, + static_cast(dataItem.flag)); + if (errCode != E_OK) { + LOGE("Bind saved sync data flag failed:%d", errCode); + return errCode; + } + errCode = BindTimestampSyncDataInCacheMode(statement, dataItem); + if (errCode != E_OK) { + LOGE("Bind saved sync data time stamp failed:%d", errCode); + return errCode; + } + return BindDevSyncDataInCacheMode(statement, dataItem.origDev, dataItem.dev); +} + +int SQLiteSingleVerStorageExecutor::BindPrimaryKeySyncDataInCacheMode( + sqlite3_stmt *statement, const Key &hashKey, uint64_t recordVersion) const +{ + int errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_CACHE_SYNC_HASH_KEY_INDEX, hashKey, false); + if (errCode != E_OK) { + LOGE("Bind saved sync data hash key failed:%d", errCode); + return errCode; + } + errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_CACHE_SYNC_VERSION_INDEX, recordVersion); + if (errCode != E_OK) { + LOGE("Bind saved sync data version failed:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::BindTimestampSyncDataInCacheMode( + sqlite3_stmt *statement, const DataItem &dataItem) const +{ + int errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_CACHE_SYNC_STAMP_INDEX, dataItem.timestamp); + if (errCode != E_OK) { + LOGE("Bind saved sync data stamp failed:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_CACHE_SYNC_W_TIME_INDEX, dataItem.writeTimestamp); + if (errCode != E_OK) { + LOGE("Bind saved sync data write stamp failed:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::BindDevSyncDataInCacheMode(sqlite3_stmt *statement, + const std::string &origDev, const std::string &deviceName) const +{ + std::string devName = DBCommon::TransferHashString(deviceName); + std::vector devVect(devName.begin(), devName.end()); + int errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_CACHE_SYNC_DEV_INDEX, devVect, true); + if (errCode != E_OK) { + LOGE("Bind dev for sync data failed:%d", errCode); + return errCode; + } + + std::vector origDevVect(origDev.begin(), origDev.end()); + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_CACHE_SYNC_ORI_DEV_INDEX, origDevVect, true); + if (errCode != E_OK) { + LOGE("Bind orig dev for sync data failed:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::GetExpandedCheckSql(QueryObject query, DataItem &dataItem) +{ + int errCode = E_OK; + SqliteQueryHelper helper = query.GetQueryHelper(errCode); + + std::string sql; + std::string expandedSql; + errCode = helper.GetSyncDataCheckSql(sql); + if (errCode != E_OK) { + LOGE("Get sync data check sql failed"); + return errCode; + } + sqlite3_stmt *stmt = nullptr; + errCode = SQLiteUtils::GetStatement(dbHandle_, sql, stmt); + if (errCode != E_OK) { + LOGE("Get statement fail. %d", errCode); + return -E_INVALID_QUERY_FORMAT; + } + + errCode = helper.BindSyncDataCheckStmt(stmt, dataItem.key); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::ExpandedSql(stmt, expandedSql); + if (errCode != E_OK) { + LOGE("Get expand sql fail. %d", errCode); + } + DBCommon::StringToVector(expandedSql, dataItem.value); +END: + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::SaveSyncDataItemInCacheMode(DataItem &dataItem, const DeviceInfo &deviceInfo, + Timestamp &maxStamp, uint64_t recordVersion, const QueryObject &query) +{ + Key hashKey; + int errCode = E_OK; + if ((dataItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG) { + hashKey = dataItem.key; + } else { + errCode = DBCommon::CalcValueHash(dataItem.key, hashKey); + if (errCode != E_OK) { + return errCode; + } + } + + if ((dataItem.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) != 0) { + errCode = GetExpandedCheckSql(query, dataItem); // record check sql in value for miss query data + if (errCode != E_OK) { + LOGE("Get sync data check sql failed. %d", errCode); + return errCode; + } + } + + std::string origDev = dataItem.origDev; + if (((dataItem.flag & DataItem::LOCAL_FLAG) != 0) && dataItem.origDev.empty()) { + origDev.clear(); + } + dataItem.dev = deviceInfo.deviceName; + dataItem.origDev = origDev; + errCode = SaveSyncDataToCacheDatabase(dataItem, hashKey, recordVersion); + if (errCode == E_OK) { + maxStamp = std::max(dataItem.timestamp, maxStamp); + } else { + LOGE("Save sync data to db failed:%d", errCode); + } + return ResetForSavingCacheData(SingleVerDataType::SYNC_TYPE); +} + +int SQLiteSingleVerStorageExecutor::SaveSyncDataToCacheDatabase(const DataItem &dataItem, + const Key &hashKey, uint64_t recordVersion) const +{ + auto statement = saveSyncStatements_.GetDataSaveStatement(false); + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = BindSyncDataInCacheMode(statement, dataItem, hashKey, recordVersion); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::PutLocalDataToCacheDB(const LocalDataItem &dataItem) const +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, INSERT_CACHE_LOCAL_SQL, statement); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = BindLocalDataInCacheMode(statement, dataItem); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::BindLocalDataInCacheMode(sqlite3_stmt *statement, + const LocalDataItem &dataItem) const +{ + int errCode = SQLiteUtils::BindBlobToStatement(statement, + BIND_CACHE_LOCAL_HASH_KEY_INDEX, dataItem.hashKey, false); + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindLocalData]Bind hash key error:%d", errCode); + return errCode; + } + + // if delete flag is set, just use the hash key instead of the key + if ((dataItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG) { + errCode = SQLiteUtils::MapSQLiteErrno(sqlite3_bind_zeroblob(statement, BIND_CACHE_LOCAL_KEY_INDEX, -1)); + } else { + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_CACHE_LOCAL_KEY_INDEX, dataItem.key, false); + } + + if (errCode != E_OK) { + LOGE("Bind saved sync data key failed:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, BIND_CACHE_LOCAL_VAL_INDEX, dataItem.value, true); + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindLocalData]Bind value error:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_CACHE_LOCAL_TIMESTAMP_INDEX, dataItem.timestamp); + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindLocalData]Bind timestamp error:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, BIND_CACHE_LOCAL_FLAG_INDEX, + static_cast(dataItem.flag)); + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindLocalData]Bind local data flag failed:%d", errCode); + return errCode; + } + + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::PutIntoConflictAndCommitForMigrateCache(DataItem &dataItem, + const DeviceInfo &deviceInfo, NotifyConflictAndObserverData ¬ify, bool isPermitForceWrite) +{ + int errCode = PrepareForNotifyConflictAndObserver(dataItem, deviceInfo, notify, isPermitForceWrite); + if (errCode != E_OK) { + errCode = (errCode == -E_NOT_FOUND ? E_OK : errCode); + if (errCode == -E_IGNORE_DATA) { + notify.dataStatus.isDefeated = true; + errCode = E_OK; + } + return errCode; + } + + // If delete data, the key is empty. + if (isSyncMigrating_ && dataItem.key.empty()) { + dataItem.key = notify.getData.key; + } + + PutConflictData(dataItem, notify.getData, deviceInfo, notify.dataStatus, notify.committedData); + if (notify.dataStatus.isDefeated) { + LOGD("Data status is defeated:%d", errCode); + return ResetForMigrateCacheData(); + } + + PutIntoCommittedData(dataItem, notify.getData, notify.dataStatus, notify.hashKey, notify.committedData); + return ResetForMigrateCacheData(); +} + +int SQLiteSingleVerStorageExecutor::GetMinTimestampInCacheDB(Timestamp &minStamp) const +{ + if (dbHandle_ == nullptr) { + return E_OK; + } + std::string sql = ((executorState_ == ExecutorState::CACHE_ATTACH_MAIN) ? + SELECT_NATIVE_MIN_TIMESTAMP_IN_CACHE_SYNC_DATA_SQL : + SELECT_NATIVE_MIN_TIMESTAMP_IN_CACHE_SYNC_DATA_SQL_FROM_MAINHANDLE); + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + goto ERROR; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + minStamp = static_cast(sqlite3_column_int64(statement, 0)); // get the first column + LOGD("Min time stamp in cacheDB is %" PRIu64, minStamp); + errCode = E_OK; + } else { + LOGE("GetMinTimestampInCacheDB failed, errCode = %d.", errCode); + } + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::InitMigrateTimestampOffset() +{ + // Not first migrate, migrateTimeOffset_ has been set. + if (migrateTimeOffset_ != 0) { + return E_OK; + } + + // Get min timestamp of local data in sync_data, cacheDB. + Timestamp minTimeInCache = 0; + int errCode = GetMinTimestampInCacheDB(minTimeInCache); + if (errCode != E_OK) { + return errCode; + } + + // There is no native data in cacheDB, cannot get accurate migrateTimeOffset_ now. + if (minTimeInCache == 0) { + migrateTimeOffset_ = -1; + LOGI("Time offset during migrating is -1."); + return E_OK; + } + + // Get max timestamp in mainDB. + Timestamp maxTimeInMain = 0; + InitCurrentMaxStamp(maxTimeInMain); + + // Get timestamp offset between mainDB and cacheDB. + // The purpose of -1 is to ensure that the first data record in the original cacheDB is 1 greater than + // the last data record in the original mainDB after the migration. + migrateTimeOffset_ = minTimeInCache - maxTimeInMain - 1; + LOGI("Min timestamp in cacheDB is %" PRIu64 ", max timestamp in mainDB is %" PRIu64 ". Time offset during migrating" + " is %" PRId64 ".", minTimeInCache, maxTimeInMain, migrateTimeOffset_); + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::ProcessTimestampForSyncDataInCacheDB(std::vector &dataItems) +{ + if (dataItems.empty()) { + LOGE("[SQLiteSingleVerStorageExecutor::ProcessTimestampForCacheDB] Invalid parameter : dataItems."); + return -E_INVALID_ARGS; + } + + // Get the offset between the min timestamp in dataitems and max timestamp in mainDB. + int errCode = InitMigrateTimestampOffset(); + if (errCode != E_OK) { + return errCode; + } + + // Set real timestamp for DataItem in dataItems and get the max timestamp in these dataitems. + Timestamp maxTimeInDataItems = 0; + for (auto &item : dataItems) { + item.timestamp -= migrateTimeOffset_; + maxTimeInDataItems = std::max(maxTimeInDataItems, item.timestamp); + } + + // Update max timestamp in mainDB. + maxTimestampInMainDB_ = maxTimeInDataItems; + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::GetEntriesForNotifyRemoveDevData(const DataItem &item, + std::vector &entries) const +{ + // When removing device data, key is 'remove', value is device name. + if (item.key != REMOVE_DEVICE_DATA_KEY) { + LOGE("Invalid key. Can not notify remove device data."); + return -E_INVALID_ARGS; + } + if ((item.flag & DataItem::REMOVE_DEVICE_DATA_NOTIFY_FLAG) == 0) { + LOGI("No need to notify remove device data."); + return E_OK; + } + entries.clear(); + std::string dev; + DBCommon::VectorToString(item.value, dev); + return GetAllSyncedEntries(dev, entries); +} + +int SQLiteSingleVerStorageExecutor::InitMigrateData() +{ + // Sync_data already in migrating. Need not to init data. + if (isSyncMigrating_) { + return E_OK; + } + ClearMigrateData(); + std::string querySQL; + std::string insertSQL; + std::string updateSQL; + if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) { + querySQL = SELECT_SYNC_HASH_SQL; + insertSQL = MIGRATE_INSERT_DATA_TO_MAINDB_FROM_MAINHANDLE; + updateSQL = MIGRATE_UPDATE_DATA_TO_MAINDB_FROM_MAINHANDLE; + } else if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + querySQL = SELECT_MAIN_SYNC_HASH_SQL_FROM_CACHEHANDLE; + insertSQL = MIGRATE_INSERT_DATA_TO_MAINDB_FROM_CACHEHANDLE; + updateSQL = MIGRATE_UPDATE_DATA_TO_MAINDB_FROM_CACHEHANDLE; + } else { + LOGE("[InitMigrateData] executor in an error state[%u]!", static_cast(executorState_)); + return -E_INVALID_DB; + } + int errCode = PrepareForSavingData(querySQL, insertSQL, updateSQL, migrateSyncStatements_); + if (errCode != E_OK) { + LOGE("Prepare migrateSyncStatements_ fail, errCode = %d", errCode); + return errCode; + } + isSyncMigrating_ = true; + return errCode; +} + +void SQLiteSingleVerStorageExecutor::ClearMigrateData() +{ + // Reset data. + migrateTimeOffset_ = 0; + maxTimestampInMainDB_ = 0; + + // Reset statement. + int errCode = migrateSyncStatements_.ResetStatement(); + if (errCode != E_OK) { + LOGE("Reset migrateSync Statements failed, errCode = %d", errCode); + } + + isSyncMigrating_ = false; +} + +int SQLiteSingleVerStorageExecutor::GetMaxTimestampDuringMigrating(Timestamp &maxTimestamp) const +{ + if (maxTimestampInMainDB_ == 0) { + return -E_NOT_INIT; + } + maxTimestamp = maxTimestampInMainDB_; + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::DeleteMetaData(const std::vector &keys) +{ + sqlite3_stmt *statement = nullptr; + const std::string sql = attachMetaMode_ ? REMOVE_ATTACH_META_VALUE_SQL : REMOVE_META_VALUE_SQL; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + return errCode; + } + + for (const auto &key : keys) { + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // first arg. + if (errCode != E_OK) { + break; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + break; + } + errCode = E_OK; + SQLiteUtils::ResetStatement(statement, false, errCode); + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::DeleteMetaDataByPrefixKey(const Key &keyPrefix) +{ + sqlite3_stmt *statement = nullptr; + const std::string sql = attachMetaMode_ ? + REMOVE_ATTACH_META_VALUE_BY_KEY_PREFIX_SQL : REMOVE_META_VALUE_BY_KEY_PREFIX_SQL; + + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::BindPrefixKey(statement, 1, keyPrefix); // 1 is first arg. + if (errCode == E_OK) { + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_sql.h b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_sql.h new file mode 100644 index 0000000000000000000000000000000000000000..a0a071e968b0415be24710dec482ae3a20430c4d --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_sql.h @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_SINGLE_VER_STORAGE_EXECUTOR_SQL_H +#define SQLITE_SINGLE_VER_STORAGE_EXECUTOR_SQL_H + +#include + +#include "types_export.h" + +namespace DistributedDB { + // cache.sync_data is design for migrating action after process restart + const std::string INSERT_LOCAL_SQL = + "INSERT OR REPLACE INTO local_data VALUES(?,?,?,?);"; + const std::string INSERT_LOCAL_SQL_FROM_CACHEHANDLE = + "INSERT OR REPLACE INTO maindb.local_data VALUES(?,?,?,?);"; + + const std::string INSERT_CACHE_LOCAL_SQL = + "INSERT OR REPLACE INTO local_data VALUES(?,?,?,?,?);"; + + const std::string UPDATE_LOCAL_SQL_FROM_CACHEHANDLE = + "UPDATE maindb.local_data SET key=?,value=?,timestamp=? where hash_key=?"; + + const std::string UPDATE_CACHE_LOCAL_SQL = + "UPDATE local_data SET key=?,value=?,timestamp=? where hash_key=?"; + + const std::string INSERT_META_SQL = + "INSERT OR REPLACE INTO meta_data VALUES(?,?);"; + + const std::string INSERT_ATTACH_META_SQL = + "INSERT OR REPLACE INTO meta.meta_data VALUES(?,?);"; + + const std::string INSERT_SYNC_SQL = + "INSERT INTO sync_data VALUES(?,?,?,?,?,?,?,?);"; + + const std::string UPDATE_SYNC_SQL = + "UPDATE sync_data SET key=?,value=?,timestamp=?,flag=?,device=?,ori_device=?,w_timestamp=? WHERE hash_key=?;"; + + const std::string INSERT_CACHE_SYNC_SQL = + "INSERT OR REPLACE INTO sync_data VALUES(?,?,?,?,?,?,?,?,?);"; + const std::string INSERT_CACHE_SYNC_SQL_FROM_MAINHANDLE = + "INSERT OR REPLACE INTO cache.sync_data VALUES(?,?,?,?,?,?,?,?,?);"; + + const std::string UPDATE_CACHE_SYNC_SQL = + "UPDATE sync_data SET key=?,value=?,timestamp=?,flag=?,device=?,ori_device=?,w_timestamp=? WHERE hash_key=?;"; + + const std::string UPDATE_CACHE_SYNC_SQL_FROM_MAINHANDLE = + "UPDATE cache.sync_data SET key=?,value=?,timestamp=?,flag=?,device=?,ori_device=?,w_timestamp=? " + "WHERE hash_key=?;"; + + const std::string DELETE_LOCAL_SQL = + "DELETE FROM local_data WHERE key=?;"; + const std::string DELETE_LOCAL_SQL_FROM_CACHEHANDLE = + "DELETE FROM maindb.local_data WHERE key=?;"; + + const std::string SELECT_ALL_META_KEYS = + "SELECT key FROM meta_data;"; + + const std::string SELECT_ATTACH_ALL_META_KEYS = + "SELECT key FROM meta.meta_data;"; + + const std::string SELECT_ALL_SYNC_ENTRIES_BY_DEV = + "SELECT key, value FROM sync_data WHERE device=? AND (flag&0x03=0);"; + + const std::string SELECT_ALL_SYNC_ENTRIES_BY_DEV_FROM_CACHEHANDLE = + "SELECT key, value FROM maindb.sync_data WHERE device=? AND (flag&0x03=0);"; + + const std::string SELECT_LOCAL_VALUE_TIMESTAMP_SQL = + "SELECT value, timestamp FROM local_data WHERE key=?;"; + + const std::string SELECT_SYNC_SQL = + "SELECT * FROM sync_data WHERE key=?;"; + + const std::string SELECT_SYNC_VALUE_WTIMESTAMP_SQL = + "SELECT value, w_timestamp FROM sync_data WHERE key=?;"; + + const std::string SELECT_SYNC_HASH_SQL = + "SELECT * FROM sync_data WHERE hash_key=?;"; + + const std::string SELECT_CACHE_SYNC_HASH_SQL = + "SELECT * FROM sync_data WHERE hash_key=? AND version=?;"; + const std::string SELECT_CACHE_SYNC_HASH_SQL_FROM_MAINHANDLE = + "SELECT * FROM cache.sync_data WHERE hash_key=? AND version=?;"; + + const std::string SELECT_LOCAL_HASH_SQL = + "SELECT * FROM local_data WHERE hash_key=?;"; + + const std::string SELECT_CACHE_LOCAL_HASH_SQL = + "SELECT * FROM local_data WHERE hash_key=?;"; + + const std::string SELECT_META_VALUE_SQL = + "SELECT value FROM meta_data WHERE key=?;"; + + const std::string SELECT_ATTACH_META_VALUE_SQL = + "SELECT value FROM meta.meta_data WHERE key=?;"; + + const std::string SELECT_MAX_TIMESTAMP_SQL = + "SELECT MAX(timestamp) FROM sync_data;"; + const std::string SELECT_MAX_TIMESTAMP_SQL_FROM_CACHEHANDLE = + "SELECT MAX(timestamp) FROM maindb.sync_data;"; + + const std::string SELECT_NATIVE_MIN_TIMESTAMP_IN_CACHE_SYNC_DATA_SQL = + "SELECT MIN(timestamp) FROM sync_data WHERE flag&0x02=0x02;"; + const std::string SELECT_NATIVE_MIN_TIMESTAMP_IN_CACHE_SYNC_DATA_SQL_FROM_MAINHANDLE = + "SELECT MIN(timestamp) FROM cache.sync_data WHERE flag&0x02=0x02;"; + + const std::string SELECT_SYNC_ENTRIES_SQL = + "SELECT * FROM sync_data WHERE timestamp >= ? AND timestamp < ? AND (flag&0x02=0x02) ORDER BY timestamp ASC;"; + + const std::string SELECT_SYNC_DELETED_ENTRIES_SQL = + "SELECT * FROM sync_data WHERE timestamp >= ? AND timestamp < ? AND (flag&0x03=0x03) ORDER BY timestamp ASC;"; + + const std::string SELECT_SYNC_MODIFY_SQL = + "SELECT * FROM sync_data WHERE timestamp >= ? AND timestamp < ? AND (flag&0x03=0x02) ORDER BY timestamp ASC;"; + + const std::string SELECT_SYNC_PREFIX_SQL = + "SELECT key, value FROM sync_data WHERE key>=? AND key<=? AND (flag&0x01=0) ORDER BY key ASC;"; + + const std::string SELECT_SYNC_ROWID_PREFIX_SQL = + "SELECT rowid FROM sync_data WHERE key>=? AND key<=? AND (flag&0x01=0) ORDER BY key ASC;"; + + const std::string SELECT_SYNC_DATA_BY_ROWID_SQL = + "SELECT key, value FROM sync_data WHERE rowid=?;"; + + const std::string SELECT_LOCAL_PREFIX_SQL = + "SELECT key, value FROM local_data WHERE key>=? AND key<=? ORDER BY key ASC;"; + + const std::string SELECT_COUNT_SYNC_PREFIX_SQL = + "SELECT count(key) FROM sync_data WHERE key>=? AND key<=? AND (flag&0x01=0);"; + + const std::string REMOVE_DEV_DATA_SQL = + "DELETE FROM sync_data WHERE device=? AND (flag&0x02=0);"; + const std::string REMOVE_DEV_DATA_SQL_FROM_CACHEHANDLE = + "DELETE FROM maindb.sync_data WHERE device=? AND (flag&0x02=0);"; + + const std::string SELECT_ENTRY_DEVICE = + "SELECT ori_device, device FROM sync_data WHERE key=?;"; + + // sql for migrating data + const std::string MIGRATE_LOCAL_SQL_FROM_CACHEHANDLE = + "INSERT OR REPLACE INTO maindb.local_data select key, value, timestamp, hash_key from main.local_data;"; + const std::string MIGRATE_LOCAL_SQL_FROM_MAINHANDLE = + "INSERT OR REPLACE INTO main.local_data select key, value, timestamp, hash_key from cache.local_data;"; + + const std::string MIGRATE_VACUUM_LOCAL_SQL_FROM_CACHEHANDLE = + "DELETE FROM maindb.local_data where hash_key in (select hash_key FROM maindb.local_data where key is null);"; + const std::string MIGRATE_VACUUM_LOCAL_SQL_FROM_MAINHANDLE = + "DELETE FROM main.local_data where hash_key in (select hash_key FROM main.local_data where key is null);"; + + // version is index, order by better than MIN() + const std::string MIGRATE_SELECT_MIN_VER_CACHEDATA_FROM_CACHEHANDLE = + "SELECT * FROM sync_data where version = (select version from sync_data order by version limit 1);"; + const std::string MIGRATE_SELECT_MIN_VER_CACHEDATA_FROM_MAINHANDLE = + "SELECT * FROM cache.sync_data where version = (select version from cache.sync_data order by version limit 1);"; + + const std::string GET_MAX_VER_CACHEDATA_FROM_CACHEHANDLE = + "select version from sync_data order by version DESC limit 1;"; + const std::string GET_MAX_VER_CACHEDATA_FROM_MAINHANDLE = + "select version from cache.sync_data order by version DESC limit 1;"; + + const std::string MIGRATE_INSERT_DATA_TO_MAINDB_FROM_CACHEHANDLE = + "INSERT INTO maindb.sync_data VALUES(?,?,?,?,?,?,?,?);"; + const std::string MIGRATE_UPDATE_DATA_TO_MAINDB_FROM_CACHEHANDLE = + "UPDATE maindb.sync_data SET key=?,value=?,timestamp=?,flag=?,device=?,ori_device=?,w_timestamp=? " + "WHERE hash_key=?;"; + + const std::string MIGRATE_INSERT_DATA_TO_MAINDB_FROM_MAINHANDLE = + "INSERT INTO sync_data VALUES(?,?,?,?,?,?,?,?);"; + const std::string MIGRATE_UPDATE_DATA_TO_MAINDB_FROM_MAINHANDLE = + "UPDATE sync_data SET key=?,value=?,timestamp=?,flag=?,device=?,ori_device=?,w_timestamp=? WHERE hash_key=?;"; + + const std::string MIGRATE_DEL_DATA_BY_VERSION_FROM_CACHEHANDLE = + "DELETE FROM sync_data WHERE version=?;"; + const std::string MIGRATE_DEL_DATA_BY_VERSION_FROM_MAINHANDLE = + "DELETE FROM cache.sync_data WHERE version=?;"; + + const std::string SELECT_MAIN_SYNC_HASH_SQL_FROM_CACHEHANDLE = "SELECT * FROM maindb.sync_data WHERE hash_key=?;"; + + const std::string REMOVE_META_VALUE_SQL = + "DELETE FROM meta_data WHERE key=?;"; + const std::string REMOVE_ATTACH_META_VALUE_SQL = + "DELETE FROM meta.meta_data WHERE key=?;"; + + const std::string CHECK_DB_INTEGRITY_SQL = "PRAGMA integrity_check;"; + + const std::string REMOVE_META_VALUE_BY_KEY_PREFIX_SQL = + "DELETE FROM meta_data WHERE key>=? AND key<=?;"; + const std::string REMOVE_ATTACH_META_VALUE_BY_KEY_PREFIX_SQL = + "DELETE FROM meta.meta_data WHERE key>=? AND key<=?;"; + + const std::string DELETE_SYNC_DATA_WITH_HASHKEY = "DELETE FROM sync_data where hash_key = ?;"; + + const std::string DELETE_SYNC_DATA_WITH_HASHKEY_FROM_CACHEHANDLE = + "DELETE FROM maindb.sync_data where hash_key = ?;"; + + const std::string GET_SYNC_DATA_TIRGGER_SQL = + "SELECT name FROM SQLITE_MASTER WHERE TYPE = 'trigger' AND TBL_NAME = 'sync_data' AND name like ?;"; + + const int BIND_KV_KEY_INDEX = 1; + const int BIND_KV_VAL_INDEX = 2; + const int BIND_LOCAL_TIMESTAMP_INDEX = 3; + const int BIND_LOCAL_HASH_KEY_INDEX = 4; + + // binding index just for the get sync data sql + const int BIND_BEGIN_STAMP_INDEX = 1; + const int BIND_END_STAMP_INDEX = 2; + + // mainDB + const int BIND_SYNC_KEY_INDEX = 1; + const int BIND_SYNC_VAL_INDEX = 2; + const int BIND_SYNC_STAMP_INDEX = 3; + const int BIND_SYNC_FLAG_INDEX = 4; + const int BIND_SYNC_DEV_INDEX = 5; + const int BIND_SYNC_ORI_DEV_INDEX = 6; + const int BIND_SYNC_HASH_KEY_INDEX = 7; + const int BIND_SYNC_W_TIME_INDEX = 8; + + const int BIND_SYNC_UPDATE_W_TIME_INDEX = 7; + const int BIND_SYNC_UPDATE_HASH_KEY_INDEX = 8; + + // cacheDB + const int BIND_CACHE_LOCAL_KEY_INDEX = 1; + const int BIND_CACHE_LOCAL_VAL_INDEX = 2; + const int BIND_CACHE_LOCAL_TIMESTAMP_INDEX = 3; + const int BIND_CACHE_LOCAL_HASH_KEY_INDEX = 4; + const int BIND_CACHE_LOCAL_FLAG_INDEX = 5; + + const int BIND_CACHE_SYNC_KEY_INDEX = 1; + const int BIND_CACHE_SYNC_VAL_INDEX = 2; + const int BIND_CACHE_SYNC_STAMP_INDEX = 3; + const int BIND_CACHE_SYNC_FLAG_INDEX = 4; + const int BIND_CACHE_SYNC_DEV_INDEX = 5; + const int BIND_CACHE_SYNC_ORI_DEV_INDEX = 6; + const int BIND_CACHE_SYNC_HASH_KEY_INDEX = 7; + const int BIND_CACHE_SYNC_W_TIME_INDEX = 8; + const int BIND_CACHE_SYNC_VERSION_INDEX = 9; + + // select result index for the item for sync database + const int SYNC_RES_KEY_INDEX = 0; + const int SYNC_RES_VAL_INDEX = 1; + const int SYNC_RES_TIME_INDEX = 2; + const int SYNC_RES_FLAG_INDEX = 3; + const int SYNC_RES_DEVICE_INDEX = 4; + const int SYNC_RES_ORI_DEV_INDEX = 5; + const int SYNC_RES_HASH_KEY_INDEX = 6; + const int SYNC_RES_W_TIME_INDEX = 7; + const int SYNC_RES_VERSION_INDEX = 8; // Available in cacheDB. + + // get kv data Response index + const int GET_KV_RES_LOCAL_TIME_INDEX = 1; + const int GET_KV_RES_SYNC_TIME_INDEX = 1; + + const int BIND_ORI_DEVICE_ID = 0; + const int BIND_PRE_DEVICE_ID = 1; + + const Key REMOVE_DEVICE_DATA_KEY = {'r', 'e', 'm', 'o', 'v', 'e'}; +} // namespace DistributedDB + +#endif // SQLITE_SINGLE_VER_STORAGE_EXECUTOR_SQL_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_subscribe.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_subscribe.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3ab1bd85d32ca8885c25e123d41ad72dc3eed2a4 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_subscribe.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_single_ver_storage_executor.h" + +#include "log_print.h" +#include "db_common.h" +#include "db_errno.h" +#include "sqlite_single_ver_storage_executor_sql.h" + +namespace DistributedDB { +using namespace TriggerMode; + +int SQLiteSingleVerStorageExecutor::CheckQueryObjectLegal(QueryObject &queryObj) const +{ + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); + if (errCode != E_OK) { + LOGE("Get query helper failed [%d]!", errCode); + return errCode; + } + + sqlite3_stmt *statement = nullptr; + errCode = helper.GetQuerySyncStatement(dbHandle_, 0, INT64_MAX, statement); // (0, INT64_MAX):max range + int ret = E_OK; + SQLiteUtils::ResetStatement(statement, true, ret); + if (ret != E_OK) { + LOGW("Failed to reset statement. error:%d", ret); + } + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::CheckMissQueryDataItem(sqlite3_stmt *stmt, const std::string &deviceName, + DataItem &item) +{ + int errCode = SQLiteUtils::StepWithRetry(stmt, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + // the value with same hashKey in DB matched the query + std::vector dev; + errCode = SQLiteUtils::GetColumnBlobValue(stmt, SYNC_RES_DEVICE_INDEX, dev); + if (errCode != E_OK) { + LOGE("Get data device info failed. %d", errCode); + return errCode; + } + auto timestamp = static_cast(sqlite3_column_int64(stmt, SYNC_RES_TIME_INDEX)); + std::string device = std::string(dev.begin(), dev.end()); + // this data item should be neglected when it's out of date of it's from same device + // otherwise, it should be erased after resolved the conflict + item.neglect = (timestamp > item.timestamp) || + (timestamp == item.timestamp && device == DBCommon::TransferHashString(deviceName)); + return E_OK; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + // the value with same hashKey in DB does not match the query, this data item should be neglected. + item.neglect = true; + return E_OK; + } + LOGE("Check sync data failed %d", errCode); + return errCode; +} + +// check the data with REMOTE_DEVICE_DATA_MISS_QUERY flag need neglect or not +int SQLiteSingleVerStorageExecutor::CheckMissQueryDataItems(sqlite3_stmt *&stmt, const SqliteQueryHelper &helper, + const DeviceInfo &deviceInfo, std::vector &dataItems) +{ + int errCode = E_OK; + for (auto &item : dataItems) { + if ((item.flag & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) != 0 && !item.key.empty()) { + errCode = helper.BindSyncDataCheckStmt(stmt, item.key); + if (errCode != E_OK) { + LOGE("Bind sync data check statement failed %d", errCode); + break; + } + errCode = CheckMissQueryDataItem(stmt, deviceInfo.deviceName, item); + if (errCode != E_OK) { + LOGE("Check miss query data item failed. %d", errCode); + return errCode; + } + SQLiteUtils::ResetStatement(stmt, false, errCode); + } + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::CheckDataWithQuery(QueryObject query, std::vector &dataItems, + const DeviceInfo &deviceInfo) +{ + int errCode = E_OK; + if (query.Empty()) { + LOGD("Query is empty, skip check."); + return E_OK; + } + SqliteQueryHelper helper = query.GetQueryHelper(errCode); + if (errCode != E_OK) { + LOGE("Get query helper failed [%d]!", errCode); + return errCode; + } + std::string sql; + errCode = helper.GetSyncDataCheckSql(sql); + if (errCode != E_OK) { + LOGE("Get sync data check sql failed"); + return errCode; + } + sqlite3_stmt *stmt = nullptr; + errCode = SQLiteUtils::GetStatement(dbHandle_, sql, stmt); + if (errCode != E_OK) { + LOGE("Get statement fail. %d", errCode); + return -E_INVALID_QUERY_FORMAT; + } + errCode = CheckMissQueryDataItems(stmt, helper, deviceInfo, dataItems); + if (errCode != E_OK) { + LOGE("check data with query failed. %d", errCode); + } + SQLiteUtils::ResetStatement(stmt, true, errCode); + return CheckCorruptedStatus(errCode); +} + +namespace { +std::string FormatSubscribeTriggerSql(const std::string &subscribeId, const std::string &subscribeCondition, + TriggerModeEnum mode) +{ + std::string triggerModeString = GetTriggerModeString(mode); + std::string accessString = ((mode == TriggerModeEnum::DELETE) ? + DBConstant::TRIGGER_REFERENCES_OLD : DBConstant::TRIGGER_REFERENCES_NEW); + std::string keyStr = DBConstant::SUBSCRIBE_QUERY_PREFIX + DBCommon::TransferHashString(subscribeId); + Key key {keyStr.begin(), keyStr.end()}; + std::string hexKeyStr = DBCommon::VectorToHexString(key); + std::string triggerName = DBConstant::SUBSCRIBE_QUERY_PREFIX + subscribeId + "_ON_" + triggerModeString; + return "CREATE TRIGGER IF NOT EXISTS " + triggerName + " AFTER " + triggerModeString + " \n" + "ON sync_data\n" + "WHEN " + subscribeCondition + "\n" + "BEGIN\n" + " SELECT " + DBConstant::UPDATE_META_FUNC + "(x'" + hexKeyStr + "', NEW.TIMESTAMP);\n" + "END;"; +} +} + +int SQLiteSingleVerStorageExecutor::AddSubscribeTrigger(QueryObject &query, const std::string &subscribeId) +{ + if (executorState_ == ExecutorState::CACHEDB || executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + LOGE("Not support add subscribe in cache db."); + return -E_NOT_SUPPORT; + } + int errCode = E_OK; + SqliteQueryHelper helper = query.GetQueryHelper(errCode); + if (errCode != E_OK) { + LOGE("Get query helper failed. %d", errCode); + return errCode; + } + // check if sqlite function is registered or not + sqlite3_stmt *stmt = nullptr; + errCode = SQLiteUtils::GetStatement(dbHandle_, "SELECT " + DBConstant::UPDATE_META_FUNC + "('K', 0);", stmt); + if (errCode != E_OK) { + LOGE("sqlite function %s has not been created.", DBConstant::UPDATE_META_FUNC.c_str()); + return -E_NOT_SUPPORT; + } + SQLiteUtils::ResetStatement(stmt, true, errCode); + + // Delete data API is actually an update operation, there is no need for DELETE trigger + for (auto mode : {TriggerModeEnum::INSERT, TriggerModeEnum::UPDATE}) { + std::string subscribeCondition; + errCode = helper.GetSubscribeSql(subscribeId, mode, subscribeCondition); + if (errCode != E_OK) { + LOGE("Get subscribe trigger create sql failed. mode: %u, errCode: %d", static_cast(mode), + errCode); + return errCode; + } + std::string sql = FormatSubscribeTriggerSql(subscribeId, subscribeCondition, mode); + errCode = SQLiteUtils::ExecuteRawSQL(dbHandle_, sql); + if (errCode != E_OK) { + LOGE("Add subscribe trigger failed. mode: %u, errCode: %d", static_cast(mode), errCode); + return errCode; + } + } + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::RemoveSubscribeTrigger(const std::vector &subscribeIds) +{ + int errCode = E_OK; + for (const auto &id : subscribeIds) { + for (auto mode : {TriggerModeEnum::INSERT, TriggerModeEnum::UPDATE}) { + const std::string trigger = DBConstant::SUBSCRIBE_QUERY_PREFIX + id + "_ON_" + GetTriggerModeString(mode); + errCode = SQLiteUtils::DropTriggerByName(dbHandle_, trigger); + if (errCode != E_OK) { + LOGE("remove subscribe trigger failed. %d", errCode); + break; + } + } + if (errCode != E_OK) { + LOGE("remove subscribe trigger for id %s failed. %d", id.c_str(), errCode); + break; + } + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::RemoveTrigger(const std::vector &triggers) +{ + int errCode = E_OK; + for (const auto &trigger : triggers) { + errCode = SQLiteUtils::DropTriggerByName(dbHandle_, trigger); + if (errCode != E_OK) { + LOGE("remove trigger failed. %d", errCode); + break; + } + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::RemoveSubscribeTriggerWaterMark(const std::vector &subscribeIds) +{ + sqlite3_stmt *statement = nullptr; + const std::string sql = attachMetaMode_ ? REMOVE_ATTACH_META_VALUE_SQL : REMOVE_META_VALUE_SQL; + int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + LOGE("Get remove trigger water mark statement failed. %d", errCode); + return errCode; + } + for (const auto &id : subscribeIds) { + errCode = SQLiteUtils::BindTextToStatement(statement, 1, DBConstant::SUBSCRIBE_QUERY_PREFIX + id); + if (errCode != E_OK) { + LOGE("Bind mark key to statement failed. %d", errCode); + break; + } + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } else { + LOGE("Remove trigger water mark failed. %d", errCode); + break; + } + SQLiteUtils::ResetStatement(statement, false, errCode); + } + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteSingleVerStorageExecutor::GetTriggers(const std::string &namePreFix, std::vector &triggerNames) +{ + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(dbHandle_, GET_SYNC_DATA_TIRGGER_SQL, stmt); + if (errCode != E_OK) { + LOGE("Get trigger query statement failed. %d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindTextToStatement(stmt, 1, namePreFix + "%"); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(stmt, true, errCode); + LOGE("Bind trigger name prefix to statement failed. %d", errCode); + return errCode; + } + + do { + errCode = SQLiteUtils::StepWithRetry(stmt, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + std::string name; + SQLiteUtils::GetColumnTextValue(stmt, 0, name); + triggerNames.emplace_back(name); + } else { + LOGE("Get trigger by name prefix failed. %d", errCode); + break; + } + } while (true); + + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_storage_engine.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_storage_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b7ed3b1fc438e5798b8a4837641efdd0e2cad3ed --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_storage_engine.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_storage_engine.h" + +#include "db_errno.h" +#include "log_print.h" +#include "sqlite_storage_executor.h" +#include "sqlite_utils.h" +#include "runtime_context.h" + +namespace DistributedDB { +SQLiteStorageEngine::SQLiteStorageEngine() +{} + +SQLiteStorageEngine::~SQLiteStorageEngine() +{} + +int SQLiteStorageEngine::InitSQLiteStorageEngine(const StorageEngineAttr &poolSize, const OpenDbProperties &option, + const std::string &identifier) +{ + if (StorageEngine::CheckEngineAttr(poolSize)) { + LOGE("Invalid storage engine attributes!"); + return -E_INVALID_ARGS; + } + engineAttr_ = poolSize; + option_ = option; + identifier_ = identifier; + if (GetEngineState() == EngineState::CACHEDB) { + LOGI("Is alive! not need to create executor, only fix option."); + return E_OK; + } + int errCode = Init(); + if (errCode != E_OK) { + LOGI("Storage engine init fail! errCode = [%d]", errCode); + } + return errCode; +} + +StorageExecutor *SQLiteStorageEngine::NewSQLiteStorageExecutor(sqlite3 *dbHandle, bool isWrite, bool isMemDb) +{ + return new (std::nothrow) SQLiteStorageExecutor(dbHandle, isWrite, isMemDb); +} + +int SQLiteStorageEngine::Upgrade(sqlite3 *db) +{ + // SQLiteSingleVerStorageEngine override this function to do table structure and even content upgrade + // SQLiteLocalStorageEngine is used by SQLiteLocalKvDB, and SQLiteLocalKvDB is used as LocalStore, CommitStorage, + // ValueSliceStorage, MetaDataStorage, all of them will not change the table structure, + // so the version of these dbFile only represent for the content version. + // SQLiteLocalStorageEngine do not override this function so content upgrade can only be done by the Storage + // who use the SQLiteLocalKvDB. + // MultiVerStorageEngine do not inherit SQLiteStorageEngine. + (void)db; + return E_OK; +} + +int SQLiteStorageEngine::CreateNewExecutor(bool isWrite, StorageExecutor *&handle) +{ + sqlite3 *dbHandle = nullptr; + int errCode = SQLiteUtils::OpenDatabase(option_, dbHandle); + if (errCode != E_OK) { + return errCode; + } + bool isMemDb = option_.isMemDb; + if (!isUpdated_) { + errCode = Upgrade(dbHandle); + if (errCode != E_OK) { + (void)sqlite3_close_v2(dbHandle); + dbHandle = nullptr; + return errCode; + } + SQLiteUtils::ExecuteCheckPoint(dbHandle); + isUpdated_ = true; + } + + handle = NewSQLiteStorageExecutor(dbHandle, isWrite, isMemDb); + if (handle == nullptr) { + LOGE("New SQLiteStorageExecutor[%d] for the pool failed.", isWrite); + (void)sqlite3_close_v2(dbHandle); + dbHandle = nullptr; + return -E_OUT_OF_MEMORY; + } + return E_OK; +} + +int SQLiteStorageEngine::ReInit() +{ + return E_OK; +} + +bool SQLiteStorageEngine::IsNeedTobeReleased() const +{ + EngineState engineState = GetEngineState(); + return ((engineState == EngineState::MAINDB) || (engineState == EngineState::INVALID)); +} + +const std::string &SQLiteStorageEngine::GetIdentifier() const +{ + return identifier_; +} + +EngineState SQLiteStorageEngine::GetEngineState() const +{ + return engineState_; +} + +void SQLiteStorageEngine::SetEngineState(EngineState state) +{ + LOGD("[SQLiteStorageEngine::SetEngineState] Engine State : [%d]", state); + engineState_ = state; // Current usage logically can guarante no concurrency +} + +const OpenDbProperties &SQLiteStorageEngine::GetOpenOption() const +{ + return option_; +} + +bool SQLiteStorageEngine::IsNeedMigrate() const +{ + return false; +} + +int SQLiteStorageEngine::ExecuteMigrate() +{ + return -E_NOT_SUPPORT; +} + +void SQLiteStorageEngine::IncreaseCacheRecordVersion() +{ + return; +} + +uint64_t SQLiteStorageEngine::GetCacheRecordVersion() const +{ + return 0; +} + +uint64_t SQLiteStorageEngine::GetAndIncreaseCacheRecordVersion() +{ + return 0; +} + +bool SQLiteStorageEngine::IsEngineCorrupted() const +{ + return false; +} + +void SQLiteStorageEngine::ClearEnginePasswd() +{ + option_.passwd.Clear(); +} + +int SQLiteStorageEngine::CheckEngineOption(const KvDBProperties &kvDBProp) const +{ + SecurityOption securityOpt; + if (RuntimeContext::GetInstance()->IsProcessSystemApiAdapterValid()) { + securityOpt.securityLabel = kvDBProp.GetSecLabel(); + securityOpt.securityFlag = kvDBProp.GetSecFlag(); + } + + int conflictReslovePolicy = kvDBProp.GetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, DEFAULT_LAST_WIN); + bool createDirByStoreIdOnly = kvDBProp.GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false); + + if (kvDBProp.GetSchemaConstRef().IsSchemaValid() == option_.schema.empty()) { + // If both true, newSchema not empty, oriEngineSchema empty, not same + // If both false, newSchema empty, oriEngineSchema not empty, not same + LOGE("Engine and kvdb schema only one not empty! kvdb schema is [%d]", option_.schema.empty()); + return -E_SCHEMA_MISMATCH; + } + // Here both schema empty or not empty, be the same + if (kvDBProp.GetSchemaConstRef().IsSchemaValid()) { + int errCode = kvDBProp.GetSchemaConstRef().CompareAgainstSchemaString(option_.schema); + if (errCode != -E_SCHEMA_EQUAL_EXACTLY) { + LOGE("Engine and kvdb schema mismatch!"); + return -E_SCHEMA_MISMATCH; + } + } + + bool isMemDb = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false); + // Here both schema empty or "not empty and equal exactly", this is ok as required + if (isMemDb == false && + option_.createDirByStoreIdOnly == createDirByStoreIdOnly && + option_.securityOpt == securityOpt && + option_.conflictReslovePolicy == conflictReslovePolicy) { + return E_OK; + } + return -E_INVALID_ARGS; +} +} diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_storage_engine.h b/mock/distributeddb/storage/src/sqlite/sqlite_storage_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..068448da680f5725a2d0bf3ff21b8e2d771f705b --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_storage_engine.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_KVDB_HANDLE_POOL_H +#define SQLITE_KVDB_HANDLE_POOL_H + +#include + +#include "macro_utils.h" +#include "storage_engine.h" +#include "sqlite_utils.h" + +namespace DistributedDB { +class SQLiteStorageEngine : public StorageEngine { +public: + SQLiteStorageEngine(); + ~SQLiteStorageEngine() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteStorageEngine); + + int InitSQLiteStorageEngine(const StorageEngineAttr &poolSize, const OpenDbProperties &option, + const std::string &identifier = std::string()); + + bool IsNeedTobeReleased() const override; + + const std::string &GetIdentifier() const override; + + EngineState GetEngineState() const override; + + int ExecuteMigrate() override; + + void SetEngineState(EngineState state) override; + + const OpenDbProperties &GetOpenOption() const; + + virtual void IncreaseCacheRecordVersion(); + virtual uint64_t GetCacheRecordVersion() const; + virtual uint64_t GetAndIncreaseCacheRecordVersion(); + + virtual bool IsEngineCorrupted() const; + + void ClearEnginePasswd() override; + + int CheckEngineOption(const KvDBProperties &kvdbOption) const override; + +protected: + + virtual int Upgrade(sqlite3 *db); + + virtual StorageExecutor *NewSQLiteStorageExecutor(sqlite3 *dbHandle, bool isWrite, bool isMemDb) = 0; + + int CreateNewExecutor(bool isWrite, StorageExecutor *&handle) override; + + virtual int ReInit(); + + bool IsNeedMigrate() const override; + + OpenDbProperties option_; +}; +} // namespace DistributedDB +#endif // SQLITE_DB_HANDLE_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_storage_executor.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_storage_executor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9991bbea8e28d1d2c6f6b10fe4ea51739dc0085d --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_storage_executor.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_storage_executor.h" + +#include "db_errno.h" +#include "log_print.h" +#include "sqlite_utils.h" + +namespace DistributedDB { +SQLiteStorageExecutor::SQLiteStorageExecutor(sqlite3 *dbHandle, bool writable, bool isMemDb) + : StorageExecutor(writable), + dbHandle_(dbHandle), + isMemDb_(isMemDb) +{} + +SQLiteStorageExecutor::~SQLiteStorageExecutor() +{ + if (dbHandle_ != nullptr) { + (void)sqlite3_close_v2(dbHandle_); + dbHandle_ = nullptr; + } +} + +int SQLiteStorageExecutor::Reset() +{ + if (dbHandle_ != nullptr) { + // Set the handle to be valid, release the heap memory as much as possible. + sqlite3_db_release_memory(dbHandle_); + return E_OK; + } + return -E_INVALID_DB; +} + +int SQLiteStorageExecutor::GetDbHandle(sqlite3 *&dbHandle) const +{ + if (dbHandle_ == nullptr) { + LOGE("Can not get dbhandle from executor!"); + return -E_NOT_FOUND; + } + dbHandle = dbHandle_; + return E_OK; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_storage_executor.h b/mock/distributeddb/storage/src/sqlite/sqlite_storage_executor.h new file mode 100644 index 0000000000000000000000000000000000000000..10abd5db26d79a3398cc4349b24ad6cb9bd05103 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_storage_executor.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_STORAGE_EXECUTOR_H +#define SQLITE_STORAGE_EXECUTOR_H + +#include "sqlite_import.h" +#include "macro_utils.h" +#include "storage_executor.h" + +namespace DistributedDB { +class SQLiteStorageExecutor : public StorageExecutor { +public: + SQLiteStorageExecutor(sqlite3 *dbHandle, bool writable, bool isMemDb); + ~SQLiteStorageExecutor() override; + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(SQLiteStorageExecutor); + + int Reset() override; + int GetDbHandle(sqlite3 *&dbHandle) const; + +protected: + sqlite3 *dbHandle_; + bool isMemDb_; +}; +} // namespace DistributedDB + +#endif // SQLITE_STORAGE_EXECUTOR_H diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_utils.cpp b/mock/distributeddb/storage/src/sqlite/sqlite_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1aef62240c3b9bd9ec0252a61b0ade4ee4fc3d87 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_utils.cpp @@ -0,0 +1,2154 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_utils.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "sqlite_import.h" +#include "securec.h" +#include "db_constant.h" +#include "db_common.h" +#include "db_errno.h" +#include "log_print.h" +#include "value_object.h" +#include "schema_utils.h" +#include "schema_constant.h" +#include "time_helper.h" +#include "platform_specific.h" + +namespace DistributedDB { + std::mutex SQLiteUtils::logMutex_; + std::string SQLiteUtils::lastErrorMsg_; +namespace { + const int BUSY_TIMEOUT_MS = 3000; // 3000ms for sqlite busy timeout. + const int BUSY_SLEEP_TIME = 50; // sleep for 50us + const int NO_SIZE_LIMIT = -1; + const int MAX_STEP_TIMES = 8000; + const int BIND_KEY_INDEX = 1; + const int BIND_VAL_INDEX = 2; + const int USING_STR_LEN = -1; + const std::string WAL_MODE_SQL = "PRAGMA journal_mode=WAL;"; + const std::string SYNC_MODE_FULL_SQL = "PRAGMA synchronous=FULL;"; + const std::string SYNC_MODE_NORMAL_SQL = "PRAGMA synchronous=NORMAL;"; + const std::string USER_VERSION_SQL = "PRAGMA user_version;"; + const std::string BEGIN_SQL = "BEGIN TRANSACTION"; + const std::string BEGIN_IMMEDIATE_SQL = "BEGIN IMMEDIATE TRANSACTION"; + const std::string COMMIT_SQL = "COMMIT TRANSACTION"; + const std::string ROLLBACK_SQL = "ROLLBACK TRANSACTION"; + const std::string JSON_EXTRACT_BY_PATH_TEST_CREATED = "SELECT json_extract_by_path('{\"field\":0}', '$.field', 0);"; + const std::string DEFAULT_ATTACH_CIPHER = "PRAGMA cipher_default_attach_cipher="; + const std::string DEFAULT_ATTACH_KDF_ITER = "PRAGMA cipher_default_attach_kdf_iter=5000"; + const std::string EXPORT_BACKUP_SQL = "SELECT export_database('backup');"; + const std::string CIPHER_CONFIG_SQL = "PRAGMA codec_cipher="; + const std::string KDF_ITER_CONFIG_SQL = "PRAGMA codec_kdf_iter=5000;"; + const std::string BACK_CIPHER_CONFIG_SQL = "PRAGMA backup.codec_cipher="; + const std::string BACK_KDF_ITER_CONFIG_SQL = "PRAGMA backup.codec_kdf_iter=5000;"; + const std::string META_CIPHER_CONFIG_SQL = "PRAGMA meta.codec_cipher="; + const std::string META_KDF_ITER_CONFIG_SQL = "PRAGMA meta.codec_kdf_iter=5000;"; + + const std::string DETACH_BACKUP_SQL = "DETACH 'backup'"; + const std::string UPDATE_META_SQL = "INSERT OR REPLACE INTO meta_data VALUES (?, ?);"; + + bool g_configLog = false; + + // statement must not be null + std::string GetColString(sqlite3_stmt *statement, int nCol) + { + std::string rowString; + for (int i = 0; i < nCol; i++) { + if (sqlite3_column_name(statement, i) != nullptr) { + rowString += sqlite3_column_name(statement, i); + } + int blankFill = (i + 1) * 16 - rowString.size(); // each column width 16 + rowString.append(static_cast((blankFill > 0) ? blankFill : 0), ' '); + } + return rowString; + } +} + +namespace TriggerMode { +const std::map TRIGGER_MODE_MAP = { + {TriggerModeEnum::NONE, ""}, + {TriggerModeEnum::INSERT, "INSERT"}, + {TriggerModeEnum::UPDATE, "UPDATE"}, + {TriggerModeEnum::DELETE, "DELETE"}, +}; + +std::string GetTriggerModeString(TriggerModeEnum mode) +{ + auto it = TRIGGER_MODE_MAP.find(mode); + return (it == TRIGGER_MODE_MAP.end()) ? "" : it->second; +} +} + +void SQLiteUtils::SqliteLogCallback(void *data, int err, const char *msg) +{ + bool verboseLog = (data != nullptr); + auto errType = static_cast(err); + errType &= 0xFF; + if (errType == 0 || errType == SQLITE_CONSTRAINT || errType == SQLITE_SCHEMA || + errType == SQLITE_NOTICE || err == SQLITE_WARNING_AUTOINDEX) { + if (verboseLog) { + LOGD("[SQLite] Error[%d] sys[%d] %s ", err, errno, sqlite3_errstr(err)); + } + } else if (errType == SQLITE_WARNING || errType == SQLITE_IOERR || + errType == SQLITE_CORRUPT || errType == SQLITE_CANTOPEN) { + LOGI("[SQLite] Error[%d], sys[%d], %s", err, errno, sqlite3_errstr(err)); + } else { + LOGE("[SQLite] Error[%d], sys[%d]", err, errno); + return; + } + + const char *errMsg = sqlite3_errstr(err); + std::lock_guard autoLock(logMutex_); + if (errMsg != nullptr) { + lastErrorMsg_ = std::string(errMsg); + } +} + +int SQLiteUtils::CreateDataBase(const OpenDbProperties &properties, sqlite3 *&dbTemp, bool setWal) +{ + uint64_t flag = SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE; + if (properties.createIfNecessary) { + flag |= SQLITE_OPEN_CREATE; + } + std::string cipherName = GetCipherName(properties.cipherType); + if (cipherName.empty()) { + LOGE("[SQLite] GetCipherName failed"); + return -E_INVALID_ARGS; + } + std::string defaultAttachCipher = DEFAULT_ATTACH_CIPHER + cipherName + ";"; + std::vector sqls {defaultAttachCipher, DEFAULT_ATTACH_KDF_ITER}; + if (setWal) { + sqls.push_back(WAL_MODE_SQL); + } + + std::string fileUrl = DBConstant::SQLITE_URL_PRE + properties.uri; + int errCode = sqlite3_open_v2(fileUrl.c_str(), &dbTemp, flag, nullptr); + if (errCode != SQLITE_OK) { + LOGE("[SQLite] open database failed: %d - sys err(%d)", errCode, errno); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto END; + } + + errCode = SetDataBaseProperty(dbTemp, properties, sqls); + if (errCode != SQLITE_OK) { + LOGE("[SQLite] SetDataBaseProperty failed: %d", errCode); + goto END; + } + +END: + if (errCode != E_OK && dbTemp != nullptr) { + (void)sqlite3_close_v2(dbTemp); + dbTemp = nullptr; + } + + return errCode; +} + +int SQLiteUtils::OpenDatabase(const OpenDbProperties &properties, sqlite3 *&db, bool setWal) +{ + { + // Only for register the sqlite3 log callback + std::lock_guard lock(logMutex_); + if (!g_configLog) { + sqlite3_config(SQLITE_CONFIG_LOG, &SqliteLogCallback, &properties.createIfNecessary); + g_configLog = true; + } + } + sqlite3 *dbTemp = nullptr; + int errCode = CreateDataBase(properties, dbTemp, setWal); + if (errCode != E_OK) { + goto END; + } + errCode = RegisterJsonFunctions(dbTemp); + if (errCode != E_OK) { + goto END; + } + // Set the synchroized mode, default for full mode. + errCode = ExecuteRawSQL(dbTemp, SYNC_MODE_FULL_SQL); + if (errCode != E_OK) { + LOGE("SQLite sync mode failed: %d", errCode); + goto END; + } + + if (!properties.isMemDb) { + errCode = SQLiteUtils::SetPersistWalMode(dbTemp); + if (errCode != E_OK) { + LOGE("SQLite set persist wall mode failed: %d", errCode); + } + } + +END: + if (errCode != E_OK && dbTemp != nullptr) { + (void)sqlite3_close_v2(dbTemp); + dbTemp = nullptr; + } + if (errCode != E_OK && errno == EKEYREVOKED) { + errCode = -E_EKEYREVOKED; + } + db = dbTemp; + return errCode; +} + +int SQLiteUtils::GetStatement(sqlite3 *db, const std::string &sql, sqlite3_stmt *&statement) +{ + if (db == nullptr) { + LOGE("Invalid db for statement"); + return -E_INVALID_DB; + } + + // Prepare the new statement only when the input parameter is not null + if (statement != nullptr) { + return E_OK; + } + + int errCode = sqlite3_prepare_v2(db, sql.c_str(), NO_SIZE_LIMIT, &statement, nullptr); + if (errCode != SQLITE_OK) { + LOGE("Prepare SQLite statement failed:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; + } + + if (statement == nullptr) { + return -E_INVALID_DB; + } + + return E_OK; +} + +int SQLiteUtils::BindTextToStatement(sqlite3_stmt *statement, int index, const std::string &str) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + // Check empty value. + if (str.empty()) { + sqlite3_bind_null(statement, index); + return E_OK; + } + + int errCode = sqlite3_bind_text(statement, index, str.c_str(), str.length(), SQLITE_TRANSIENT); + if (errCode != SQLITE_OK) { + LOGE("[SQLiteUtil][Bind text]Failed to bind the value:%d", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + return E_OK; +} + +int SQLiteUtils::BindInt64ToStatement(sqlite3_stmt *statement, int index, int64_t value) +{ + // statement check outSide + int errCode = sqlite3_bind_int64(statement, index, value); + if (errCode != SQLITE_OK) { + LOGE("[SQLiteUtil][Bind int64]Failed to bind the value:%d", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + return E_OK; +} + +int SQLiteUtils::BindBlobToStatement(sqlite3_stmt *statement, int index, const std::vector &value, + bool permEmpty) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + // Check empty value. + if (value.empty() && !permEmpty) { + LOGI("[SQLiteUtil][Bind blob]Invalid value"); + return -E_INVALID_ARGS; + } + + int errCode; + if (value.empty()) { + errCode = sqlite3_bind_zeroblob(statement, index, -1); // -1 for zero-length blob. + } else { + errCode = sqlite3_bind_blob(statement, index, static_cast(value.data()), + value.size(), SQLITE_TRANSIENT); + } + + if (errCode != SQLITE_OK) { + LOGE("[SQLiteUtil][Bind blob]Failed to bind the value:%d", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + return E_OK; +} + +void SQLiteUtils::ResetStatement(sqlite3_stmt *&statement, bool isNeedFinalize, int &errCode) +{ + if (statement == nullptr) { + return; + } + + int innerCode = SQLITE_OK; + // if need finalize the statement, just goto finalize. + if (isNeedFinalize) { + goto FINAL; + } + + // reset the statement firstly. + innerCode = sqlite3_reset(statement); + if (innerCode != SQLITE_OK) { + LOGE("[SQLiteUtils] reset statement error:%d, sys:%d", innerCode, errno); + isNeedFinalize = true; + } else { + sqlite3_clear_bindings(statement); + } + +FINAL: + if (isNeedFinalize) { + int finalizeResult = sqlite3_finalize(statement); + if (finalizeResult != SQLITE_OK) { + LOGD("[SQLiteUtils] finalize statement error:%d, sys:%d", finalizeResult, errno); + innerCode = finalizeResult; + } + statement = nullptr; + } + + if (innerCode != SQLITE_OK) { // the sqlite error code has higher priority. + errCode = SQLiteUtils::MapSQLiteErrno(innerCode); + } +} + +int SQLiteUtils::StepWithRetry(sqlite3_stmt *statement, bool isMemDb) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + int retryCount = 0; + do { + errCode = sqlite3_step(statement); + if ((errCode == SQLITE_LOCKED) && isMemDb) { + std::this_thread::sleep_for(std::chrono::microseconds(BUSY_SLEEP_TIME)); + retryCount++; + } else { + break; + } + } while (retryCount <= MAX_STEP_TIMES); + + if (errCode != SQLITE_DONE && errCode != SQLITE_ROW) { + LOGE("[SQLiteUtils] Step error:%d, sys:%d", errCode, errno); + } + + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +int SQLiteUtils::BindPrefixKey(sqlite3_stmt *statement, int index, const Key &keyPrefix) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + const size_t maxKeySize = DBConstant::MAX_KEY_SIZE; + // bind the first prefix key + int errCode = BindBlobToStatement(statement, index, keyPrefix, true); + if (errCode != SQLITE_OK) { + LOGE("Bind the prefix first error:%d", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + // bind the second prefix key + uint8_t end[maxKeySize]; + errno_t status = memset_s(end, maxKeySize, UCHAR_MAX, maxKeySize); // max byte value is 0xFF. + if (status != EOK) { + LOGE("memset error:%d", status); + return -E_SECUREC_ERROR; + } + + if (!keyPrefix.empty()) { + status = memcpy_s(end, maxKeySize, keyPrefix.data(), keyPrefix.size()); + if (status != EOK) { + LOGE("memcpy error:%d", status); + return -E_SECUREC_ERROR; + } + } + + // index wouldn't be too large, just add one to the first index. + errCode = sqlite3_bind_blob(statement, index + 1, end, maxKeySize, SQLITE_TRANSIENT); + if (errCode != SQLITE_OK) { + LOGE("Bind the prefix second error:%d", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + return E_OK; +} + +int SQLiteUtils::BeginTransaction(sqlite3 *db, TransactType type) +{ + if (type == TransactType::IMMEDIATE) { + return ExecuteRawSQL(db, BEGIN_IMMEDIATE_SQL); + } + + return ExecuteRawSQL(db, BEGIN_SQL); +} + +int SQLiteUtils::CommitTransaction(sqlite3 *db) +{ + return ExecuteRawSQL(db, COMMIT_SQL); +} + +int SQLiteUtils::RollbackTransaction(sqlite3 *db) +{ + return ExecuteRawSQL(db, ROLLBACK_SQL); +} + +int SQLiteUtils::ExecuteRawSQL(sqlite3 *db, const std::string &sql) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + char *errMsg = nullptr; + int errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK) { + LOGE("[SQLiteUtils][ExecuteSQL] failed(%d), sys(%d)", errCode, errno); + } + + if (errMsg != nullptr) { + sqlite3_free(errMsg); + errMsg = nullptr; + } + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +int SQLiteUtils::SetKey(sqlite3 *db, CipherType type, const CipherPassword &passwd, const bool &isMemDb) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + // in memory mode no need cipher + if (isMemDb) { + return E_OK; + } + + if (passwd.GetSize() != 0) { +#ifndef OMIT_ENCRYPT + int errCode = sqlite3_key(db, static_cast(passwd.GetData()), static_cast(passwd.GetSize())); + if (errCode != SQLITE_OK) { + LOGE("[SQLiteUtils][Setkey] config key failed:(%d)", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + errCode = SQLiteUtils::SetCipherSettings(db, type); + if (errCode != E_OK) { + LOGE("[SQLiteUtils][Setkey] set cipher settings failed:%d", errCode); + return errCode; + } +#else + return -E_NOT_SUPPORT; +#endif + } + + // verify key + int errCode = SQLiteUtils::ExecuteRawSQL(db, USER_VERSION_SQL); + if (errCode != E_OK) { + LOGE("[SQLiteUtils][Setkey] verify version failed:%d", errCode); + if (errno == EKEYREVOKED) { + return -E_EKEYREVOKED; + } + if (errCode == -E_BUSY) { + return errCode; + } + return -E_INVALID_PASSWD_OR_CORRUPTED_DB; + } + return E_OK; +} + +int SQLiteUtils::GetColumnBlobValue(sqlite3_stmt *statement, int index, std::vector &value) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + int keySize = sqlite3_column_bytes(statement, index); + auto keyRead = static_cast(sqlite3_column_blob(statement, index)); + if (keySize < 0) { + LOGE("[SQLiteUtils][Column blob] size:%d", keySize); + return -E_INVALID_DATA; + } else if (keySize == 0 || keyRead == nullptr) { + value.resize(0); + } else { + value.resize(keySize); + value.assign(keyRead, keyRead + keySize); + } + + return E_OK; +} + +int SQLiteUtils::GetColumnTextValue(sqlite3_stmt *statement, int index, std::string &value) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + const unsigned char *val = sqlite3_column_text(statement, index); + value = (val != nullptr) ? std::string(reinterpret_cast(val)) : std::string(); + return E_OK; +} + +int SQLiteUtils::AttachNewDatabase(sqlite3 *db, CipherType type, const CipherPassword &password, + const std::string &attachDbAbsPath, const std::string &attachAsName) +{ + // example: "ATTACH '../new.db' AS backup KEY XXXX;" + std::string attachSql = "ATTACH ? AS " + attachAsName + " KEY ?;"; // Internal interface not need verify alias name + + sqlite3_stmt* statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, attachSql, statement); + if (errCode != E_OK) { + return errCode; + } + // 1st is name. + errCode = sqlite3_bind_text(statement, 1, attachDbAbsPath.c_str(), attachDbAbsPath.length(), SQLITE_TRANSIENT); + if (errCode != SQLITE_OK) { + LOGE("Bind the attached db name failed:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto END; + } + // Passwords do not allow vector operations, so we can not use function BindBlobToStatement here. + errCode = sqlite3_bind_blob(statement, 2, static_cast(password.GetData()), // 2 means password index. + password.GetSize(), SQLITE_TRANSIENT); + if (errCode != SQLITE_OK) { + LOGE("Bind the attached key failed:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGE("Execute the SQLite attach failed:%d", errCode); + goto END; + } + errCode = SQLiteUtils::ExecuteRawSQL(db, WAL_MODE_SQL); + if (errCode != E_OK) { + LOGE("Set journal mode failed: %d", errCode); + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteUtils::CreateMetaDatabase(const std::string &metaDbPath) +{ + OpenDbProperties metaProperties {metaDbPath, true, false}; + sqlite3 *db = nullptr; + int errCode = SQLiteUtils::OpenDatabase(metaProperties, db); + if (errCode != E_OK) { + LOGE("[CreateMetaDatabase] Failed to create the meta database[%d]", errCode); + } + if (db != nullptr) { + (void)sqlite3_close_v2(db); + db = nullptr; + } + return errCode; +} + +int SQLiteUtils::CheckIntegrity(sqlite3 *db, const std::string &sql) +{ + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + LOGE("Prepare the integrity check statement error:%d", errCode); + return errCode; + } + int resultCnt = 0; + bool checkResultOK = false; + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + break; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + auto result = reinterpret_cast(sqlite3_column_text(statement, 0)); + if (result == nullptr) { + continue; + } + resultCnt = (resultCnt > 1) ? resultCnt : (resultCnt + 1); + if (strcmp(result, "ok") == 0) { + checkResultOK = true; + } + } else { + checkResultOK = false; + LOGW("Step for the integrity check failed:%d", errCode); + break; + } + } while (true); + if (resultCnt == 1 && checkResultOK) { + errCode = E_OK; + } else { + errCode = -E_INVALID_PASSWD_OR_CORRUPTED_DB; + } + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} +#ifdef RELATIONAL_STORE +namespace { // anonymous namespace for schema analysis +int AnalysisSchemaSqlAndTrigger(sqlite3 *db, const std::string &tableName, TableInfo &table) +{ + std::string sql = "select type, sql from sqlite_master where tbl_name = ?"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Prepare the analysis schema sql and trigger statement error:%d", errCode); + return errCode; + } + errCode = SQLiteUtils::BindTextToStatement(statement, 1, tableName); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Bind table name failed:%d", errCode); + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; + } + + errCode = -E_NOT_FOUND; + std::vector triggerList; + do { + int err = SQLiteUtils::StepWithRetry(statement); + if (err == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + break; + } else if (err == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = E_OK; + std::string type; + (void) SQLiteUtils::GetColumnTextValue(statement, 0, type); + if (type == "table") { + std::string createTableSql; + (void) SQLiteUtils::GetColumnTextValue(statement, 1, createTableSql); // 1 means create table sql + table.SetCreateTableSql(createTableSql); + } + } else { + LOGE("[AnalysisSchema] Step for the analysis create table sql and trigger failed:%d", err); + errCode = SQLiteUtils::MapSQLiteErrno(err); + break; + } + } while (true); + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int GetSchemaIndexList(sqlite3 *db, const std::string &tableName, std::vector &indexList) +{ + std::string sql = "pragma index_list(" + tableName + ")"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Prepare the get schema index list statement error:%d", errCode); + return errCode; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + std::string indexName; + (void) SQLiteUtils::GetColumnTextValue(statement, 1, indexName); // 1 means index name + std::string origin; + (void) SQLiteUtils::GetColumnTextValue(statement, 3, origin); // 3 means index type, whether unique + if (origin == "c") { // 'c' means index created by user declare + indexList.push_back(indexName); + } + } else { + LOGW("[AnalysisSchema] Step for the get schema index list failed:%d", errCode); + break; + } + } while (true); + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int AnalysisSchemaIndexDefine(sqlite3 *db, const std::string &indexName, CompositeFields &indexDefine) +{ + auto sql = "pragma index_info(" + indexName + ")"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Prepare the analysis schema index statement error:%d", errCode); + return errCode; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + std::string indexField; + (void) SQLiteUtils::GetColumnTextValue(statement, 2, indexField); // 2 means index's column name. + indexDefine.push_back(indexField); + } else { + LOGW("[AnalysisSchema] Step for the analysis schema index failed:%d", errCode); + break; + } + } while (true); + + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int AnalysisSchemaIndex(sqlite3 *db, const std::string &tableName, TableInfo &table) +{ + std::vector indexList; + int errCode = GetSchemaIndexList(db, tableName, indexList); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] get schema index list failed."); + return errCode; + } + + for (const auto &indexName : indexList) { + CompositeFields indexDefine; + errCode = AnalysisSchemaIndexDefine(db, indexName, indexDefine); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] analysis schema index columns failed."); + return errCode; + } + table.AddIndexDefine(indexName, indexDefine); + } + return E_OK; +} + +namespace { +bool CheckFieldName(const std::string &fieldName) +{ + auto iter = std::find_if_not(fieldName.begin(), fieldName.end(), [](char c) { + return (std::isalnum(c) || c == '_'); + }); + return iter == fieldName.end(); +} +} + +int SetFieldInfo(sqlite3_stmt *statement, TableInfo &table) +{ + FieldInfo field; + field.SetColumnId(sqlite3_column_int(statement, 0)); // 0 means column id index + + std::string tmpString; + (void) SQLiteUtils::GetColumnTextValue(statement, 1, tmpString); // 1 means column name index + if (!CheckFieldName(tmpString)) { + LOGE("[AnalysisSchema] unsupported field name."); + return -E_NOT_SUPPORT; + } + field.SetFieldName(tmpString); + + (void) SQLiteUtils::GetColumnTextValue(statement, 2, tmpString); // 2 means datatype index + field.SetDataType(tmpString); + + field.SetNotNull(static_cast(sqlite3_column_int64(statement, 3))); // 3 means whether null index + + (void) SQLiteUtils::GetColumnTextValue(statement, 4, tmpString); // 4 means default value index + if (!tmpString.empty()) { + field.SetDefaultValue(tmpString); + } + + if (sqlite3_column_int64(statement, 5) != 0) { // 5 means primary key index + if (!table.GetPrimaryKey().empty()) { + // Primary key is already set, usually because the primary key has multiple fields, not support + LOGE("[AnalysisSchema] Not support for composite primary key"); + return -E_NOT_SUPPORT; + } + table.SetPrimaryKey(field.GetFieldName()); + } + table.AddField(field); + return E_OK; +} + +int AnalysisSchemaFieldDefine(sqlite3 *db, const std::string &tableName, TableInfo &table) +{ + std::string sql = "pragma table_info(" + tableName + ")"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Prepare the analysis schema field statement error:%d", errCode); + return errCode; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = SetFieldInfo(statement, table); + if (errCode != E_OK) { + break; + } + } else { + LOGW("[AnalysisSchema] Step for the analysis schema field failed:%d", errCode); + break; + } + } while (true); + + if (table.GetPrimaryKey().empty()) { + table.SetPrimaryKey("rowid"); + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} +} // end of anonymous namespace for schema analysis + +int SQLiteUtils::AnalysisSchema(sqlite3 *db, const std::string &tableName, TableInfo &table) +{ + int errCode = AnalysisSchemaSqlAndTrigger(db, tableName, table); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Analysis sql and trigger failed. errCode = [%d]", errCode); + return errCode; + } + + errCode = AnalysisSchemaIndex(db, tableName, table); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Analysis index failed."); + return errCode; + } + + errCode = AnalysisSchemaFieldDefine(db, tableName, table); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Analysis field failed."); + return errCode; + } + + table.SetTableName(tableName); + return E_OK; +} +#endif +#ifndef OMIT_ENCRYPT +int SQLiteUtils::ExportDatabase(sqlite3 *db, CipherType type, const CipherPassword &passwd, + const std::string &newDbName) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + int errCode = AttachNewDatabase(db, type, passwd, newDbName); + if (errCode != E_OK) { + LOGE("Attach New Db fail!"); + return errCode; + } + errCode = SQLiteUtils::ExecuteRawSQL(db, EXPORT_BACKUP_SQL); + if (errCode != E_OK) { + LOGE("Execute the SQLite export failed:%d", errCode); + } + + int detachError = SQLiteUtils::ExecuteRawSQL(db, DETACH_BACKUP_SQL); + if (errCode == E_OK) { + errCode = detachError; + if (detachError != E_OK) { + LOGE("Execute the SQLite detach failed:%d", errCode); + } + } + return errCode; +} + +int SQLiteUtils::Rekey(sqlite3 *db, const CipherPassword &passwd) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + int errCode = sqlite3_rekey(db, static_cast(passwd.GetData()), static_cast(passwd.GetSize())); + if (errCode != E_OK) { + LOGE("SQLite rekey failed:(%d)", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + return E_OK; +} +#else +int SQLiteUtils::ExportDatabase(sqlite3 *db, CipherType type, const CipherPassword &passwd, + const std::string &newDbName) +{ + (void)db; + (void)type; + (void)passwd; + (void)newDbName; + return -E_NOT_SUPPORT; +} + +int SQLiteUtils::Rekey(sqlite3 *db, const CipherPassword &passwd) +{ + (void)db; + (void)passwd; + return -E_NOT_SUPPORT; +} +#endif + +int SQLiteUtils::GetVersion(const OpenDbProperties &properties, int &version) +{ + if (properties.uri.empty()) { + return -E_INVALID_ARGS; + } + + sqlite3 *dbTemp = nullptr; + // Please make sure the database file exists and is working properly + std::string fileUrl = DBConstant::SQLITE_URL_PRE + properties.uri; + int errCode = sqlite3_open_v2(fileUrl.c_str(), &dbTemp, SQLITE_OPEN_URI | SQLITE_OPEN_READONLY, nullptr); + if (errCode != SQLITE_OK) { + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + LOGE("Open database failed: %d, sys:%d", errCode, errno); + goto END; + } + + errCode = SQLiteUtils::SetKey(dbTemp, properties.cipherType, properties.passwd, properties.isMemDb); + if (errCode != E_OK) { + LOGE("Set key failed: %d", errCode); + goto END; + } + + errCode = GetVersion(dbTemp, version); + +END: + if (dbTemp != nullptr) { + (void)sqlite3_close_v2(dbTemp); + dbTemp = nullptr; + } + return errCode; +} + +int SQLiteUtils::GetVersion(sqlite3 *db, int &version) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + std::string strSql = "PRAGMA user_version;"; + sqlite3_stmt *statement = nullptr; + int errCode = sqlite3_prepare(db, strSql.c_str(), -1, &statement, nullptr); + if (errCode != SQLITE_OK || statement == nullptr) { + LOGE("[SqlUtil][GetVer] sqlite3_prepare failed."); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + return errCode; + } + + if (sqlite3_step(statement) == SQLITE_ROW) { + // Get pragma user_version at first column + version = sqlite3_column_int(statement, 0); + } else { + LOGE("[SqlUtil][GetVer] Get db user_version failed."); + errCode = SQLiteUtils::MapSQLiteErrno(SQLITE_ERROR); + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteUtils::GetJournalMode(sqlite3 *db, std::string &mode) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + std::string sql = "PRAGMA journal_mode;"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK || statement == nullptr) { + return errCode; + } + + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = SQLiteUtils::GetColumnTextValue(statement, 0, mode); + } else { + LOGE("[SqlUtil][GetJournal] Get db journal_mode failed."); + } + + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteUtils::SetUserVer(const OpenDbProperties &properties, int version) +{ + if (properties.uri.empty()) { + return -E_INVALID_ARGS; + } + + // Please make sure the database file exists and is working properly + sqlite3 *db = nullptr; + int errCode = SQLiteUtils::OpenDatabase(properties, db); + if (errCode != E_OK) { + return errCode; + } + + // Set user version + errCode = SQLiteUtils::SetUserVer(db, version); + if (errCode != E_OK) { + LOGE("Set user version fail: %d", errCode); + goto END; + } + +END: + if (db != nullptr) { + (void)sqlite3_close_v2(db); + db = nullptr; + } + + return errCode; +} + +int SQLiteUtils::SetUserVer(sqlite3 *db, int version) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + std::string userVersionSql = "PRAGMA user_version=" + std::to_string(version) + ";"; + return SQLiteUtils::ExecuteRawSQL(db, userVersionSql); +} + +int SQLiteUtils::MapSQLiteErrno(int errCode) +{ + if (errCode == SQLITE_OK) { + return E_OK; + } else if (errCode == SQLITE_IOERR) { + if (errno == EKEYREVOKED) { + return -E_EKEYREVOKED; + } + } else if (errCode == SQLITE_CORRUPT || errCode == SQLITE_NOTADB) { + return -E_INVALID_PASSWD_OR_CORRUPTED_DB; + } else if (errCode == SQLITE_LOCKED || errCode == SQLITE_BUSY) { + return -E_BUSY; + } else if (errCode == SQLITE_ERROR && errno == EKEYREVOKED) { + return -E_EKEYREVOKED; + } + return -errCode; +} + +int SQLiteUtils::SetBusyTimeout(sqlite3 *db, int timeout) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + // Set the default busy handler to retry automatically before returning SQLITE_BUSY. + int errCode = sqlite3_busy_timeout(db, timeout); + if (errCode != SQLITE_OK) { + LOGE("[SQLite] set busy timeout failed:%d", errCode); + } + + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +#ifndef OMIT_ENCRYPT +int SQLiteUtils::ExportDatabase(const std::string &srcFile, CipherType type, const CipherPassword &srcPasswd, + const std::string &targetFile, const CipherPassword &passwd) +{ + std::vector createTableSqls; + OpenDbProperties option = {srcFile, true, false, createTableSqls, type, srcPasswd}; + sqlite3 *db = nullptr; + int errCode = SQLiteUtils::OpenDatabase(option, db); + if (errCode != E_OK) { + LOGE("Open db error while exporting:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::ExportDatabase(db, type, passwd, targetFile); + if (db != nullptr) { + (void)sqlite3_close_v2(db); + db = nullptr; + } + return errCode; +} +#else +int SQLiteUtils::ExportDatabase(const std::string &srcFile, CipherType type, const CipherPassword &srcPasswd, + const std::string &targetFile, const CipherPassword &passwd) +{ + (void)srcFile; + (void)type; + (void)srcPasswd; + (void)targetFile; + (void)passwd; + return -E_NOT_SUPPORT; +} +#endif + +int SQLiteUtils::SaveSchema(const OpenDbProperties &properties) +{ + if (properties.uri.empty()) { + return -E_INVALID_ARGS; + } + + sqlite3 *db = nullptr; + int errCode = OpenDatabase(properties, db); + if (errCode != E_OK) { + return errCode; + } + + errCode = SaveSchema(db, properties.schema); + (void)sqlite3_close_v2(db); + db = nullptr; + return errCode; +} + +int SQLiteUtils::SaveSchema(sqlite3 *db, const std::string &strSchema) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + sqlite3_stmt *statement = nullptr; + std::string sql = "INSERT OR REPLACE INTO meta_data VALUES(?,?);"; + int errCode = GetStatement(db, sql, statement); + if (errCode != E_OK) { + return errCode; + } + + Key schemaKey; + DBCommon::StringToVector(DBConstant::SCHEMA_KEY, schemaKey); + errCode = BindBlobToStatement(statement, BIND_KEY_INDEX, schemaKey, false); + if (errCode != E_OK) { + ResetStatement(statement, true, errCode); + return errCode; + } + + Value schemaValue; + DBCommon::StringToVector(strSchema, schemaValue); + errCode = BindBlobToStatement(statement, BIND_VAL_INDEX, schemaValue, false); + if (errCode != E_OK) { + ResetStatement(statement, true, errCode); + return errCode; + } + + errCode = StepWithRetry(statement); // memory db does not support schema + if (errCode != MapSQLiteErrno(SQLITE_DONE)) { + LOGE("[SqlUtil][SetSchema] StepWithRetry fail, errCode=%d.", errCode); + ResetStatement(statement, true, errCode); + return errCode; + } + errCode = E_OK; + ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteUtils::GetSchema(const OpenDbProperties &properties, std::string &strSchema) +{ + sqlite3 *db = nullptr; + int errCode = OpenDatabase(properties, db); + if (errCode != E_OK) { + return errCode; + } + + int version = 0; + errCode = GetVersion(db, version); + if (version <= 0 || errCode != E_OK) { + // if version does exist, it represents database is error + (void)sqlite3_close_v2(db); + db = nullptr; + return -E_INVALID_VERSION; + } + + errCode = GetSchema(db, strSchema); + (void)sqlite3_close_v2(db); + db = nullptr; + return errCode; +} + +int SQLiteUtils::GetSchema(sqlite3 *db, std::string &strSchema) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + sqlite3_stmt *statement = nullptr; + std::string sql = "SELECT value FROM meta_data WHERE key=?;"; + int errCode = GetStatement(db, sql, statement); + if (errCode != E_OK) { + return errCode; + } + + Key schemakey; + DBCommon::StringToVector(DBConstant::SCHEMA_KEY, schemakey); + errCode = BindBlobToStatement(statement, 1, schemakey, false); + if (errCode != E_OK) { + ResetStatement(statement, true, errCode); + return errCode; + } + + errCode = StepWithRetry(statement); // memory db does not support schema + if (errCode == MapSQLiteErrno(SQLITE_DONE)) { + ResetStatement(statement, true, errCode); + return -E_NOT_FOUND; + } else if (errCode != MapSQLiteErrno(SQLITE_ROW)) { + ResetStatement(statement, true, errCode); + return errCode; + } + + Value schemaValue; + errCode = GetColumnBlobValue(statement, 0, schemaValue); + if (errCode != E_OK) { + ResetStatement(statement, true, errCode); + return errCode; + } + DBCommon::VectorToString(schemaValue, strSchema); + ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteUtils::IncreaseIndex(sqlite3 *db, const IndexName &name, const IndexInfo &info, SchemaType type, + uint32_t skipSize) +{ + if (db == nullptr) { + LOGE("[IncreaseIndex] Sqlite DB not exists."); + return -E_INVALID_DB; + } + if (name.empty()) { + LOGE("[IncreaseIndex] Name can not be empty."); + return -E_NOT_PERMIT; + } + if (info.empty()) { + LOGE("[IncreaseIndex] Info can not be empty."); + return -E_NOT_PERMIT; + } + std::string indexName = SchemaUtils::FieldPathString(name); + std::string sqlCommand = "CREATE INDEX IF NOT EXISTS '" + indexName + "' ON sync_data ("; + for (uint32_t i = 0; i < info.size(); i++) { + if (i != 0) { + sqlCommand += ", "; + } + std::string extractSql = SchemaObject::GenerateExtractSQL(type, info[i].first, info[i].second, + skipSize); + if (extractSql.empty()) { // Unlikely + LOGE("[IncreaseIndex] GenerateExtractSQL fail at field=%u.", i); + return -E_INTERNAL_ERROR; + } + sqlCommand += extractSql; + } + sqlCommand += ") WHERE (flag&0x01=0);"; + return SQLiteUtils::ExecuteRawSQL(db, sqlCommand); +} + +int SQLiteUtils::ChangeIndex(sqlite3 *db, const IndexName &name, const IndexInfo &info, SchemaType type, + uint32_t skipSize) +{ + // Currently we change index by drop it then create it, SQLite "REINDEX" may be used in the future + int errCode = DecreaseIndex(db, name); + if (errCode != OK) { + LOGE("[ChangeIndex] Decrease fail=%d.", errCode); + return errCode; + } + errCode = IncreaseIndex(db, name, info, type, skipSize); + if (errCode != OK) { + LOGE("[ChangeIndex] Increase fail=%d.", errCode); + return errCode; + } + return E_OK; +} + +int SQLiteUtils::DecreaseIndex(sqlite3 *db, const IndexName &name) +{ + if (db == nullptr) { + LOGE("[DecreaseIndex] Sqlite DB not exists."); + return -E_INVALID_DB; + } + if (name.empty()) { + LOGE("[DecreaseIndex] Name can not be empty."); + return -E_NOT_PERMIT; + } + std::string indexName = SchemaUtils::FieldPathString(name); + std::string sqlCommand = "DROP INDEX IF EXISTS '" + indexName + "';"; + return ExecuteRawSQL(db, sqlCommand); +} + +int SQLiteUtils::RegisterJsonFunctions(sqlite3 *db) +{ + if (db == nullptr) { + LOGE("Sqlite DB not exists."); + return -E_INVALID_DB; + } + int errCode = sqlite3_create_function_v2(db, "calc_hash_key", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, &CalcHashKey, nullptr, nullptr, nullptr); + if (errCode != SQLITE_OK) { + LOGE("sqlite3_create_function_v2 about calc_hash_key returned %d", errCode); + return MapSQLiteErrno(errCode); + } +#ifdef USING_DB_JSON_EXTRACT_AUTOMATICALLY + errCode = ExecuteRawSQL(db, JSON_EXTRACT_BY_PATH_TEST_CREATED); + if (errCode == E_OK) { + LOGI("json_extract_by_path already created."); + } else { + // Specify need 3 parameter in json_extract_by_path function + errCode = sqlite3_create_function_v2(db, "json_extract_by_path", 3, SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, &JsonExtractByPath, nullptr, nullptr, nullptr); + if (errCode != SQLITE_OK) { + LOGE("sqlite3_create_function_v2 about json_extract_by_path returned %d", errCode); + return MapSQLiteErrno(errCode); + } + } +#endif + return E_OK; +} + +namespace { +void SchemaObjectDestructor(SchemaObject *inObject) +{ + delete inObject; + inObject = nullptr; +} +} +#ifdef RELATIONAL_STORE +int SQLiteUtils::RegisterCalcHash(sqlite3 *db) +{ + TransactFunc func; + func.xFunc = &CalcHashKey; + return SQLiteUtils::RegisterFunction(db, "calc_hash", 1, nullptr, func); +} + +void SQLiteUtils::GetSysTime(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + if (ctx == nullptr || argc != 1 || argv == nullptr) { + LOGE("Parameter does not meet restrictions."); + return; + } + + sqlite3_result_int64(ctx, (sqlite3_int64)TimeHelper::GetSysCurrentTime()); +} + +int SQLiteUtils::RegisterGetSysTime(sqlite3 *db) +{ + TransactFunc func; + func.xFunc = &GetSysTime; + return SQLiteUtils::RegisterFunction(db, "get_sys_time", 1, nullptr, func); +} + +int SQLiteUtils::CreateRelationalMetaTable(sqlite3 *db) +{ + std::string sql = + "CREATE TABLE IF NOT EXISTS " + DBConstant::RELATIONAL_PREFIX + "metadata(" \ + "key BLOB PRIMARY KEY NOT NULL," \ + "value BLOB);"; + + int errCode = SQLiteUtils::ExecuteRawSQL(db, sql); + if (errCode != E_OK) { + LOGE("[SQLite] execute create table sql failed"); + return errCode; + } + return E_OK; +} + +int SQLiteUtils::CreateRelationalLogTable(sqlite3 *db, const std::string &oriTableName) +{ + const std::string tableName = DBConstant::RELATIONAL_PREFIX + oriTableName + "_log"; + std::string sql = + "CREATE TABLE IF NOT EXISTS " + tableName + "(" \ + "data_key INT NOT NULL," \ + "device BLOB," \ + "ori_device BLOB," \ + "timestamp INT NOT NULL," \ + "wtimestamp INT NOT NULL," \ + "flag INT NOT NULL," \ + "hash_key BLOB NOT NULL," + "PRIMARY KEY(device,hash_key));" + "CREATE INDEX IF NOT EXISTS " + DBConstant::RELATIONAL_PREFIX + "time_flag_index ON " + tableName + + "(timestamp, flag);" + "CREATE INDEX IF NOT EXISTS " + DBConstant::RELATIONAL_PREFIX + "hashkey_index ON " + tableName + "(hash_key);"; + + int errCode = SQLiteUtils::ExecuteRawSQL(db, sql); + if (errCode != E_OK) { + LOGE("[SQLite] execute create table sql failed"); + } + return errCode; +} + +namespace { +std::string GetInsertTrigger(const TableInfo &table) +{ + std::string logTblName = DBConstant::RELATIONAL_PREFIX + table.GetTableName() + "_log"; + std::string insertTrigger = "CREATE TRIGGER IF NOT EXISTS "; + insertTrigger += "naturalbase_rdb_" + table.GetTableName() + "_ON_INSERT AFTER INSERT \n"; + insertTrigger += "ON " + table.GetTableName() + "\n"; + insertTrigger += "BEGIN\n"; + insertTrigger += "\t INSERT OR REPLACE INTO " + logTblName; + insertTrigger += " (data_key, device, ori_device, timestamp, wtimestamp, flag, hash_key)"; + insertTrigger += " VALUES (new.rowid, '', '',"; + insertTrigger += " get_sys_time(0), get_sys_time(0),"; + insertTrigger += " CASE WHEN (SELECT count(*)<>0 FROM " + logTblName + " WHERE hash_key=calc_hash(new." + + table.GetPrimaryKey() + ") AND flag&0x02=0x02) THEN 0x22 ELSE 0x02 END,"; + insertTrigger += " calc_hash(new." + table.GetPrimaryKey() + "));\n"; + insertTrigger += "END;"; + return insertTrigger; +} + +std::string GetUpdateTrigger(const TableInfo &table) +{ + std::string updateTrigger = "CREATE TRIGGER IF NOT EXISTS "; + updateTrigger += "naturalbase_rdb_" + table.GetTableName() + "_ON_UPDATE AFTER UPDATE \n"; + updateTrigger += "ON " + table.GetTableName() + "\n"; + updateTrigger += "BEGIN\n"; + updateTrigger += "\t UPDATE " + DBConstant::RELATIONAL_PREFIX + table.GetTableName() + "_log"; + updateTrigger += " SET timestamp=get_sys_time(0), device='', flag=0x22"; + updateTrigger += " where hash_key=calc_hash(old." + table.GetPrimaryKey() + ") and flag&0x02=0x02;\n"; + updateTrigger += "END;"; + return updateTrigger; +} + +std::string GetDeleteTrigger(const TableInfo &table) +{ + std::string deleteTrigger = "CREATE TRIGGER IF NOT EXISTS "; + deleteTrigger += "naturalbase_rdb_" + table.GetTableName() + "_ON_DELETE BEFORE DELETE \n"; + deleteTrigger += "ON " + table.GetTableName() + "\n"; + deleteTrigger += "BEGIN\n"; + deleteTrigger += "\t UPDATE " + DBConstant::RELATIONAL_PREFIX + table.GetTableName() + "_log"; + deleteTrigger += " SET flag=0x03,timestamp=get_sys_time(0)"; + deleteTrigger += " where hash_key=calc_hash(old." + table.GetPrimaryKey() + ") and flag&0x02=0x02;\n"; + deleteTrigger += "END;"; + return deleteTrigger; +} +} + +int SQLiteUtils::AddRelationalLogTableTrigger(sqlite3 *db, const TableInfo &table) +{ + std::vector sqls = {GetInsertTrigger(table), GetUpdateTrigger(table), GetDeleteTrigger(table)}; + // add insert,update,delete trigger + for (const auto &sql : sqls) { + int errCode = SQLiteUtils::ExecuteRawSQL(db, sql); + if (errCode != E_OK) { + LOGE("[SQLite] execute create log trigger sql failed"); + return errCode; + } + } + return E_OK; +} + +int SQLiteUtils::CreateSameStuTable(sqlite3 *db, const TableInfo &baseTbl, const std::string &newTableName) +{ + std::string sql = "CREATE TABLE IF NOT EXISTS " + newTableName + "("; + const std::map &fields = baseTbl.GetFields(); + for (uint32_t cid = 0; cid < fields.size(); ++cid) { + std::string fieldName = baseTbl.GetFieldName(cid); + sql += fieldName + " " + fields.at(fieldName).GetDataType(); + if (fields.at(fieldName).IsNotNull()) { + sql += " NOT NULL"; + } + if (fields.at(fieldName).HasDefaultValue()) { + sql += " DEFAULT " + fields.at(fieldName).GetDefaultValue(); + } + if (fieldName == baseTbl.GetPrimaryKey()) { + sql += " PRIMARY KEY"; + } + sql += ","; + } + sql.pop_back(); + sql += ");"; + int errCode = SQLiteUtils::ExecuteRawSQL(db, sql); + if (errCode != E_OK) { + LOGE("[SQLite] execute create table sql failed"); + } + return errCode; +} + +int SQLiteUtils::CloneIndexes(sqlite3 *db, const std::string &oriTableName, const std::string &newTableName) +{ + std::string sql = + "SELECT 'CREATE ' || CASE WHEN il.'unique' THEN 'UNIQUE ' ELSE '' END || 'INDEX IF NOT EXISTS ' || '" + + newTableName + "_' || il.name || ' ON ' || '" + newTableName + + "' || '(' || GROUP_CONCAT(ii.name) || ');' " + "FROM sqlite_master AS m," + "pragma_index_list(m.name) AS il," + "pragma_index_info(il.name) AS ii " + "WHERE m.type='table' AND m.name='" + oriTableName + "' AND il.origin='c' " + "GROUP BY il.name;"; + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + LOGE("Prepare the clone sql failed:%d", errCode); + return errCode; + } + + sql.clear(); + while (true) { + errCode = SQLiteUtils::StepWithRetry(stmt, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + std::string indexSql; + (void)GetColumnTextValue(stmt, 0, indexSql); + sql += indexSql; + continue; + } + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + (void)ResetStatement(stmt, true, errCode); + break; + } + + if (errCode != E_OK) { + return errCode; + } + errCode = SQLiteUtils::ExecuteRawSQL(db, sql); + if (errCode != E_OK) { + LOGE("[SQLite] execute create table sql failed"); + } + return errCode; +} + +int SQLiteUtils::RegisterFunction(sqlite3 *db, const std::string &funcName, int nArg, void *uData, TransactFunc &func) +{ + if (db == nullptr) { + LOGE("Sqlite DB not exists."); + return -E_INVALID_DB; + } + + int errCode = sqlite3_create_function_v2(db, funcName.c_str(), nArg, SQLITE_UTF8 | SQLITE_DETERMINISTIC, uData, + func.xFunc, func.xStep, func.xFinal, func.xDestroy); + if (errCode != SQLITE_OK) { + LOGE("sqlite3_create_function_v2 about [%s] returned %d", funcName.c_str(), errCode); + return MapSQLiteErrno(errCode); + } + return E_OK; +} +#endif +int SQLiteUtils::RegisterFlatBufferFunction(sqlite3 *db, const std::string &inSchema) +{ + if (db == nullptr) { + LOGE("Sqlite DB not exists."); + return -E_INVALID_DB; + } + auto heapSchemaObj = new (std::nothrow) SchemaObject; + if (heapSchemaObj == nullptr) { + return -E_OUT_OF_MEMORY; + } + int errCode = heapSchemaObj->ParseFromSchemaString(inSchema); + if (errCode != E_OK) { // Unlikely, it has been parsed before + delete heapSchemaObj; + heapSchemaObj = nullptr; + return -E_INTERNAL_ERROR; + } + if (heapSchemaObj->GetSchemaType() != SchemaType::FLATBUFFER) { // Do not need to register FlatBufferExtract + delete heapSchemaObj; + heapSchemaObj = nullptr; + return E_OK; + } + errCode = sqlite3_create_function_v2(db, SchemaObject::GetExtractFuncName(SchemaType::FLATBUFFER).c_str(), + 3, SQLITE_UTF8 | SQLITE_DETERMINISTIC, heapSchemaObj, &FlatBufferExtractByPath, nullptr, nullptr, // 3 args + reinterpret_cast(SchemaObjectDestructor)); + // About the release of heapSchemaObj: SQLite guarantee that at following case, sqlite will invoke the destructor + // (that is SchemaObjectDestructor) we passed to it. See sqlite.org for more information. + // The destructor is invoked when the function is deleted, either by being overloaded or when the database + // connection closes. The destructor is also invoked if the call to sqlite3_create_function_v2() fails + if (errCode != SQLITE_OK) { + LOGE("sqlite3_create_function_v2 about flatbuffer_extract_by_path return=%d.", errCode); + // As mentioned above, SQLite had invoked the SchemaObjectDestructor to release the heapSchemaObj + return MapSQLiteErrno(errCode); + } + return E_OK; +} + +void SQLiteUtils::UpdateMetaDataWithinTrigger(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + if (ctx == nullptr || argc != 2 || argv == nullptr) { // 2 : Number of parameters for sqlite register function + LOGE("[UpdateMetaDataWithinTrigger] Invalid parameter, argc=%d.", argc); + return; + } + auto *handle = static_cast(sqlite3_user_data(ctx)); + if (handle == nullptr) { + sqlite3_result_error(ctx, "Sqlite context is invalid.", USING_STR_LEN); + LOGE("Sqlite context is invalid."); + return; + } + auto *keyPtr = static_cast(sqlite3_value_blob(argv[0])); // 0 : first argv for key + int keyLen = sqlite3_value_bytes(argv[0]); // 0 : first argv for key + if (keyPtr == nullptr || keyLen <= 0 || keyLen > static_cast(DBConstant::MAX_KEY_SIZE)) { + sqlite3_result_error(ctx, "key is invalid.", USING_STR_LEN); + LOGE("key is invalid."); + return; + } + auto val = sqlite3_value_int64(argv[1]); // 1 : second argv for value + + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(handle, UPDATE_META_SQL, stmt); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "Get update meta_data statement failed.", USING_STR_LEN); + LOGE("Get update meta_data statement failed. %d", errCode); + return; + } + + Key key(keyPtr, keyPtr + keyLen); + errCode = SQLiteUtils::BindBlobToStatement(stmt, BIND_KEY_INDEX, key, false); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "Bind key to statement failed.", USING_STR_LEN); + LOGE("Bind key to statement failed. %d", errCode); + goto END; + } + + errCode = SQLiteUtils::BindInt64ToStatement(stmt, BIND_VAL_INDEX, val); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "Bind value to statement failed.", USING_STR_LEN); + LOGE("Bind value to statement failed. %d", errCode); + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(stmt, false); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + sqlite3_result_error(ctx, "Execute the update meta_data attach failed.", USING_STR_LEN); + LOGE("Execute the update meta_data attach failed. %d", errCode); + } +END: + SQLiteUtils::ResetStatement(stmt, true, errCode); +} + +int SQLiteUtils::RegisterMetaDataUpdateFunction(sqlite3 *db) +{ + int errCode = sqlite3_create_function_v2(db, DBConstant::UPDATE_META_FUNC.c_str(), + 2, // 2: argc for register function + SQLITE_UTF8 | SQLITE_DETERMINISTIC, db, &SQLiteUtils::UpdateMetaDataWithinTrigger, nullptr, nullptr, nullptr); + if (errCode != SQLITE_OK) { + LOGE("sqlite3_create_function_v2 about %s returned %d", DBConstant::UPDATE_META_FUNC.c_str(), errCode); + } + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +struct ValueParseCache { + ValueObject valueParsed; + std::vector valueOriginal; +}; + +namespace { +inline bool IsDeleteRecord(const uint8_t *valueBlob, int valueBlobLen) +{ + return (valueBlob == nullptr) || (valueBlobLen <= 0); // In fact, sqlite guarantee valueBlobLen not negative +} + +// Use the same cache id as sqlite use for json_extract which is substituted by our json_extract_by_path +// A negative cache-id enables sharing of cache between different operation during the same statement +constexpr int VALUE_CACHE_ID = -429938; + +void ValueParseCacheFree(ValueParseCache *inCache) +{ + delete inCache; + inCache = nullptr; +} + +// We don't use cache array since we only cache value column of sqlite table, see sqlite implementation for compare. +const ValueObject *ParseValueThenCacheOrGetFromCache(sqlite3_context *ctx, const uint8_t *valueBlob, + uint32_t valueBlobLen, uint32_t offset) +{ + // Note: All parameter had already been check inside JsonExtractByPath, only called by JsonExtractByPath + auto cached = static_cast(sqlite3_get_auxdata(ctx, VALUE_CACHE_ID)); + if (cached != nullptr) { // A previous cache exist + if (cached->valueOriginal.size() == valueBlobLen) { + if (std::memcmp(cached->valueOriginal.data(), valueBlob, valueBlobLen) == 0) { + // Cache match + return &(cached->valueParsed); + } + } + } + // No cache or cache mismatch + auto newCache = new (std::nothrow) ValueParseCache; + if (newCache == nullptr) { + sqlite3_result_error(ctx, "[ParseValueCache] OOM.", USING_STR_LEN); + LOGE("[ParseValueCache] OOM."); + return nullptr; + } + int errCode = newCache->valueParsed.Parse(valueBlob, valueBlob + valueBlobLen, offset); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "[ParseValueCache] Parse fail.", USING_STR_LEN); + LOGE("[ParseValueCache] Parse fail, errCode=%d.", errCode); + delete newCache; + newCache = nullptr; + return nullptr; + } + newCache->valueOriginal.assign(valueBlob, valueBlob + valueBlobLen); + sqlite3_set_auxdata(ctx, VALUE_CACHE_ID, newCache, reinterpret_cast(ValueParseCacheFree)); + // If sqlite3_set_auxdata fail, it will immediately call ValueParseCacheFree to delete newCache; + // Next time sqlite3_set_auxdata will call ValueParseCacheFree to delete newCache of this time; + // At the end, newCache will be eventually deleted when call sqlite3_reset or sqlite3_finalize; + // Since sqlite3_set_auxdata may fail, we have to call sqlite3_get_auxdata other than return newCache directly. + auto cacheInAuxdata = static_cast(sqlite3_get_auxdata(ctx, VALUE_CACHE_ID)); + if (cacheInAuxdata == nullptr) { + return nullptr; + } + return &(cacheInAuxdata->valueParsed); +} +} + +void SQLiteUtils::JsonExtractByPath(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + if (ctx == nullptr || argc != 3 || argv == nullptr) { // 3 parameters, which are value, path and offset + LOGE("[JsonExtract] Invalid parameter, argc=%d.", argc); + return; + } + auto valueBlob = static_cast(sqlite3_value_blob(argv[0])); + int valueBlobLen = sqlite3_value_bytes(argv[0]); + if (IsDeleteRecord(valueBlob, valueBlobLen)) { + // Currently delete records are filtered out of query and create-index sql, so not allowed here. + sqlite3_result_error(ctx, "[JsonExtract] Delete record not allowed.", USING_STR_LEN); + LOGE("[JsonExtract] Delete record not allowed."); + return; + } + auto path = reinterpret_cast(sqlite3_value_text(argv[1])); + int offset = sqlite3_value_int(argv[2]); // index 2 is the third parameter + if ((path == nullptr) || (offset < 0)) { + sqlite3_result_error(ctx, "[JsonExtract] Path nullptr or offset invalid.", USING_STR_LEN); + LOGE("[JsonExtract] Path nullptr or offset=%d invalid.", offset); + return; + } + FieldPath outPath; + int errCode = SchemaUtils::ParseAndCheckFieldPath(path, outPath); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "[JsonExtract] Path illegal.", USING_STR_LEN); + LOGE("[JsonExtract] Path=%s illegal.", path); + return; + } + // Parameter Check Done Here + const ValueObject *valueObj = ParseValueThenCacheOrGetFromCache(ctx, valueBlob, static_cast(valueBlobLen), + static_cast(offset)); + if (valueObj == nullptr) { + return; // Necessary had been printed in ParseValueThenCacheOrGetFromCache + } + JsonExtractInnerFunc(ctx, *valueObj, outPath); +} + +namespace { +inline bool IsExtractableType(FieldType inType) +{ + return (inType != FieldType::LEAF_FIELD_NULL && inType != FieldType::LEAF_FIELD_ARRAY && + inType != FieldType::LEAF_FIELD_OBJECT && inType != FieldType::INTERNAL_FIELD_OBJECT); +} +} + +void SQLiteUtils::JsonExtractInnerFunc(sqlite3_context *ctx, const ValueObject &inValue, const FieldPath &inPath) +{ + FieldType outType = FieldType::LEAF_FIELD_NULL; // Default type null for invalid-path(path not exist) + int errCode = inValue.GetFieldTypeByFieldPath(inPath, outType); + if (errCode != E_OK && errCode != -E_INVALID_PATH) { + sqlite3_result_error(ctx, "[JsonExtract] GetFieldType fail.", USING_STR_LEN); + LOGE("[JsonExtract] GetFieldType fail, errCode=%d.", errCode); + return; + } + FieldValue outValue; + if (IsExtractableType(outType)) { + errCode = inValue.GetFieldValueByFieldPath(inPath, outValue); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "[JsonExtract] GetFieldValue fail.", USING_STR_LEN); + LOGE("[JsonExtract] GetFieldValue fail, errCode=%d.", errCode); + return; + } + } + // FieldType null, array, object do not have value, all these FieldValue will be regarded as null in JsonReturn. + ExtractReturn(ctx, outType, outValue); +} + +// NOTE!!! This function is performance sensitive !!! Carefully not to allocate memory often!!! +void SQLiteUtils::FlatBufferExtractByPath(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + if (ctx == nullptr || argc != 3 || argv == nullptr) { // 3 parameters, which are value, path and offset + LOGE("[FlatBufferExtract] Invalid parameter, argc=%d.", argc); + return; + } + auto schema = static_cast(sqlite3_user_data(ctx)); + if (schema == nullptr || !schema->IsSchemaValid() || (schema->GetSchemaType() != SchemaType::FLATBUFFER)) { + sqlite3_result_error(ctx, "[FlatBufferExtract] No SchemaObject or invalid.", USING_STR_LEN); + LOGE("[FlatBufferExtract] No SchemaObject or invalid."); + return; + } + // Get information from argv + auto valueBlob = static_cast(sqlite3_value_blob(argv[0])); + int valueBlobLen = sqlite3_value_bytes(argv[0]); + if (IsDeleteRecord(valueBlob, valueBlobLen)) { + // Currently delete records are filtered out of query and create-index sql, so not allowed here. + sqlite3_result_error(ctx, "[FlatBufferExtract] Delete record not allowed.", USING_STR_LEN); + LOGE("[FlatBufferExtract] Delete record not allowed."); + return; + } + auto path = reinterpret_cast(sqlite3_value_text(argv[1])); + int offset = sqlite3_value_int(argv[2]); // index 2 is the third parameter + if ((path == nullptr) || (offset < 0) || (static_cast(offset) != schema->GetSkipSize())) { + sqlite3_result_error(ctx, "[FlatBufferExtract] Path null or offset invalid.", USING_STR_LEN); + LOGE("[FlatBufferExtract] Path null or offset=%d(skipsize=%u) invalid.", offset, schema->GetSkipSize()); + return; + } + FlatBufferExtractInnerFunc(ctx, *schema, RawValue{valueBlob, valueBlobLen}, path); +} + +namespace { +constexpr uint32_t FLATBUFFER_MAX_CACHE_SIZE = 102400; // 100 KBytes + +void FlatBufferCacheFree(std::vector *inCache) +{ + delete inCache; + inCache = nullptr; +} +} + +void SQLiteUtils::FlatBufferExtractInnerFunc(sqlite3_context *ctx, const SchemaObject &schema, const RawValue &inValue, + RawString inPath) +{ + // All parameter had already been check inside FlatBufferExtractByPath, only called by FlatBufferExtractByPath + if (schema.GetSkipSize() % SchemaConstant::SECURE_BYTE_ALIGN == 0) { + TypeValue outExtract; + int errCode = schema.ExtractValue(ValueSource::FROM_DBFILE, inPath, inValue, outExtract, nullptr); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "[FlatBufferExtract] ExtractValue fail.", USING_STR_LEN); + LOGE("[FlatBufferExtract] ExtractValue fail, errCode=%d.", errCode); + return; + } + ExtractReturn(ctx, outExtract.first, outExtract.second); + return; + } + // Not byte-align secure, we have to make a cache for copy. Check whether cache had already exist. + auto cached = static_cast *>(sqlite3_get_auxdata(ctx, VALUE_CACHE_ID)); // Share the same id + if (cached == nullptr) { + // Make the cache + auto newCache = new (std::nothrow) std::vector; + if (newCache == nullptr) { + sqlite3_result_error(ctx, "[FlatBufferExtract] OOM.", USING_STR_LEN); + LOGE("[FlatBufferExtract] OOM."); + return; + } + newCache->resize(FLATBUFFER_MAX_CACHE_SIZE); + sqlite3_set_auxdata(ctx, VALUE_CACHE_ID, newCache, reinterpret_cast(FlatBufferCacheFree)); + // If sqlite3_set_auxdata fail, it will immediately call FlatBufferCacheFree to delete newCache; + // Next time sqlite3_set_auxdata will call FlatBufferCacheFree to delete newCache of this time; + // At the end, newCache will be eventually deleted when call sqlite3_reset or sqlite3_finalize; + // Since sqlite3_set_auxdata may fail, we have to call sqlite3_get_auxdata other than return newCache directly. + // See sqlite.org for more information. + cached = static_cast *>(sqlite3_get_auxdata(ctx, VALUE_CACHE_ID)); + } + if (cached == nullptr) { + LOGW("[FlatBufferExtract] Something wrong with Auxdata, but it is no matter without cache."); + } + TypeValue outExtract; + int errCode = schema.ExtractValue(ValueSource::FROM_DBFILE, inPath, inValue, outExtract, cached); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "[FlatBufferExtract] ExtractValue fail.", USING_STR_LEN); + LOGE("[FlatBufferExtract] ExtractValue fail, errCode=%d.", errCode); + return; + } + ExtractReturn(ctx, outExtract.first, outExtract.second); +} + +void SQLiteUtils::ExtractReturn(sqlite3_context *ctx, FieldType type, const FieldValue &value) +{ + if (ctx == nullptr) { + return; + } + switch (type) { + case FieldType::LEAF_FIELD_BOOL: + sqlite3_result_int(ctx, (value.boolValue ? 1 : 0)); + break; + case FieldType::LEAF_FIELD_INTEGER: + sqlite3_result_int(ctx, value.integerValue); + break; + case FieldType::LEAF_FIELD_LONG: + sqlite3_result_int64(ctx, value.longValue); + break; + case FieldType::LEAF_FIELD_DOUBLE: + sqlite3_result_double(ctx, value.doubleValue); + break; + case FieldType::LEAF_FIELD_STRING: + // The SQLITE_TRANSIENT value means that the content will likely change in the near future and + // that SQLite should make its own private copy of the content before returning. + sqlite3_result_text(ctx, value.stringValue.c_str(), -1, SQLITE_TRANSIENT); // -1 mean use the string length + break; + default: + // All other type regard as null + sqlite3_result_null(ctx); + } + return; +} + +void SQLiteUtils::CalcHashKey(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + // 1 means that the function only needs one parameter, namely key + if (ctx == nullptr || argc != 1 || argv == nullptr) { + LOGE("Parameter does not meet restrictions."); + return; + } + auto keyBlob = static_cast(sqlite3_value_blob(argv[0])); + if (keyBlob == nullptr) { + sqlite3_result_error(ctx, "Parameters is invalid.", USING_STR_LEN); + LOGE("Parameters is invalid."); + return; + } + int blobLen = sqlite3_value_bytes(argv[0]); + std::vector value(keyBlob, keyBlob + blobLen); + std::vector hashValue; + int errCode = DBCommon::CalcValueHash(value, hashValue); + if (errCode != E_OK) { + sqlite3_result_error(ctx, "Get hash value error.", USING_STR_LEN); + LOGE("Get hash value error."); + return; + } + sqlite3_result_blob(ctx, hashValue.data(), hashValue.size(), SQLITE_TRANSIENT); + return; +} + +int SQLiteUtils::GetDbSize(const std::string &dir, const std::string &dbName, uint64_t &size) +{ + std::string dataDir = dir + "/" + dbName + DBConstant::SQLITE_DB_EXTENSION; + uint64_t localDbSize = 0; + int errCode = OS::CalFileSize(dataDir, localDbSize); + if (errCode != E_OK) { + LOGD("Failed to get the db file size, errCode:%d", errCode); + return errCode; + } + + std::string shmFileName = dataDir + "-shm"; + uint64_t localshmFileSize = 0; + errCode = OS::CalFileSize(shmFileName, localshmFileSize); + if (errCode != E_OK) { + localshmFileSize = 0; + } + + std::string walFileName = dataDir + "-wal"; + uint64_t localWalFileSize = 0; + errCode = OS::CalFileSize(walFileName, localWalFileSize); + if (errCode != E_OK) { + localWalFileSize = 0; + } + + // 64-bit system is Suffice. Computer storage is less than uint64_t max + size += (localDbSize + localshmFileSize + localWalFileSize); + return E_OK; +} + +int SQLiteUtils::ExplainPlan(sqlite3 *db, const std::string &execSql, bool isQueryPlan) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + sqlite3_stmt *statement = nullptr; + std::string explainSql = (isQueryPlan ? "explain query plan " : "explain ") + execSql; + int errCode = GetStatement(db, explainSql, statement); + if (errCode != E_OK) { + return errCode; + } + + bool isFirst = true; + errCode = StepWithRetry(statement); // memory db does not support schema + while (errCode == MapSQLiteErrno(SQLITE_ROW)) { + int nCol = sqlite3_column_count(statement); + nCol = std::min(nCol, 8); // Read 8 column at most + + if (isFirst) { + LOGD("#### %s", GetColString(statement, nCol).c_str()); + isFirst = false; + } + + std::string rowString; + for (int i = 0; i < nCol; i++) { + if (sqlite3_column_text(statement, i) != nullptr) { + rowString += reinterpret_cast(sqlite3_column_text(statement, i)); + } + int blankFill = (i + 1) * 16 - rowString.size(); // each column width 16 + rowString.append(static_cast((blankFill > 0) ? blankFill : 0), ' '); + } + LOGD("#### %s", rowString.c_str()); + errCode = StepWithRetry(statement); + } + if (errCode != MapSQLiteErrno(SQLITE_DONE)) { + LOGE("[SqlUtil][Explain] StepWithRetry fail, errCode=%d.", errCode); + ResetStatement(statement, true, errCode); + return errCode; + } + errCode = E_OK; + ResetStatement(statement, true, errCode); + return errCode; +} + +int SQLiteUtils::SetDataBaseProperty(sqlite3 *db, const OpenDbProperties &properties, + const std::vector &sqls) +{ + // Set the default busy handler to retry automatically before returning SQLITE_BUSY. + int errCode = SetBusyTimeout(db, BUSY_TIMEOUT_MS); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::SetKey(db, properties.cipherType, properties.passwd, properties.isMemDb); + if (errCode != E_OK) { + LOGD("SQLiteUtils::SetKey fail!!![%d]", errCode); + return errCode; + } + + for (const auto &sql : sqls) { + errCode = SQLiteUtils::ExecuteRawSQL(db, sql); + if (errCode != E_OK) { + LOGE("[SQLite] execute sql failed: %d", errCode); + return errCode; + } + } + // Create table if not exist according the sqls. + if (properties.createIfNecessary) { + for (const auto &sql : properties.sqls) { + errCode = SQLiteUtils::ExecuteRawSQL(db, sql); + if (errCode != E_OK) { + LOGE("[SQLite] execute preset sqls failed"); + return errCode; + } + } + } + return E_OK; +} + +#ifndef OMIT_ENCRYPT +int SQLiteUtils::SetCipherSettings(sqlite3 *db, CipherType type) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + std::string cipherName = GetCipherName(type); + if (cipherName.empty()) { + return -E_INVALID_ARGS; + } + std::string cipherConfig = CIPHER_CONFIG_SQL + cipherName + ";"; + int errCode = SQLiteUtils::ExecuteRawSQL(db, cipherConfig); + if (errCode != E_OK) { + LOGE("[SQLiteUtils][SetCipherSettings] config cipher failed:%d", errCode); + return errCode; + } + errCode = SQLiteUtils::ExecuteRawSQL(db, KDF_ITER_CONFIG_SQL); + if (errCode != E_OK) { + LOGE("[SQLiteUtils][SetCipherSettings] config iter failed:%d", errCode); + return errCode; + } + return errCode; +} + +std::string SQLiteUtils::GetCipherName(CipherType type) +{ + if (type == CipherType::AES_256_GCM || type == CipherType::DEFAULT) { + return "'aes-256-gcm'"; + } + return ""; +} +#endif + +int SQLiteUtils::DropTriggerByName(sqlite3 *db, const std::string &name) +{ + const std::string dropTriggerSql = "DROP TRIGGER " + name + ";"; + int errCode = SQLiteUtils::ExecuteRawSQL(db, dropTriggerSql); + if (errCode != E_OK) { + LOGE("Remove trigger failed. %d", errCode); + } + return errCode; +} + +int SQLiteUtils::ExpandedSql(sqlite3_stmt *stmt, std::string &basicString) +{ + if (stmt == nullptr) { + return -E_INVALID_ARGS; + } + char *eSql = sqlite3_expanded_sql(stmt); + if (eSql == nullptr) { + LOGE("expand statement to sql failed."); + return -E_INVALID_DATA; + } + basicString = std::string(eSql); + sqlite3_free(eSql); + return E_OK; +} + +void SQLiteUtils::ExecuteCheckPoint(sqlite3 *db) +{ + if (db == nullptr) { + return; + } + + int chkResult = sqlite3_wal_checkpoint_v2(db, nullptr, SQLITE_CHECKPOINT_TRUNCATE, nullptr, nullptr); + LOGI("SQLite checkpoint result:%d", chkResult); +} + +int SQLiteUtils::CheckTableEmpty(sqlite3 *db, const std::string &tableName, bool &isEmpty) +{ + if (db == nullptr) { + return -E_INVALID_ARGS; + } + + std::string cntSql = "SELECT min(rowid) FROM " + tableName + ";"; + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, cntSql, stmt); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::StepWithRetry(stmt, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) { + isEmpty = true; + } else { + isEmpty = false; + } + errCode = E_OK; + } + + SQLiteUtils::ResetStatement(stmt, true, errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +int SQLiteUtils::SetPersistWalMode(sqlite3 *db) +{ + if (db == nullptr) { + return -E_INVALID_ARGS; + } + int opCode = 1; + int errCode = sqlite3_file_control(db, "main", SQLITE_FCNTL_PERSIST_WAL, &opCode); + if (errCode != SQLITE_OK) { + LOGE("Set persist wal mode failed. %d", errCode); + } + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +int SQLiteUtils::CheckSchemaChanged(sqlite3_stmt *stmt, const TableInfo &table, int offset) +{ + if (stmt == nullptr) { + return -E_INVALID_ARGS; + } + + int columnNum = sqlite3_column_count(stmt); + if (columnNum - offset != static_cast(table.GetFields().size())) { + LOGE("Schema field number does not match."); + return -E_DISTRIBUTED_SCHEMA_CHANGED; + } + + auto fields = table.GetFields(); + for (int i = offset; i < columnNum; i++) { + const char *name = sqlite3_column_name(stmt, i); + std::string colName = (name == nullptr) ? std::string() : name; + const char *declType = sqlite3_column_decltype(stmt, i); + std::string colType = (declType == nullptr) ? std::string() : declType; + transform(colType.begin(), colType.end(), colType.begin(), ::tolower); + + auto it = fields.find(colName); + if (it == fields.end() || it->second.GetDataType() != colType) { + LOGE("Schema field define does not match."); + return -E_DISTRIBUTED_SCHEMA_CHANGED; + } + } + return E_OK; +} + +int64_t SQLiteUtils::GetLastRowId(sqlite3 *db) +{ + if (db == nullptr) { + return -1; + } + return sqlite3_last_insert_rowid(db); +} + +std::string SQLiteUtils::GetLastErrorMsg() +{ + std::lock_guard autoLock(logMutex_); + return lastErrorMsg_; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sqlite/sqlite_utils.h b/mock/distributeddb/storage/src/sqlite/sqlite_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..048d1097098a83239a2d02da05136697343bcdd8 --- /dev/null +++ b/mock/distributeddb/storage/src/sqlite/sqlite_utils.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SQLITE_UTILS_H +#define SQLITE_UTILS_H + +#include +#include +#include +#include "sqlite_import.h" + +#include "db_types.h" +#include "schema_object.h" +#include "store_types.h" +#ifdef RELATIONAL_STORE +#include "relational_schema_object.h" +#endif + +namespace DistributedDB { +enum class TransactType { + DEFERRED, + IMMEDIATE, +}; + +struct TransactFunc { + void (*xFunc)(sqlite3_context*, int, sqlite3_value**) = nullptr; + void (*xStep)(sqlite3_context*, int, sqlite3_value**) = nullptr; + void (*xFinal)(sqlite3_context*) = nullptr; + void (*xDestroy)(void*) = nullptr; +}; + +namespace TriggerMode { +enum class TriggerModeEnum { + NONE, + INSERT, + UPDATE, + DELETE +}; + +std::string GetTriggerModeString(TriggerModeEnum mode); +} + +struct OpenDbProperties { + std::string uri {}; + bool createIfNecessary = true; + bool isMemDb = false; + std::vector sqls {}; + CipherType cipherType = CipherType::AES_256_GCM; + CipherPassword passwd {}; + std::string schema {}; + std::string subdir {}; + SecurityOption securityOpt {}; + int conflictReslovePolicy = DEFAULT_LAST_WIN; + bool createDirByStoreIdOnly = false; +}; + +class SQLiteUtils { +public: + // Initialize the SQLiteUtils with the given properties. + static int OpenDatabase(const OpenDbProperties &properties, sqlite3 *&db, bool setWal = true); + + // Check the statement and prepare the new if statement is null + static int GetStatement(sqlite3 *db, const std::string &sql, sqlite3_stmt *&statement); + + // Bind the Text to the statement + static int BindTextToStatement(sqlite3_stmt *statement, int index, const std::string &str); + + static int BindInt64ToStatement(sqlite3_stmt *statement, int index, int64_t value); + + // Bind the blob to the statement + static int BindBlobToStatement(sqlite3_stmt *statement, int index, const std::vector &value, + bool permEmpty = true); + + // Reset the statement + static void ResetStatement(sqlite3_stmt *&statement, bool isNeedFinalize, int &errCode); + + // Step the statement + static int StepWithRetry(sqlite3_stmt *statement, bool isMemDb = false); + + // Bind the prefix key range + static int BindPrefixKey(sqlite3_stmt *statement, int index, const Key &keyPrefix); + + static int BeginTransaction(sqlite3 *db, TransactType type = TransactType::DEFERRED); + + static int CommitTransaction(sqlite3 *db); + + static int RollbackTransaction(sqlite3 *db); + + static int ExecuteRawSQL(sqlite3 *db, const std::string &sql); + + static int SetKey(sqlite3 *db, CipherType type, const CipherPassword &passwd, const bool &isMemDb); + + static int GetColumnBlobValue(sqlite3_stmt *statement, int index, std::vector &value); + + static int GetColumnTextValue(sqlite3_stmt *statement, int index, std::string &value); + + static int ExportDatabase(sqlite3 *db, CipherType type, const CipherPassword &passwd, const std::string &newDbName); + + static int ExportDatabase(const std::string &srcFile, CipherType type, const CipherPassword &srcPasswd, + const std::string &targetFile, const CipherPassword &passwd); + + static int Rekey(sqlite3 *db, const CipherPassword &passwd); + + static int GetVersion(const OpenDbProperties &properties, int &version); + + static int GetVersion(sqlite3 *db, int &version); + + static int GetJournalMode(sqlite3 *db, std::string &mode); + + static int SetUserVer(const OpenDbProperties &properties, int version); + + static int SetUserVer(sqlite3 *db, int version); + + static int MapSQLiteErrno(int errCode); + + static int SaveSchema(const OpenDbProperties &properties); + + static int SaveSchema(sqlite3 *db, const std::string &strSchema); + + static int GetSchema(const OpenDbProperties &properties, std::string &strSchema); + + static int GetSchema(sqlite3 *db, std::string &strSchema); + + static int IncreaseIndex(sqlite3 *db, const IndexName &name, const IndexInfo &info, SchemaType type, + uint32_t skipSize); + + static int ChangeIndex(sqlite3 *db, const IndexName &name, const IndexInfo &info, SchemaType type, + uint32_t skipSize); + + static int DecreaseIndex(sqlite3 *db, const IndexName &name); + + static int RegisterJsonFunctions(sqlite3 *db); + // Register the flatBufferExtract function if the schema is of flatBuffer-type(To be refactor) + static int RegisterFlatBufferFunction(sqlite3 *db, const std::string &inSchema); + + static int RegisterMetaDataUpdateFunction(sqlite3 *db); + + static int GetDbSize(const std::string &dir, const std::string &dbName, uint64_t &size); + + static int ExplainPlan(sqlite3 *db, const std::string &execSql, bool isQueryPlan); + + static int AttachNewDatabase(sqlite3 *db, CipherType type, const CipherPassword &password, + const std::string &attachDbAbsPath, const std::string &attachAsName = "backup"); + + static int CreateMetaDatabase(const std::string &metaDbPath); + + static int CheckIntegrity(sqlite3 *db, const std::string &sql); +#ifdef RELATIONAL_STORE + static int RegisterCalcHash(sqlite3 *db); + + static int RegisterGetSysTime(sqlite3 *db); + + static int CreateRelationalLogTable(sqlite3 *db, const std::string &oriTableName); + static int CreateRelationalMetaTable(sqlite3 *db); + + static int AddRelationalLogTableTrigger(sqlite3 *db, const TableInfo &table); + static int AnalysisSchema(sqlite3 *db, const std::string &tableName, TableInfo &table); + + static int CreateSameStuTable(sqlite3 *db, const TableInfo &baseTbl, const std::string &newTableName); + static int CloneIndexes(sqlite3 *db, const std::string &oriTableName, const std::string &newTableName); +#endif + + static int DropTriggerByName(sqlite3 *db, const std::string &name); + + static int ExpandedSql(sqlite3_stmt *stmt, std::string &basicString); + + static void ExecuteCheckPoint(sqlite3 *db); + + static int CheckTableEmpty(sqlite3 *db, const std::string &tableName, bool &isEmpty); + + static int SetPersistWalMode(sqlite3 *db); + + static int CheckSchemaChanged(sqlite3_stmt *stmt, const TableInfo &table, int offset); + + static int64_t GetLastRowId(sqlite3 *db); + + static std::string GetLastErrorMsg(); +private: + + static int CreateDataBase(const OpenDbProperties &properties, sqlite3 *&dbTemp, bool setWal); + + static int SetBusyTimeout(sqlite3 *db, int timeout); + + static void JsonExtractByPath(sqlite3_context *ctx, int argc, sqlite3_value **argv); + + static void JsonExtractInnerFunc(sqlite3_context *ctx, const ValueObject &inValue, const FieldPath &inPath); + + static void FlatBufferExtractByPath(sqlite3_context *ctx, int argc, sqlite3_value **argv); + + static void FlatBufferExtractInnerFunc(sqlite3_context *ctx, const SchemaObject &schema, const RawValue &inValue, + RawString inPath); + + static void ExtractReturn(sqlite3_context *ctx, FieldType type, const FieldValue &value); + + static void CalcHashKey(sqlite3_context *ctx, int argc, sqlite3_value **argv); + + static void GetSysTime(sqlite3_context *ctx, int argc, sqlite3_value **argv); + + static int SetDataBaseProperty(sqlite3 *db, const OpenDbProperties &properties, + const std::vector &sqls); + + static int RegisterFunction(sqlite3 *db, const std::string &funcName, int nArg, void *uData, TransactFunc &func); + +#ifndef OMIT_ENCRYPT + static int SetCipherSettings(sqlite3 *db, CipherType type); + + static std::string GetCipherName(CipherType type); +#endif + + static void UpdateMetaDataWithinTrigger(sqlite3_context *ctx, int argc, sqlite3_value **argv); + + static void SqliteLogCallback(void *data, int err, const char *msg); + + static std::mutex logMutex_; + static std::string lastErrorMsg_; +}; +} // namespace DistributedDB + +#endif // SQLITE_UTILS_H diff --git a/mock/distributeddb/storage/src/storage_engine.cpp b/mock/distributeddb/storage/src/storage_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73727a8d9b4faae50477bd3d9903d31e4df13176 --- /dev/null +++ b/mock/distributeddb/storage/src/storage_engine.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "storage_engine.h" + +#include + +#include "db_common.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +const int StorageEngine::MAX_WAIT_TIME = 30; +const int StorageEngine::MAX_WRITE_SIZE = 1; +const int StorageEngine::MAX_READ_SIZE = 16; + +StorageEngine::StorageEngine() + : isUpdated_(false), + isMigrating_(false), + engineState_(EngineState::INVALID), + commitNotifyFunc_(nullptr), + isInitialized_(false), + perm_(OperatePerm::NORMAL_PERM), + operateAbort_(false), + isExistConnection_(false) +{} + +StorageEngine::~StorageEngine() +{ + Release(); +} + +int StorageEngine::Init() +{ + if (isInitialized_) { + LOGD("Storage engine has been initialized!"); + return E_OK; + } + + // only for create the database avoid the minimum number is 0. + int errCode = E_OK; + StorageExecutor *handle = nullptr; + if (engineAttr_.minReadNum == 0 && engineAttr_.minWriteNum == 0) { + errCode = CreateNewExecutor(true, handle); + if (errCode != E_OK) { + goto ERROR; + } + + if (handle != nullptr) { + delete handle; + handle = nullptr; + } + } + + for (uint32_t i = 0; i < engineAttr_.minWriteNum; i++) { + handle = nullptr; + errCode = CreateNewExecutor(true, handle); + if (errCode != E_OK) { + goto ERROR; + } + AddStorageExecutor(handle); + } + + for (uint32_t i = 0; i < engineAttr_.minReadNum; i++) { + handle = nullptr; + errCode = CreateNewExecutor(false, handle); + if (errCode != E_OK) { + goto ERROR; + } + AddStorageExecutor(handle); + } + isInitialized_ = true; + +ERROR: + if (errCode != E_OK) { + // Assumed file system has classification function, can only get one write handle + if (errCode == -E_EKEYREVOKED && !writeIdleList_.empty()) { + return E_OK; + } + Release(); + } + return errCode; +} + +StorageExecutor *StorageEngine::FindExecutor(bool writable, OperatePerm perm, int &errCode, int waitTime) +{ + if (GetEngineState() == EngineState::ENGINE_BUSY) { + LOGI("Storage engine is busy!"); + errCode = -E_BUSY; + return nullptr; + } + + if (writable) { + return FindWriteExecutor(perm, errCode, waitTime); + } + + return FindReadExecutor(perm, errCode, waitTime); +} + +StorageExecutor *StorageEngine::FindWriteExecutor(OperatePerm perm, int &errCode, int waitTime) +{ + std::unique_lock lock(writeMutex_); + errCode = -E_BUSY; + if (perm_ == OperatePerm::DISABLE_PERM || perm_ != perm) { + LOGI("Not permitted to get the executor[%u]", static_cast(perm_)); + return nullptr; + } + if (waitTime <= 0) { // non-blocking. + if (writeIdleList_.empty() && + writeIdleList_.size() + writeUsingList_.size() == engineAttr_.maxWriteNum) { + return nullptr; + } + return FetchStorageExecutor(true, writeIdleList_, writeUsingList_, errCode); + } + + // Not prohibited and there is an available handle + bool result = writeCondition_.wait_for(lock, std::chrono::seconds(waitTime), + [this, &perm]() { + return (perm_ == OperatePerm::NORMAL_PERM || perm_ == perm) && (!writeIdleList_.empty() || + (writeIdleList_.size() + writeUsingList_.size() < engineAttr_.maxWriteNum) || + operateAbort_); + }); + if (operateAbort_) { + LOGI("Abort write executor and executor and busy for operate!"); + return nullptr; + } + if (!result) { + LOGI("Get write handle result[%d], permissType[%u], operType[%u], write[%zu-%zu-%" PRIu32 "]", result, + static_cast(perm_), static_cast(perm), writeIdleList_.size(), writeUsingList_.size(), + engineAttr_.maxWriteNum); + return nullptr; + } + return FetchStorageExecutor(true, writeIdleList_, writeUsingList_, errCode); +} + +StorageExecutor *StorageEngine::FindReadExecutor(OperatePerm perm, int &errCode, int waitTime) +{ + std::unique_lock lock(readMutex_); + errCode = -E_BUSY; + if (perm_ == OperatePerm::DISABLE_PERM || perm_ != perm) { + LOGI("Not permitted to get the executor[%u]", static_cast(perm_)); + return nullptr; + } + + if (waitTime <= 0) { // non-blocking. + if (readIdleList_.empty() && + readIdleList_.size() + readUsingList_.size() == engineAttr_.maxReadNum) { + return nullptr; + } + return FetchStorageExecutor(false, readIdleList_, readUsingList_, errCode); + } + + // Not prohibited and there is an available handle + bool result = readCondition_.wait_for(lock, std::chrono::seconds(waitTime), + [this, &perm]() { + return (perm_ == OperatePerm::NORMAL_PERM || perm_ == perm) && + (!readIdleList_.empty() || (readIdleList_.size() + readUsingList_.size() < engineAttr_.maxReadNum) || + operateAbort_); + }); + if (operateAbort_) { + LOGI("Abort find read executor and busy for operate!"); + return nullptr; + } + if (!result) { + LOGI("Get read handle result[%d], permissType[%u], operType[%u], read[%zu-%zu-%" PRIu32 "]", result, + static_cast(perm_), static_cast(perm), readIdleList_.size(), readUsingList_.size(), + engineAttr_.maxReadNum); + return nullptr; + } + return FetchStorageExecutor(false, readIdleList_, readUsingList_, errCode); +} + +void StorageEngine::Recycle(StorageExecutor *&handle) +{ + if (handle == nullptr) { + return; + } + std::string id = DBCommon::TransferStringToHex(identifier_); + LOGD("Recycle executor[%d] for id[%.6s]", handle->GetWritable(), id.c_str()); + if (handle->GetWritable()) { + std::unique_lock lock(writeMutex_); + auto iter = std::find(writeUsingList_.begin(), writeUsingList_.end(), handle); + if (iter != writeUsingList_.end()) { + writeUsingList_.remove(handle); + if (writeIdleList_.size() >= 1) { + delete handle; + handle = nullptr; + return; + } + handle->Reset(); + writeIdleList_.push_back(handle); + writeCondition_.notify_one(); + } + } else { + std::unique_lock lock(readMutex_); + auto iter = std::find(readUsingList_.begin(), readUsingList_.end(), handle); + if (iter != readUsingList_.end()) { + readUsingList_.remove(handle); + if (readIdleList_.size() >= 1) { + delete handle; + handle = nullptr; + return; + } + handle->Reset(); + readIdleList_.push_back(handle); + readCondition_.notify_one(); + } + } + handle = nullptr; +} + +void StorageEngine::ClearCorruptedFlag() +{ + return; +} + +void StorageEngine::Release() +{ + CloseExecutor(); + isInitialized_ = false; + isUpdated_ = false; + ClearCorruptedFlag(); + SetEngineState(EngineState::INVALID); +} + +int StorageEngine::TryToDisable(bool isNeedCheckAll, OperatePerm disableType) +{ + if (engineState_ != EngineState::MAINDB && engineState_ != EngineState::INVALID) { + LOGE("Not support disable handle when cacheDB existed! state = [%d]", engineState_); + return(engineState_ == EngineState::CACHEDB) ? -E_NOT_SUPPORT : -E_BUSY; + } + + std::lock(writeMutex_, readMutex_); + std::lock_guard writeLock(writeMutex_, std::adopt_lock); + std::lock_guard readLock(readMutex_, std::adopt_lock); + + if (!isNeedCheckAll) { + goto END; + } + + if (!writeUsingList_.empty() || !readUsingList_.empty()) { + LOGE("Database handle used"); + return -E_BUSY; + } +END: + if (perm_ == OperatePerm::NORMAL_PERM) { + LOGI("database is disable for re-build:%d", static_cast(disableType)); + perm_ = disableType; + writeCondition_.notify_all(); + readCondition_.notify_all(); + } + return E_OK; +} + +void StorageEngine::Enable(OperatePerm enableType) +{ + std::lock(writeMutex_, readMutex_); + std::lock_guard writeLock(writeMutex_, std::adopt_lock); + std::lock_guard readLock(readMutex_, std::adopt_lock); + if (perm_ == enableType) { + LOGI("Re-enable the database"); + perm_ = OperatePerm::NORMAL_PERM; + writeCondition_.notify_all(); + readCondition_.notify_all(); + } +} + +void StorageEngine::Abort(OperatePerm enableType) +{ + std::lock(writeMutex_, readMutex_); + std::lock_guard writeLock(writeMutex_, std::adopt_lock); + std::lock_guard readLock(readMutex_, std::adopt_lock); + if (perm_ == enableType) { + LOGI("Abort the handle occupy, release all!"); + perm_ = OperatePerm::NORMAL_PERM; + operateAbort_ = true; + + writeCondition_.notify_all(); + readCondition_.notify_all(); + } +} + +bool StorageEngine::IsNeedTobeReleased() const +{ + return true; +} + +const std::string &StorageEngine::GetIdentifier() const +{ + return identifier_; +} + +EngineState StorageEngine::GetEngineState() const +{ + return engineState_; +} + +void StorageEngine::SetEngineState(EngineState state) +{ + LOGI("Storage engine state to [%d]!", state); + engineState_ = state; +} + +bool StorageEngine::IsNeedMigrate() const +{ + LOGI("No need to migrate!"); + return false; +} + +int StorageEngine::ExecuteMigrate() +{ + LOGW("Migration is not supported!"); + return -E_NOT_SUPPORT; +} + +void StorageEngine::SetNotifiedCallback(const std::function &callback) +{ + std::unique_lock lock(notifyMutex_); + commitNotifyFunc_ = callback; + return; +} + +void StorageEngine::SetConnectionFlag(bool isExisted) +{ + return isExistConnection_.store(isExisted); +} + +bool StorageEngine::IsExistConnection() const +{ + return isExistConnection_.load(); +} + +void StorageEngine::ClearEnginePasswd() +{ + return; +} + +int StorageEngine::CheckEngineOption(const KvDBProperties &kvdbOption) const +{ + return E_OK; +} + +void StorageEngine::AddStorageExecutor(StorageExecutor *handle) +{ + if (handle == nullptr) { + return; + } + + if (handle->GetWritable()) { + writeIdleList_.push_back(handle); + } else { + readIdleList_.push_back(handle); + } +} + +void StorageEngine::CloseExecutor() +{ + { + std::lock_guard lock(writeMutex_); + for (auto &item : writeIdleList_) { + if (item != nullptr) { + delete item; + item = nullptr; + } + } + writeIdleList_.clear(); + } + + { + std::lock_guard lock(readMutex_); + for (auto &item : readIdleList_) { + if (item != nullptr) { + delete item; + item = nullptr; + } + } + readIdleList_.clear(); + } +} + +StorageExecutor *StorageEngine::FetchStorageExecutor(bool isWrite, std::list &idleList, + std::list &usingList, int &errCode) +{ + if (idleList.empty()) { + StorageExecutor *handle = nullptr; + errCode = CreateNewExecutor(isWrite, handle); + if ((errCode != E_OK) || (handle == nullptr)) { + if (errCode != -E_EKEYREVOKED) { + return nullptr; + } + LOGE("Key revoked status, couldn't create the new executor"); + if (!usingList.empty()) { + LOGE("Can't create new executor for revoked"); + errCode = -E_BUSY; + } + return nullptr; + } + + AddStorageExecutor(handle); + } + auto item = idleList.front(); + usingList.push_back(item); + idleList.remove(item); + LOGD("Get executor[%d] from [%.6s], using[%zu]", isWrite, + DBCommon::TransferStringToHex(identifier_).c_str(), usingList.size()); + errCode = E_OK; + return item; +} + +bool StorageEngine::CheckEngineAttr(const StorageEngineAttr &poolSize) +{ + return (poolSize.maxReadNum > MAX_READ_SIZE || + poolSize.maxWriteNum > MAX_WRITE_SIZE || + poolSize.minReadNum > poolSize.maxReadNum || + poolSize.minWriteNum > poolSize.maxWriteNum); +} + +bool StorageEngine::IsMigrating() const +{ + return isMigrating_.load(); +} +} diff --git a/mock/distributeddb/storage/src/storage_engine.h b/mock/distributeddb/storage/src/storage_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..a948e236dc5ada3cfa962d02db668a521c870742 --- /dev/null +++ b/mock/distributeddb/storage/src/storage_engine.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STORAGE_ENGINE_H +#define STORAGE_ENGINE_H + +#include +#include +#include +#include + +#include "db_types.h" +#include "macro_utils.h" +#include "storage_executor.h" +#include "kvdb_commit_notify_filterable_data.h" + +namespace DistributedDB { +struct StorageEngineAttr { + uint32_t minWriteNum = 1; + uint32_t maxWriteNum = 1; + uint32_t minReadNum = 1; + uint32_t maxReadNum = 1; +}; + +class StorageEngine { +public: + StorageEngine(); + virtual ~StorageEngine(); + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(StorageEngine); + + int Init(); + + StorageExecutor *FindExecutor(bool writable, OperatePerm perm, int &errCode, int waitTime = MAX_WAIT_TIME); + + void Recycle(StorageExecutor *&handle); + + void Release(); + + int TryToDisable(bool isNeedCheckAll, OperatePerm disableType = OperatePerm::DISABLE_PERM); + + void Enable(OperatePerm enableType = OperatePerm::NORMAL_PERM); + + void Abort(OperatePerm enableType = OperatePerm::NORMAL_PERM); + + virtual bool IsNeedTobeReleased() const; + + virtual const std::string &GetIdentifier() const; + + virtual EngineState GetEngineState() const; + + virtual void SetEngineState(EngineState state); + + virtual bool IsNeedMigrate() const; + + virtual int ExecuteMigrate(); + + virtual void SetNotifiedCallback(const std::function &callback); + + void SetConnectionFlag(bool isExisted); + + bool IsExistConnection() const; + + virtual void ClearEnginePasswd(); + + virtual int CheckEngineOption(const KvDBProperties &kvdbOption) const; + + virtual bool IsMigrating() const; + +protected: + virtual int CreateNewExecutor(bool isWrite, StorageExecutor *&handle) = 0; + + void CloseExecutor(); + + virtual void AddStorageExecutor(StorageExecutor *handle); + + static bool CheckEngineAttr(const StorageEngineAttr &poolSize); + + StorageEngineAttr engineAttr_; + bool isUpdated_; + std::atomic isMigrating_; + std::string identifier_; + EngineState engineState_; + + // Mutex for commitNotifyFunc_. + mutable std::shared_mutex notifyMutex_; + + // Callback function for commit notify. + std::function commitNotifyFunc_; + +private: + StorageExecutor *FetchStorageExecutor(bool isWrite, std::list &idleList, + std::list &usingList, int &errCode); + + StorageExecutor *FindWriteExecutor(OperatePerm perm, int &errCode, int waitTime); + StorageExecutor *FindReadExecutor(OperatePerm perm, int &errCode, int waitTime); + + virtual void ClearCorruptedFlag(); + + static const int MAX_WAIT_TIME; + static const int MAX_WRITE_SIZE; + static const int MAX_READ_SIZE; + + bool isInitialized_; + OperatePerm perm_; + bool operateAbort_; + + std::mutex readMutex_; + std::mutex writeMutex_; + std::condition_variable writeCondition_; + std::condition_variable readCondition_; + std::list writeUsingList_; + std::list writeIdleList_; + std::list readUsingList_; + std::list readIdleList_; + std::atomic isExistConnection_; +}; +} // namespace DistributedDB +#endif // STORAGE_ENGINE_H diff --git a/mock/distributeddb/storage/src/storage_engine_manager.cpp b/mock/distributeddb/storage/src/storage_engine_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..369873ffcb5bcd9010db2c3e13ee156a548f921e --- /dev/null +++ b/mock/distributeddb/storage/src/storage_engine_manager.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "storage_engine_manager.h" +#include "log_print.h" +#include "db_errno.h" +#include "runtime_context.h" +#include "sqlite_single_ver_storage_engine.h" + +namespace DistributedDB { +bool StorageEngineManager::isRegLockStatusListener_ = false; +std::mutex StorageEngineManager::instanceLock_; +std::atomic StorageEngineManager::instance_{nullptr}; +std::mutex StorageEngineManager::storageEnginesLock_; + +namespace { + std::string GetIdentifier(const KvDBProperties &property) + { + return property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + } + + int GetDatabaseType(const KvDBProperties &property) + { + return property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + } +} + +StorageEngineManager::StorageEngineManager() : lockStatusListener_(nullptr) +{} + +StorageEngineManager::~StorageEngineManager() +{ + if (lockStatusListener_ != nullptr) { + lockStatusListener_->Drop(true); + } +} + +StorageEngine *StorageEngineManager::GetStorageEngine(const KvDBProperties &property, int &errCode) +{ + StorageEngineManager *manager = GetInstance(); + if (manager == nullptr) { + LOGE("[StorageEngineManager] GetInstance failed"); + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + std::string identifier = GetIdentifier(property); + manager->EnterGetEngineProcess(identifier); + auto storageEngine = manager->FindStorageEngine(identifier); + if (storageEngine == nullptr) { + storageEngine = manager->CreateStorageEngine(property, errCode); + if (errCode == E_OK) { + manager->InsertStorageEngine(identifier, storageEngine); + } + } else { + errCode = storageEngine->CheckEngineOption(property); + if (errCode != E_OK) { + LOGE("kvdb property mismatch engine option! errCode = [%d]", errCode); + storageEngine = nullptr; + } + } + + manager->ExitGetEngineProcess(identifier); + return storageEngine; +} + +int StorageEngineManager::ReleaseStorageEngine(StorageEngine *storageEngine) +{ + if (storageEngine == nullptr) { + LOGE("[StorageEngineManager] The engine to be released is nullptr"); + return -E_INVALID_ARGS; + } + + // Clear commit notify callback function. + storageEngine->SetNotifiedCallback(nullptr); + + // If the cacheDB is valid, the storageEngine is not released to prevent the cache mechanism failed + bool isRelease = storageEngine->IsNeedTobeReleased(); + if (!isRelease) { + LOGW("[StorageEngineManager] storageEngine do not need to be released."); + return E_OK; + } + + StorageEngineManager *manager = GetInstance(); + if (manager == nullptr) { + LOGE("[StorageEngineManager] Release GetInstance failed"); + return -E_OUT_OF_MEMORY; + } + + LOGD("[StorageEngineManager] storageEngine to be released."); + return manager->ReleaseEngine(storageEngine); +} + +int StorageEngineManager::ForceReleaseStorageEngine(const std::string &identifier) +{ + StorageEngineManager *manager = GetInstance(); + if (manager == nullptr) { + LOGE("[StorageEngineManager] Force release GetInstance failed"); + return -E_OUT_OF_MEMORY; + } + + LOGD("[StorageEngineManager] Force release engine."); + manager->ReleaseResources(identifier); + return E_OK; +} + +int StorageEngineManager::ExecuteMigration(StorageEngine *storageEngine) +{ + if (storageEngine == nullptr) { + LOGE("storage engine is nullptr can not execute migration!"); + return -E_INVALID_ARGS; + } + if (storageEngine->IsExistConnection()) { + return storageEngine->ExecuteMigrate(); + } + LOGI("connection is not existed, not need execute migration!"); + return -E_INVALID_DB; +} + +StorageEngineManager *StorageEngineManager::GetInstance() +{ + // For Double-Checked Locking, we need check instance_ twice + if (instance_ == nullptr) { + std::lock_guard lockGuard(instanceLock_); + if (instance_ == nullptr) { + instance_ = new (std::nothrow) StorageEngineManager(); + if (instance_ == nullptr) { + LOGE("[StorageEngineManager] Failed to alloc the engine manager!"); + return nullptr; + } + } + } + + if (!isRegLockStatusListener_) { + int errCode = (instance_.load())->RegisterLockStatusListener(); + if (errCode != E_OK) { + LOGW("[StorageEngineManager] Failed to regitster lock status listener:%d", errCode); + } else { + isRegLockStatusListener_ = true; + } + } + return instance_; +} + +int StorageEngineManager::RegisterLockStatusListener() +{ + int errCode = E_OK; + lockStatusListener_ = RuntimeContext::GetInstance()->RegisterLockStatusLister( + [this](void *lockStatus) { + if (lockStatus == nullptr) { + return; + } + bool isLocked = *static_cast(lockStatus); + LOGD("[StorageEngineManager] Lock status to %d", isLocked); + if (isLocked) { + return; + } + int taskErrCode = RuntimeContext::GetInstance()->ScheduleTask( + std::bind(&StorageEngineManager::LockStatusNotifier, this, isLocked)); + if (taskErrCode != E_OK) { + LOGE("[StorageEngineManager] LockStatusNotifier ScheduleTask failed : %d", taskErrCode); + } + }, errCode); + if (errCode != E_OK) { + LOGW("[StorageEngineManager] Failed to register lock status listener: %d.", errCode); + } + return errCode; +} + +void StorageEngineManager::LockStatusNotifier(bool isAccessControlled) +{ + (void)isAccessControlled; + std::lock_guard lockGuard(storageEnginesLock_); + StorageEngine *storageEngine = nullptr; + for (const auto &item : storageEngines_) { + storageEngine = item.second; + LOGD("Begin to migrate for lock status change"); + (void)ExecuteMigration(storageEngine); + } +} + +void StorageEngineManager::RemoveEngineFromCache(const std::string &identifier) +{ + StorageEngineManager *manager = GetInstance(); + if (manager != nullptr) { + manager->EraseStorageEngine(identifier); + } +} + +StorageEngine *StorageEngineManager::CreateStorageEngine(const KvDBProperties &property, int &errCode) +{ + int databaseType = GetDatabaseType(property); + if (databaseType != KvDBProperties::SINGLE_VER_TYPE) { + LOGE("[StorageEngineManager] Database type error : %d", databaseType); + errCode = -E_NOT_SUPPORT; + return nullptr; + } + + auto storageEngine = new (std::nothrow) SQLiteSingleVerStorageEngine(); + if (storageEngine == nullptr) { + LOGE("[StorageEngineManager] Create storage engine failed"); + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + errCode = E_OK; + return storageEngine; +} + +StorageEngine *StorageEngineManager::FindStorageEngine(const std::string &identifier) +{ + std::lock_guard lockGuard(storageEnginesLock_); + auto iter = storageEngines_.find(identifier); + if (iter != storageEngines_.end()) { + auto storageEngine = iter->second; + if (storageEngine == nullptr) { + LOGE("[StorageEngineManager] storageEngine in cache is nullptr"); + storageEngines_.erase(identifier); + return nullptr; + } + + return storageEngine; + } + + return nullptr; +} + +void StorageEngineManager::InsertStorageEngine(const std::string &identifier, StorageEngine *&storageEngine) +{ + std::lock_guard lockGuard(storageEnginesLock_); + storageEngines_.insert(std::pair(identifier, storageEngine)); +} + +void StorageEngineManager::EraseStorageEngine(const std::string &identifier) +{ + std::lock_guard lockGuard(storageEnginesLock_); + storageEngines_.erase(identifier); +} + +void StorageEngineManager::ReleaseResources(const std::string &identifier) +{ + StorageEngine *storageEngine = nullptr; + + { + std::lock_guard lockGuard(storageEnginesLock_); + auto iter = storageEngines_.find(identifier); + if (iter != storageEngines_.end()) { + storageEngine = iter->second; + storageEngines_.erase(identifier); + } + } + + if (storageEngine != nullptr) { + LOGI("[StorageEngineManager] Release storage engine"); + delete storageEngine; + storageEngine = nullptr; + } + + return; +} + +int StorageEngineManager::ReleaseEngine(StorageEngine *releaseEngine) +{ + const std::string identifier = releaseEngine->GetIdentifier(); + StorageEngine *cacheEngine = nullptr; + + { + std::lock_guard lockGuard(storageEnginesLock_); + auto iter = storageEngines_.find(identifier); + if (iter != storageEngines_.end()) { + cacheEngine = iter->second; + storageEngines_.erase(identifier); + } + } + + if (cacheEngine == nullptr) { + LOGE("[StorageEngineManager] cache engine is null"); + return -E_ALREADY_RELEASE; + } + if (cacheEngine != releaseEngine) { + LOGE("[StorageEngineManager] cache engine is not equal the input engine"); + return -E_INVALID_ARGS; + } + + delete releaseEngine; + releaseEngine = nullptr; + return E_OK; +} + +void StorageEngineManager::EnterGetEngineProcess(const std::string &identifier) +{ + std::unique_lock lock(getEngineMutex_); + getEngineCondition_.wait(lock, [this, &identifier]() { + return this->getEngineSet_.count(identifier) == 0; + }); + (void)getEngineSet_.insert(identifier); +} + +void StorageEngineManager::ExitGetEngineProcess(const std::string &identifier) +{ + std::unique_lock lock(getEngineMutex_); + (void)getEngineSet_.erase(identifier); + getEngineCondition_.notify_all(); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/storage_executor.cpp b/mock/distributeddb/storage/src/storage_executor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b6a9faf7519d38e16b257c76e2e6d085989f69e --- /dev/null +++ b/mock/distributeddb/storage/src/storage_executor.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "storage_executor.h" + +#include "db_errno.h" + +namespace DistributedDB { +StorageExecutor::StorageExecutor(bool writable) + : writable_(writable), + isCorrupted_(false) +{} + +StorageExecutor::~StorageExecutor() +{} + +bool StorageExecutor::GetWritable() const +{ + return writable_; +} + +bool StorageExecutor::GetCorruptedStatus() const +{ + return isCorrupted_; +} + +void StorageExecutor::SetCorruptedStatus() const +{ + isCorrupted_ = true; +} + +int StorageExecutor::CheckCorruptedStatus(int errCode) const +{ + if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) { + SetCorruptedStatus(); + } + return errCode; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/storage_executor.h b/mock/distributeddb/storage/src/storage_executor.h new file mode 100644 index 0000000000000000000000000000000000000000..9645cef7a61b010c1240c2b5ed8dbfef8a41c3d1 --- /dev/null +++ b/mock/distributeddb/storage/src/storage_executor.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STORAGE_EXECUTOR_H +#define STORAGE_EXECUTOR_H + +#include "macro_utils.h" + +namespace DistributedDB { +enum EngineState { + INVALID = -1, // default value, representative database is not generated + CACHEDB, + ATTACHING, // main db and cache db attach together + MIGRATING, // begine to Migrate data + MAINDB, + ENGINE_BUSY, // In order to change handle during the migration process, it is temporarily unavailable +}; + +class StorageExecutor { +public: + explicit StorageExecutor(bool writable); + virtual ~StorageExecutor(); + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(StorageExecutor); + + virtual bool GetWritable() const; + + virtual int CheckCorruptedStatus(int errCode) const; + + virtual bool GetCorruptedStatus() const; + + virtual void SetCorruptedStatus() const; + + virtual int Reset() = 0; + +protected: + bool writable_; + mutable bool isCorrupted_; +}; +} // namespace DistributedDB + +#endif // STORAGE_EXECUTOR_H diff --git a/mock/distributeddb/storage/src/sync_able_engine.cpp b/mock/distributeddb/storage/src/sync_able_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff02fea25848d4e7344943c1213b44a12168d9e0 --- /dev/null +++ b/mock/distributeddb/storage/src/sync_able_engine.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sync_able_engine.h" + +#include "db_dump_helper.h" +#include "db_errno.h" +#include "log_print.h" +#include "parcel.h" +#include "runtime_context.h" + +namespace DistributedDB { +SyncAbleEngine::SyncAbleEngine(ISyncInterface *store) + : syncer_(), + started_(false), + store_(store) +{} + +SyncAbleEngine::~SyncAbleEngine() +{} + +// Start a sync action. +int SyncAbleEngine::Sync(const ISyncer::SyncParma &parm, uint64_t connectionId) +{ + if (!started_) { + StartSyncer(); + if (!started_) { + return -E_NOT_INIT; + } + } + return syncer_.Sync(parm, connectionId); +} + +void SyncAbleEngine::WakeUpSyncer() +{ + StartSyncer(); +} + +void SyncAbleEngine::Close() +{ + StopSyncer(); +} + +void SyncAbleEngine::EnableAutoSync(bool enable) +{ + if (!started_) { + StartSyncer(); + } + return syncer_.EnableAutoSync(enable); +} + +int SyncAbleEngine::EnableManualSync(void) +{ + return syncer_.EnableManualSync(); +} + +int SyncAbleEngine::DisableManualSync(void) +{ + return syncer_.DisableManualSync(); +} + +// Get The current virtual timestamp +uint64_t SyncAbleEngine::GetTimestamp() +{ + if (!started_) { + StartSyncer(); + } + return syncer_.GetTimestamp(); +} + +int SyncAbleEngine::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, const std::string &tableName) +{ + if (!started_) { + StartSyncer(); + } + return syncer_.EraseDeviceWaterMark(deviceId, isNeedHash, tableName); +} + +// Start syncer +void SyncAbleEngine::StartSyncer() +{ + if (store_ == nullptr) { + LOGF("KvDB got null sync interface."); + return; + } + + int errCode = syncer_.Initialize(store_, true); + if (errCode == E_OK) { + started_ = true; + } else { + LOGE("KvDB start syncer failed, err:'%d'.", errCode); + } +} + +// Stop syncer +void SyncAbleEngine::StopSyncer() +{ + if (started_) { + syncer_.Close(true); + started_ = false; + } +} + +void SyncAbleEngine::TriggerSync(int notifyEvent) +{ + if (!started_) { + StartSyncer(); + } + if (started_) { + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this, notifyEvent] { + syncer_.LocalDataChanged(notifyEvent); + }); + if (errCode != E_OK) { + LOGE("[TriggerSync] SyncAbleEngine TriggerSync LocalDataChanged retCode:%d", errCode); + } + } +} + +int SyncAbleEngine::GetLocalIdentity(std::string &outTarget) +{ + if (!started_) { + StartSyncer(); + } + return syncer_.GetLocalIdentity(outTarget); +} + +void SyncAbleEngine::StopSync(uint64_t connectionId) +{ + if (started_) { + syncer_.StopSync(connectionId); + } +} + +void SyncAbleEngine::Dump(int fd) +{ + SyncerBasicInfo basicInfo = syncer_.DumpSyncerBasicInfo(); + DBDumpHelper::Dump(fd, "\tisSyncActive = %d, isAutoSync = %d\n\n", basicInfo.isSyncActive, + basicInfo.isAutoSync); + if (basicInfo.isSyncActive) { + DBDumpHelper::Dump(fd, "\tDistributedDB Database Sync Module Message Info:\n"); + syncer_.Dump(fd); + } +} +} diff --git a/mock/distributeddb/storage/src/sync_able_engine.h b/mock/distributeddb/storage/src/sync_able_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..30e2c9da02afc4611d60af718a95f4de46838dde --- /dev/null +++ b/mock/distributeddb/storage/src/sync_able_engine.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNC_ABLE_ENGINE_H +#define SYNC_ABLE_ENGINE_H + +#include + +#include "ref_object.h" +#include "syncer_proxy.h" + +namespace DistributedDB { +class SyncAbleEngine final { +public: + explicit SyncAbleEngine(ISyncInterface *store); + ~SyncAbleEngine(); + void TriggerSync(int notifyEvent); + + // Start a sync action. + int Sync(const ISyncer::SyncParma &parm, uint64_t connectionId); + + void WakeUpSyncer(); + void Close(); + + // Enable auto sync + void EnableAutoSync(bool enable); + + int EnableManualSync(void); + int DisableManualSync(void); + + // Get The current virtual timestamp + uint64_t GetTimestamp(); + + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, const std::string &tableName = ""); + + int GetLocalIdentity(std::string &outTarget); + + // Stop a sync action in progress + void StopSync(uint64_t connectionId); + + void Dump(int fd); +private: + // Start syncer + void StartSyncer(); + + // Stop syncer + void StopSyncer(); + + SyncerProxy syncer_; // use for sync Interactive + std::atomic started_; + ISyncInterface *store_; +}; +} // namespace DistributedDB +#endif // SYNC_ABLE_ENGINE_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/sync_able_kvdb.cpp b/mock/distributeddb/storage/src/sync_able_kvdb.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd2bb5638dc6857d4b797ff191cb501fcec8165c --- /dev/null +++ b/mock/distributeddb/storage/src/sync_able_kvdb.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sync_able_kvdb.h" + +#include "db_dump_helper.h" +#include "db_errno.h" +#include "log_print.h" +#include "parcel.h" +#include "runtime_context.h" +#include "user_change_monitor.h" + +namespace DistributedDB { +const EventType SyncAbleKvDB::REMOTE_PUSH_FINISHED = 1; + +SyncAbleKvDB::SyncAbleKvDB() + : started_(false), + closed_(false), + isSyncModuleActiveCheck_(false), + isSyncNeedActive_(true), + notifyChain_(nullptr), + userChangeListerner_(nullptr) +{} + +SyncAbleKvDB::~SyncAbleKvDB() +{ + if (notifyChain_ != nullptr) { + (void)notifyChain_->UnRegisterEventType(REMOTE_PUSH_FINISHED); + KillAndDecObjRef(notifyChain_); + notifyChain_ = nullptr; + } + if (userChangeListerner_ != nullptr) { + userChangeListerner_->Drop(true); + userChangeListerner_ = nullptr; + } +} + +void SyncAbleKvDB::DelConnection(GenericKvDBConnection *connection) +{ + auto realConnection = static_cast(connection); + if (realConnection != nullptr) { + KillAndDecObjRef(realConnection); + realConnection = nullptr; + } +} + +void SyncAbleKvDB::TriggerSync(int notifyEvent) +{ + if (!started_) { + StartSyncer(); + } + if (started_) { + syncer_.LocalDataChanged(notifyEvent); + } +} + +void SyncAbleKvDB::CommitNotify(int notifyEvent, KvDBCommitNotifyFilterAbleData *data) +{ + SyncAbleKvDB::TriggerSync(notifyEvent); + + GenericKvDB::CommitNotify(notifyEvent, data); +} + +void SyncAbleKvDB::Close() +{ + StopSyncer(true); +} + +// Start a sync action. +int SyncAbleKvDB::Sync(const ISyncer::SyncParma &parma, uint64_t connectionId) +{ + if (!started_) { + int errCode = StartSyncer(); + if (!started_) { + return errCode; + } + } + return syncer_.Sync(parma, connectionId); +} + +void SyncAbleKvDB::EnableAutoSync(bool enable) +{ + if (!started_) { + StartSyncer(); + } + return syncer_.EnableAutoSync(enable); +} + +void SyncAbleKvDB::WakeUpSyncer() +{ + StartSyncer(); +} + +// Stop a sync action in progress. +void SyncAbleKvDB::StopSync(uint64_t connectionId) +{ + if (started_) { + syncer_.StopSync(connectionId); + } +} + +void SyncAbleKvDB::SetSyncModuleActive() +{ + if (isSyncModuleActiveCheck_) { + return; + } + IKvDBSyncInterface *syncInterface = GetSyncInterface(); + if (syncInterface == nullptr) { + LOGF("KvDB got null sync interface."); + return; + } + bool isSyncDualTupleMode = syncInterface->GetDbProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, + false); + if (!isSyncDualTupleMode) { + isSyncNeedActive_ = true; + isSyncModuleActiveCheck_ = true; + return; + } + std::string userId = syncInterface->GetDbProperties().GetStringProp(KvDBProperties::USER_ID, ""); + std::string appId = syncInterface->GetDbProperties().GetStringProp(KvDBProperties::APP_ID, ""); + std::string storeId = syncInterface->GetDbProperties().GetStringProp(KvDBProperties::STORE_ID, ""); + isSyncNeedActive_ = RuntimeContext::GetInstance()->IsSyncerNeedActive(userId, appId, storeId); + if (!isSyncNeedActive_) { + LOGI("syncer no need to active"); + } + isSyncModuleActiveCheck_ = true; +} + +bool SyncAbleKvDB::GetSyncModuleActive() +{ + return isSyncNeedActive_; +} + +void SyncAbleKvDB::ReSetSyncModuleActive() +{ + isSyncModuleActiveCheck_ = false; + isSyncNeedActive_ = true; +} + +// Start syncer +int SyncAbleKvDB::StartSyncer(bool isCheckSyncActive, bool isNeedActive) +{ + std::unique_lock lock(syncerOperateLock_); + int errCode = StartSyncerWithNoLock(isCheckSyncActive, isNeedActive); + closed_ = false; + return errCode; +} + +int SyncAbleKvDB::StartSyncerWithNoLock(bool isCheckSyncActive, bool isNeedActive) +{ + IKvDBSyncInterface *syncInterface = GetSyncInterface(); + if (syncInterface == nullptr) { + LOGF("KvDB got null sync interface."); + return -E_INVALID_ARGS; + } + if (!isCheckSyncActive) { + SetSyncModuleActive(); + isNeedActive = GetSyncModuleActive(); + } + int errCode = syncer_.Initialize(syncInterface, isNeedActive); + if (errCode == E_OK) { + started_ = true; + } else { + LOGE("KvDB start syncer failed, err:'%d'.", errCode); + } + bool isSyncDualTupleMode = syncInterface->GetDbProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, + false); + if (isSyncDualTupleMode && isCheckSyncActive && !isNeedActive && (userChangeListerner_ == nullptr)) { + // active to non_active + userChangeListerner_ = RuntimeContext::GetInstance()->RegisterUserChangedListerner( + std::bind(&SyncAbleKvDB::ChangeUserListerner, this), UserChangeMonitor::USER_ACTIVE_TO_NON_ACTIVE_EVENT); + } else if (isSyncDualTupleMode && (userChangeListerner_ == nullptr)) { + EventType event = isNeedActive ? + UserChangeMonitor::USER_ACTIVE_EVENT : UserChangeMonitor::USER_NON_ACTIVE_EVENT; + userChangeListerner_ = RuntimeContext::GetInstance()->RegisterUserChangedListerner( + std::bind(&SyncAbleKvDB::UserChangeHandle, this), event); + } + return errCode; +} + +// Stop syncer +void SyncAbleKvDB::StopSyncer(bool isClosedOperation) +{ + std::unique_lock lock(syncerOperateLock_); + StopSyncerWithNoLock(isClosedOperation); +} + +void SyncAbleKvDB::StopSyncerWithNoLock(bool isClosedOperation) +{ + ReSetSyncModuleActive(); + syncer_.Close(isClosedOperation); + if (started_) { + started_ = false; + } + closed_ = isClosedOperation; + if (userChangeListerner_ != nullptr) { + userChangeListerner_->Drop(false); + userChangeListerner_ = nullptr; + } +} + +void SyncAbleKvDB::UserChangeHandle() +{ + bool isNeedChange; + bool isNeedActive = true; + IKvDBSyncInterface *syncInterface = GetSyncInterface(); + if (syncInterface == nullptr) { + LOGF("KvDB got null sync interface."); + return; + } + std::unique_lock lock(syncerOperateLock_); + if (closed_) { + LOGI("kvDB is already closed"); + return; + } + std::string userId = syncInterface->GetDbProperties().GetStringProp(KvDBProperties::USER_ID, ""); + std::string appId = syncInterface->GetDbProperties().GetStringProp(KvDBProperties::APP_ID, ""); + std::string storeId = syncInterface->GetDbProperties().GetStringProp(KvDBProperties::STORE_ID, ""); + isNeedActive = RuntimeContext::GetInstance()->IsSyncerNeedActive(userId, appId, storeId); + isNeedChange = (isNeedActive != isSyncNeedActive_) ? true : false; + // non_active to active or active to non_active + if (isNeedChange) { + StopSyncerWithNoLock(); // will drop userChangeListerner; + isSyncModuleActiveCheck_ = true; + isSyncNeedActive_ = isNeedActive; + StartSyncerWithNoLock(true, isNeedActive); + } +} + +void SyncAbleKvDB::ChangeUserListerner() +{ + // only active to non_active call, put into USER_NON_ACTIVE_EVENT listerner from USER_ACTIVE_TO_NON_ACTIVE_EVENT + if (userChangeListerner_ != nullptr) { + userChangeListerner_->Drop(false); + userChangeListerner_ = nullptr; + } + if (userChangeListerner_ == nullptr) { + userChangeListerner_ = RuntimeContext::GetInstance()->RegisterUserChangedListerner( + std::bind(&SyncAbleKvDB::UserChangeHandle, this), UserChangeMonitor::USER_NON_ACTIVE_EVENT); + } +} + +// Get The current virtual timestamp +uint64_t SyncAbleKvDB::GetTimestamp() +{ + if (!started_ && !isSyncModuleActiveCheck_) { + StartSyncer(); + } + return syncer_.GetTimestamp(); +} + +// Get the dataItem's append length +uint32_t SyncAbleKvDB::GetAppendedLen() const +{ + return Parcel::GetAppendedLen(); +} + +int SyncAbleKvDB::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) +{ + if (!started_) { + StartSyncer(); + } + return syncer_.EraseDeviceWaterMark(deviceId, isNeedHash); +} + +int SyncAbleKvDB::GetQueuedSyncSize(int *queuedSyncSize) const +{ + return syncer_.GetQueuedSyncSize(queuedSyncSize); +} + +int SyncAbleKvDB::SetQueuedSyncLimit(const int *queuedSyncLimit) +{ + return syncer_.SetQueuedSyncLimit(queuedSyncLimit); +} + +int SyncAbleKvDB::GetQueuedSyncLimit(int *queuedSyncLimit) const +{ + return syncer_.GetQueuedSyncLimit(queuedSyncLimit); +} + +int SyncAbleKvDB::DisableManualSync(void) +{ + return syncer_.DisableManualSync(); +} + +int SyncAbleKvDB::EnableManualSync(void) +{ + return syncer_.EnableManualSync(); +} + +int SyncAbleKvDB::GetLocalIdentity(std::string &outTarget) +{ + return syncer_.GetLocalIdentity(outTarget); +} + +int SyncAbleKvDB::SetStaleDataWipePolicy(WipePolicy policy) +{ + return syncer_.SetStaleDataWipePolicy(policy); +} + +int SyncAbleKvDB::RegisterEventType(EventType type) +{ + if (notifyChain_ == nullptr) { + notifyChain_ = new (std::nothrow) NotificationChain; + if (notifyChain_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + } + + int errCode = notifyChain_->RegisterEventType(type); + if (errCode == -E_ALREADY_REGISTER) { + return E_OK; + } + if (errCode != E_OK) { + LOGE("[SyncAbleKvDB] Register event type %u failed! err %d", type, errCode); + KillAndDecObjRef(notifyChain_); + notifyChain_ = nullptr; + } + return errCode; +} + +NotificationChain::Listener *SyncAbleKvDB::AddRemotePushFinishedNotify(const RemotePushFinishedNotifier ¬ifier, + int &errCode) +{ + std::unique_lock lock(notifyChainLock_); + errCode = RegisterEventType(REMOTE_PUSH_FINISHED); + if (errCode != E_OK) { + return nullptr; + } + + auto listener = notifyChain_->RegisterListener(REMOTE_PUSH_FINISHED, + [notifier](void *arg) { + if (arg == nullptr) { + LOGE("PragmaRemotePushNotify is null."); + return; + } + notifier(*static_cast(arg)); + }, nullptr, errCode); + if (errCode != E_OK) { + LOGE("[SyncAbleKvDB] Add remote push finished notifier failed! err %d", errCode); + } + return listener; +} + +void SyncAbleKvDB::NotifyRemotePushFinishedInner(const std::string &targetId) const +{ + { + std::shared_lock lock(notifyChainLock_); + if (notifyChain_ == nullptr) { + return; + } + } + RemotePushNotifyInfo info; + info.deviceId = targetId; + notifyChain_->NotifyEvent(REMOTE_PUSH_FINISHED, static_cast(&info)); +} + +int SyncAbleKvDB::SetSyncRetry(bool isRetry) +{ + return syncer_.SetSyncRetry(isRetry); +} + +int SyncAbleKvDB::SetEqualIdentifier(const std::string &identifier, const std::vector &targets) +{ + return syncer_.SetEqualIdentifier(identifier, targets); +} + +void SyncAbleKvDB::Dump(int fd) +{ + SyncerBasicInfo basicInfo = syncer_.DumpSyncerBasicInfo(); + DBDumpHelper::Dump(fd, "\tisSyncActive = %d, isAutoSync = %d\n\n", basicInfo.isSyncActive, + basicInfo.isAutoSync); + if (basicInfo.isSyncActive) { + DBDumpHelper::Dump(fd, "\tDistributedDB Database Sync Module Message Info:\n"); + syncer_.Dump(fd); + } +} +} diff --git a/mock/distributeddb/storage/src/sync_able_kvdb.h b/mock/distributeddb/storage/src/sync_able_kvdb.h new file mode 100644 index 0000000000000000000000000000000000000000..133a242c604d5fa21f3c051e0fa6ca7044c90381 --- /dev/null +++ b/mock/distributeddb/storage/src/sync_able_kvdb.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNC_ABLE_KVDB_H +#define SYNC_ABLE_KVDB_H + +#include + +#include "generic_kvdb.h" +#include "ikvdb_sync_interface.h" +#include "intercepted_data.h" +#include "sync_able_kvdb_connection.h" +#include "syncer_proxy.h" + +namespace DistributedDB { +class SyncAbleKvDB : public GenericKvDB { +public: + SyncAbleKvDB(); + ~SyncAbleKvDB() override; + DISABLE_COPY_ASSIGN_MOVE(SyncAbleKvDB); + + // Delete a connection object. + void DelConnection(GenericKvDBConnection *connection) override; + + // Used to notify Syncer and other listeners data has changed + void CommitNotify(int notifyEvent, KvDBCommitNotifyFilterAbleData *data) override; + + // Invoked automatically when connection count is zero + void Close() override; + + // Start a sync action. + int Sync(const ISyncer::SyncParma &parma, uint64_t connectionId); + + // Enable auto sync + void EnableAutoSync(bool enable); + + // Stop a sync action in progress. + void StopSync(uint64_t connectionId); + + // Get The current virtual timestamp + uint64_t GetTimestamp(); + + void WakeUpSyncer() override; + + // Get manual sync queue size + int GetQueuedSyncSize(int *queuedSyncSize) const; + + // Set manual sync queue limit + int SetQueuedSyncLimit(const int *queuedSyncLimit); + + // Get manual sync queue limit + int GetQueuedSyncLimit(int *queuedSyncLimit) const; + + // Disable add new manual sync , for rekey + int DisableManualSync(void); + + // Enable add new manual sync , for rekey + int EnableManualSync(void); + + int SetStaleDataWipePolicy(WipePolicy policy); + + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash); + + NotificationChain::Listener *AddRemotePushFinishedNotify(const RemotePushFinishedNotifier ¬ifier, int &errCode); + + void NotifyRemotePushFinishedInner(const std::string &targetId) const; + + int SetSyncRetry(bool isRetry); + // Set an equal identifier for this database, After this called, send msg to the target will use this identifier + int SetEqualIdentifier(const std::string &identifier, const std::vector &targets); + + virtual void SetDataInterceptor(const PushDataInterceptor &interceptor) = 0; + + void Dump(int fd) override; + +protected: + virtual IKvDBSyncInterface *GetSyncInterface() = 0; + + void SetSyncModuleActive(); + + bool GetSyncModuleActive(); + + void ReSetSyncModuleActive(); + // Start syncer + int StartSyncer(bool isCheckSyncActive = false, bool isNeedActive = true); + + int StartSyncerWithNoLock(bool isCheckSyncActive, bool isNeedActive); + + // Stop syncer + void StopSyncer(bool isClosedOperation = false); + + void StopSyncerWithNoLock(bool isClosedOperation = false); + + void UserChangeHandle(); + + void ChangeUserListerner(); + + // Get the dataItem's append length, the append length = after-serialized-len - original-dataItem-len + uint32_t GetAppendedLen() const; + + int GetLocalIdentity(std::string &outTarget); + + void TriggerSync(int notifyEvent); + +private: + int RegisterEventType(EventType type); + + SyncerProxy syncer_; + std::atomic started_; + std::atomic closed_; + std::atomic isSyncModuleActiveCheck_; + std::atomic isSyncNeedActive_; + mutable std::shared_mutex notifyChainLock_; + NotificationChain *notifyChain_; + + mutable std::mutex syncerOperateLock_; + NotificationChain::Listener *userChangeListerner_; + + static const EventType REMOTE_PUSH_FINISHED; +}; +} + +#endif // SYNC_ABLE_KVDB_H diff --git a/mock/distributeddb/storage/src/sync_able_kvdb_connection.cpp b/mock/distributeddb/storage/src/sync_able_kvdb_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca10da6dba362090e65b2c659c4f3e7a3e2fe2d7 --- /dev/null +++ b/mock/distributeddb/storage/src/sync_able_kvdb_connection.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sync_able_kvdb_connection.h" + +#include "log_print.h" +#include "db_errno.h" +#include "db_constant.h" +#include "kvdb_pragma.h" +#include "performance_analysis.h" +#include "runtime_context.h" +#include "sync_able_kvdb.h" + +namespace DistributedDB { +SyncAbleKvDBConnection::SyncAbleKvDBConnection(SyncAbleKvDB *kvDB) + : GenericKvDBConnection(kvDB), + remotePushFinishedListener_(nullptr) +{ + OnKill([this]() { + auto *db = GetDB(); + if (db == nullptr) { + return; + } + // Drop the lock before we call RemoveSyncOperation(). + UnlockObj(); + db->StopSync(GetConnectionId()); + LockObj(); + }); +} + +SyncAbleKvDBConnection::~SyncAbleKvDBConnection() +{ + if (remotePushFinishedListener_ != nullptr) { + remotePushFinishedListener_->Drop(true); + } + remotePushFinishedListener_ = nullptr; +} + +void SyncAbleKvDBConnection::InitPragmaFunc() +{ + if (!pragmaFunc_.empty()) { + return; + } + pragmaFunc_ = { + {PRAGMA_SYNC_DEVICES, [this](void *parameter, int &errCode) { + errCode = PragmaSyncAction(static_cast(parameter)); }}, + {PRAGMA_AUTO_SYNC, [this](void *parameter, int &errCode) { + errCode = EnableAutoSync(*(static_cast(parameter))); }}, + {PRAGMA_PERFORMANCE_ANALYSIS_GET_REPORT, [](void *parameter, int &errCode) { + *(static_cast(parameter)) = PerformanceAnalysis::GetInstance()->GetStatistics(); }}, + {PRAGMA_PERFORMANCE_ANALYSIS_OPEN, [](void *parameter, int &errCode) { + PerformanceAnalysis::GetInstance()->OpenPerformanceAnalysis(); }}, + {PRAGMA_PERFORMANCE_ANALYSIS_CLOSE, [](void *parameter, int &errCode) { + PerformanceAnalysis::GetInstance()->ClosePerformanceAnalysis(); }}, + {PRAGMA_PERFORMANCE_ANALYSIS_SET_REPORTFILENAME, [](void *parameter, int &errCode) { + PerformanceAnalysis::GetInstance()->SetFileNumber(*(static_cast(parameter))); }}, + {PRAGMA_GET_QUEUED_SYNC_SIZE, [this](void *parameter, int &errCode) { + errCode = GetQueuedSyncSize(static_cast(parameter)); }}, + {PRAGMA_SET_QUEUED_SYNC_LIMIT, [this](void *parameter, int &errCode) { + errCode = SetQueuedSyncLimit(static_cast(parameter)); }}, + {PRAGMA_GET_QUEUED_SYNC_LIMIT, [this](void *parameter, int &errCode) { + errCode = GetQueuedSyncLimit(static_cast(parameter)); }}, + {PRAGMA_SET_WIPE_POLICY, [this](void *parameter, int &errCode) { + errCode = SetStaleDataWipePolicy(static_cast(parameter)); }}, + {PRAGMA_REMOTE_PUSH_FINISHED_NOTIFY, [this](void *parameter, int &errCode) { + errCode = SetRemotePushFinishedNotify(static_cast(parameter)); }}, + {PRAGMA_SET_SYNC_RETRY, [this](void *parameter, int &errCode) { + errCode = SetSyncRetry(*(static_cast(parameter))); }}, + {PRAGMA_ADD_EQUAL_IDENTIFIER, [this](void *parameter, int &errCode) { + errCode = SetEqualIdentifier(static_cast(parameter)); }}, + {PRAGMA_INTERCEPT_SYNC_DATA, [this](void *parameter, int &errCode) { + errCode = SetPushDataInterceptor(*static_cast(parameter)); }}, + {PRAGMA_SUBSCRIBE_QUERY, [this](void *parameter, int &errCode) { + errCode = PragmaSyncAction(static_cast(parameter)); }}, + }; +} + +int SyncAbleKvDBConnection::Pragma(int cmd, void *parameter) +{ + int errCode = PragmaParamCheck(cmd, parameter); + if (errCode != E_OK) { + return -E_INVALID_ARGS; + } + + InitPragmaFunc(); + auto iter = pragmaFunc_.find(cmd); + if (iter != pragmaFunc_.end()) { + iter->second(parameter, errCode); + return errCode; + } + + // Call Pragma() of super class. + return GenericKvDBConnection::Pragma(cmd, parameter); +} + +int SyncAbleKvDBConnection::PragmaParamCheck(int cmd, const void *parameter) +{ + switch (cmd) { + case PRAGMA_AUTO_SYNC: + case PRAGMA_PERFORMANCE_ANALYSIS_GET_REPORT: + case PRAGMA_PERFORMANCE_ANALYSIS_SET_REPORTFILENAME: + if (parameter == nullptr) { + return -E_INVALID_ARGS; + } + return E_OK; + default: + return E_OK; + } +} + +int SyncAbleKvDBConnection::PragmaSyncAction(const PragmaSync *syncParameter) +{ + if (syncParameter == nullptr) { + return -E_INVALID_ARGS; + } + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + + if (isExclusive_.load()) { + return -E_BUSY; + } + { + AutoLock lockGuard(this); + if (IsKilled()) { + // If this happens, users are using a closed connection. + LOGE("Pragma sync on a closed connection."); + return -E_STALE; + } + IncObjRef(this); + } + + ISyncer::SyncParma syncParam; + syncParam.devices = syncParameter->devices_; + syncParam.mode = syncParameter->mode_; + syncParam.wait = syncParameter->wait_; + syncParam.isQuerySync = syncParameter->isQuerySync_; + syncParam.syncQuery = syncParameter->query_; + syncParam.onFinalize = [this]() { DecObjRef(this); }; + syncParam.onComplete = std::bind(&SyncAbleKvDBConnection::OnSyncComplete, this, std::placeholders::_1, + syncParameter->onComplete_, syncParameter->wait_); + int errCode = kvDB->Sync(syncParam, GetConnectionId()); + if (errCode != E_OK) { + DecObjRef(this); + } + return errCode; +} + +int SyncAbleKvDBConnection::EnableAutoSync(bool enable) +{ + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + kvDB->EnableAutoSync(enable); + return E_OK; +} + +void SyncAbleKvDBConnection::OnSyncComplete(const std::map &statuses, + const std::function &devicesMap)> &onComplete, bool wait) +{ + AutoLock lockGuard(this); + if (!IsKilled() && onComplete) { + // Drop the lock before invoking the callback. + // Do pragma-sync again in the prev sync callback is supported. + UnlockObj(); + // The connection may be closed after UnlockObj(). + // RACE: 'KillObj()' against 'onComplete()'. + if (!IsKilled()) { + onComplete(statuses); + } + LockObj(); + } +} + +int SyncAbleKvDBConnection::GetQueuedSyncSize(int *queuedSyncSize) const +{ + if (queuedSyncSize == nullptr) { + return -E_INVALID_ARGS; + } + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + return kvDB->GetQueuedSyncSize(queuedSyncSize); +} + +int SyncAbleKvDBConnection::SetQueuedSyncLimit(const int *queuedSyncLimit) +{ + if (queuedSyncLimit == nullptr) { + return -E_INVALID_ARGS; + } + if ((*queuedSyncLimit > DBConstant::QUEUED_SYNC_LIMIT_MAX) || + (*queuedSyncLimit < DBConstant::QUEUED_SYNC_LIMIT_MIN)) { + return -E_INVALID_ARGS; + } + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + return kvDB->SetQueuedSyncLimit(queuedSyncLimit); +} + +int SyncAbleKvDBConnection::GetQueuedSyncLimit(int *queuedSyncLimit) const +{ + if (queuedSyncLimit == nullptr) { + return -E_INVALID_ARGS; + } + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + return kvDB->GetQueuedSyncLimit(queuedSyncLimit); +} + +int SyncAbleKvDBConnection::DisableManualSync(void) +{ + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + return kvDB->DisableManualSync(); +} + +int SyncAbleKvDBConnection::EnableManualSync(void) +{ + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + return kvDB->EnableManualSync(); +} + +int SyncAbleKvDBConnection::SetStaleDataWipePolicy(const WipePolicy *policy) +{ + if (policy == nullptr) { + return -E_INVALID_ARGS; + } + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + return kvDB->SetStaleDataWipePolicy(*policy); +} + +int SyncAbleKvDBConnection::SetRemotePushFinishedNotify(PragmaRemotePushNotify *notifyParma) +{ + if (notifyParma == nullptr) { + return -E_INVALID_ARGS; + } + + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + + int errCode = E_OK; + NotificationChain::Listener *tmpListener = nullptr; + if (notifyParma->notifier_ != nullptr) { + tmpListener = kvDB->AddRemotePushFinishedNotify(notifyParma->notifier_, errCode); + if (tmpListener == nullptr) { + return errCode; + } + } + + std::lock_guard lock(remotePushFinishedListenerLock_); + // Drop old listener and set the new listener + if (remotePushFinishedListener_ != nullptr) { + errCode = remotePushFinishedListener_->Drop(); + if (errCode != E_OK) { + LOGE("[SyncAbleConnection] Drop Remote push finished listener failed %d", errCode); + if (tmpListener != nullptr) { + tmpListener->Drop(); + } + return errCode; + } + } + remotePushFinishedListener_ = tmpListener; + return errCode; +} + +int SyncAbleKvDBConnection::SetSyncRetry(bool isRetry) +{ + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + return kvDB->SetSyncRetry(isRetry); +} + +int SyncAbleKvDBConnection::SetEqualIdentifier(const PragmaSetEqualIdentifier *param) +{ + if (param == nullptr) { + return -E_INVALID_ARGS; + } + + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + return kvDB->SetEqualIdentifier(param->identifier_, param->targets_); +} + +int SyncAbleKvDBConnection::SetPushDataInterceptor(const PushDataInterceptor &interceptor) +{ + SyncAbleKvDB *kvDB = GetDB(); + if (kvDB == nullptr) { + return -E_INVALID_CONNECTION; + } + kvDB->SetDataInterceptor(interceptor); + return E_OK; +} +} diff --git a/mock/distributeddb/storage/src/sync_able_kvdb_connection.h b/mock/distributeddb/storage/src/sync_able_kvdb_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..a640bbe0fd92bd42c31015edcbe0712328287056 --- /dev/null +++ b/mock/distributeddb/storage/src/sync_able_kvdb_connection.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNC_ABLE_KVDB_CONNECTION_H +#define SYNC_ABLE_KVDB_CONNECTION_H + +#include "generic_kvdb_connection.h" +#include "intercepted_data.h" +#include "ref_object.h" + +namespace DistributedDB { +class SyncAbleKvDB; +struct PragmaSync; +struct PragmaRemotePushNotify; +struct PragmaSetEqualIdentifier; + +class SyncAbleKvDBConnection : public GenericKvDBConnection, public virtual RefObject { +public: + explicit SyncAbleKvDBConnection(SyncAbleKvDB *kvDB); + ~SyncAbleKvDBConnection() override; + DISABLE_COPY_ASSIGN_MOVE(SyncAbleKvDBConnection); + + // Pragma interface. + int Pragma(int cmd, void *parameter) override; + +protected: + int DisableManualSync(); + + int EnableManualSync(); + +private: + // Do pragma-sync action. + int PragmaParamCheck(int cmd, const void *parameter); + int PragmaSyncAction(const PragmaSync *syncParameter); + void InitPragmaFunc(); + + // If enable is true, it will enable auto sync + int EnableAutoSync(bool enable); + + void OnSyncComplete(const std::map &statuses, + const std::function &devicesMap)> &onComplete, bool wait); + + int GetQueuedSyncSize(int *queuedSyncSize) const; + + int SetQueuedSyncLimit(const int *queuedSyncLimit); + + int GetQueuedSyncLimit(int *queuedSyncLimit) const; + + int SetStaleDataWipePolicy(const WipePolicy *policy); + + int SetRemotePushFinishedNotify(PragmaRemotePushNotify *notifyParma); + + int SetSyncRetry(bool isRetry); + // Set an equal identifier for this database, After this called, send msg to the target will use this identifier + int SetEqualIdentifier(const PragmaSetEqualIdentifier *param); + + int SetPushDataInterceptor(const PushDataInterceptor &interceptor); + + std::mutex remotePushFinishedListenerLock_; + NotificationChain::Listener *remotePushFinishedListener_; + std::map> pragmaFunc_{}; +}; +} +#endif // SYNC_ABLE_KVDB_CONNECTION_H diff --git a/mock/distributeddb/storage/src/upgrader/database_upgrader.h b/mock/distributeddb/storage/src/upgrader/database_upgrader.h new file mode 100644 index 0000000000000000000000000000000000000000..2877a3d047722fe50cb85c650d07cd88e1545f0e --- /dev/null +++ b/mock/distributeddb/storage/src/upgrader/database_upgrader.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DATABASE_UPGRADER_H +#define DATABASE_UPGRADER_H + +namespace DistributedDB { +class DatabaseUpgrader { +public: + virtual ~DatabaseUpgrader() {}; + virtual int Upgrade() = 0; +}; +} // namespace DistributedDB +#endif // DATABASE_UPGRADER_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/upgrader/single_ver_database_upgrader.cpp b/mock/distributeddb/storage/src/upgrader/single_ver_database_upgrader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2af6ecd9ba96e923b076a0a6069a055dded56493 --- /dev/null +++ b/mock/distributeddb/storage/src/upgrader/single_ver_database_upgrader.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_database_upgrader.h" +#include "db_errno.h" +#include "log_print.h" +#include "version.h" + +namespace DistributedDB { +int SingleVerDatabaseUpgrader::Upgrade() +{ + int errCode = GetDatabaseVersion(dbVersion_); + if (errCode != E_OK) { + LOGE("[SingleUp][Upgrade] GetVersion error:%d.", errCode); + return errCode; + } + if (dbVersion_ > SINGLE_VER_STORE_VERSION_CURRENT) { + LOGE("[SingleUp][Upgrade] DbVersion=%d is newer.", dbVersion_); + return -E_VERSION_NOT_SUPPORT; + } + LOGI("[SingleUp][Upgrade] from %d to %d.", dbVersion_, SINGLE_VER_STORE_VERSION_CURRENT); + errCode = BeginUpgrade(); + if (errCode != E_OK) { + LOGE("[SingleUp][Upgrade] Begin error:%d.", errCode); + return errCode; + } + errCode = ExecuteUpgrade(); + if (errCode != E_OK) { + LOGE("[SingleUp][Upgrade] Execute error:%d.", errCode); + EndUpgrade(false); + return errCode; + } + + if (dbVersion_ < SINGLE_VER_STORE_VERSION_CURRENT) { + errCode = SetDatabaseVersion(SINGLE_VER_STORE_VERSION_CURRENT); + if (errCode != E_OK) { + LOGE("[SingleUp][Upgrade] SetVersion error:%d.", errCode); + EndUpgrade(false); + return errCode; + } + } + + errCode = EndUpgrade(true); + if (errCode != E_OK) { + LOGE("[SingleUp][Upgrade] End error:%d.", errCode); + return errCode; + } + + return E_OK; +} + +int SingleVerDatabaseUpgrader::ExecuteUpgrade() +{ + if (dbVersion_ <= SINGLE_VER_STORE_VERSION_CURRENT) { + return UpgradeFromDatabaseVersion(dbVersion_); + } + return E_OK; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/upgrader/single_ver_database_upgrader.h b/mock/distributeddb/storage/src/upgrader/single_ver_database_upgrader.h new file mode 100644 index 0000000000000000000000000000000000000000..fcdbae46a50c84070ff6915b472950ac291a4b61 --- /dev/null +++ b/mock/distributeddb/storage/src/upgrader/single_ver_database_upgrader.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_DATABASE_UPGRADER_H +#define SINGLE_VER_DATABASE_UPGRADER_H + +#include "database_upgrader.h" + +namespace DistributedDB { +class SingleVerDatabaseUpgrader : virtual public DatabaseUpgrader { +public: + ~SingleVerDatabaseUpgrader() override {}; + int Upgrade() override; +protected: + virtual int BeginUpgrade() = 0; + virtual int ExecuteUpgrade(); + virtual int EndUpgrade(bool isSuccess) = 0; + + // Database structure related upgrade + virtual int GetDatabaseVersion(int &version) const = 0; + virtual int SetDatabaseVersion(int version) = 0; + virtual int UpgradeFromDatabaseVersion(int version) = 0; + + // Database version only increased when the structure of database changed + int dbVersion_ = 0; +}; +} // namespace DistributedDB +#endif // SINGLE_VER_DATABASE_UPGRADER_H \ No newline at end of file diff --git a/mock/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.cpp b/mock/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..00b0560a89b240d712f4bee43799834687d3d0eb --- /dev/null +++ b/mock/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_schema_database_upgrader.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +SingleVerSchemaDatabaseUpgrader::SingleVerSchemaDatabaseUpgrader(const SchemaObject &newSchema) + : newSchema_(newSchema) +{ +} + +int SingleVerSchemaDatabaseUpgrader::ExecuteUpgrade() +{ + // Calling the base class to upgrade the database structure first + int errCode = SingleVerDatabaseUpgrader::ExecuteUpgrade(); + if (errCode != E_OK) { + LOGE("[SingleSchemaUp][ExecUp] Upgrade database structure fail, errCode=%d.", errCode); + return errCode; + } + return ExecuteUpgradeSchema(); +} + +int SingleVerSchemaDatabaseUpgrader::ExecuteUpgradeSchema() +{ + // Upgrade value or index according to newSchema and oriDbSchema + LOGD("[SingleSchemaUp][ExecUp] Enter."); + if (!newSchema_.IsSchemaValid()) { + LOGI("[SingleSchemaUp][ExecUp] No schema newly designated."); + return E_OK; + } + SchemaObject oriSchemaObject; + int errCode = RestoreSchemaObjectFromDatabase(oriSchemaObject); + if (errCode != E_OK) { + return errCode; + } + // Judge and gather upgrade information + bool valueNeedUpgrade = false; + IndexDifference indexDiffer; + if (oriSchemaObject.IsSchemaValid()) { + errCode = oriSchemaObject.CompareAgainstSchemaObject(newSchema_, indexDiffer); + if (errCode == -E_SCHEMA_EQUAL_EXACTLY) { + LOGI("[SingleSchemaUp][ExecUp] NewSchema equal exactly with oriDbSchema."); + return E_OK; + } else if (errCode == -E_SCHEMA_UNEQUAL_INCOMPATIBLE) { + return -E_SCHEMA_MISMATCH; + } else if (errCode == -E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE) { + valueNeedUpgrade = true; + } + // ValueNeedUpgrade false for case E_SCHEMA_UNEQUAL_COMPATIBLE + } else { + // Upgrade normalDb to schemaDb + valueNeedUpgrade = true; + indexDiffer.increase = newSchema_.GetIndexInfo(); + } + // Remember to upgrade value first, upgrade index later, for both Json-Schema and FlatBuffer-Schema + if (valueNeedUpgrade) { + errCode = UpgradeValues(); + if (errCode != E_OK) { + return errCode; + } + } + errCode = UpgradeIndexes(indexDiffer); + if (errCode != E_OK) { + return errCode; + } + // Update schema into database file + errCode = SetDatabaseSchema(newSchema_.ToSchemaString()); + if (errCode != E_OK) { + return errCode; + } + return E_OK; +} + +int SingleVerSchemaDatabaseUpgrader::RestoreSchemaObjectFromDatabase(SchemaObject &outOriSchema) const +{ + std::string oriDbSchema; + int errCode = GetDatabaseSchema(oriDbSchema); // If no schema in db should return E_OK with an empty string + if (errCode != E_OK) { + return errCode; + } + if (!oriDbSchema.empty()) { + errCode = outOriSchema.ParseFromSchemaString(oriDbSchema); + if (errCode != E_OK) { + LOGW("[SingleSchemaUp][ExecUp] Schema in dbFile parse fail, regard as no schema."); + } + } + return E_OK; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.h b/mock/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.h new file mode 100644 index 0000000000000000000000000000000000000000..2054671f441237f13ce311f335429438cedb13a2 --- /dev/null +++ b/mock/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_SCHEMA_DATABASE_UPGRADER_H +#define SINGLE_VER_SCHEMA_DATABASE_UPGRADER_H + +#include "schema_object.h" +#include "single_ver_database_upgrader.h" + +namespace DistributedDB { +class SingleVerSchemaDatabaseUpgrader : virtual public SingleVerDatabaseUpgrader { +public: + // An invalid SchemaObject indicate no schema + explicit SingleVerSchemaDatabaseUpgrader(const SchemaObject &newSchema); + ~SingleVerSchemaDatabaseUpgrader() override {}; +protected: + int ExecuteUpgrade() override; + + // Database content related upgrade + int ExecuteUpgradeSchema(); + + // Get an empty string with return_code E_OK indicate no schema but everything normally + virtual int GetDatabaseSchema(std::string &dbSchema) const = 0; + + // Set or update schema into database file + virtual int SetDatabaseSchema(const std::string &dbSchema) = 0; + + virtual int UpgradeValues() = 0; + virtual int UpgradeIndexes(const IndexDifference &indexDiffer) = 0; + + SchemaObject newSchema_; +private: + int RestoreSchemaObjectFromDatabase(SchemaObject &outOriSchema) const; +}; +} // namespace DistributedDB +#endif // SINGLE_VER_SCHEMA_DATABASE_UPGRADER_H diff --git a/mock/distributeddb/syncer/include/isyncer.h b/mock/distributeddb/syncer/include/isyncer.h new file mode 100644 index 0000000000000000000000000000000000000000..96571937a816e73b1f454a77812732f4aad0997e --- /dev/null +++ b/mock/distributeddb/syncer/include/isyncer.h @@ -0,0 +1,126 @@ + +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_SYNCER_H +#define I_SYNCER_H + +#include +#include +#include + +#include "isync_interface.h" +#include "types_export.h" +#include "query_sync_object.h" +#include "store_types.h" + +namespace DistributedDB { +struct SyncerBasicInfo { + bool isSyncActive = false; + bool isAutoSync = false; + bool isClearHistoryData = false; +}; +class ISyncer { +public: + struct SyncParma { + std::vector devices; + std::function &devicesMap)> onComplete; + SyncStatusCallback relationOnComplete; + std::function onFinalize; + int mode = 0; + bool wait = false; + bool isQuerySync = false; + QuerySyncObject syncQuery; + }; + + virtual ~ISyncer() {}; + + // Init the Syncer modules + virtual int Initialize(ISyncInterface *syncInterface, bool isNeedActive) + { + return -E_NOT_SUPPORT; + } + + // Close + virtual int Close(bool isClosedOperation) = 0; + + // Sync function. + // param devices: The device id list. + // param mode: Sync mode, see SyncMode. + // param onComplete: The syncer finish callback. set by caller + // param onFinalize: will be callback when this Sync Operation finalized. + // return a Sync id. It will return a positive value if failed, + virtual int Sync(const std::vector &devices, int mode, + const std::function &)> &onComplete, + const std::function &onFinalize, bool wait) = 0; + + // Sync function. use SyncParma to reduce parameter. + virtual int Sync(const SyncParma ¶m, uint64_t connectionId) = 0; + + // Remove the operation, with the given syncId, used to clean resource if sync finished or failed. + virtual int RemoveSyncOperation(int syncId) = 0; + + virtual int StopSync(uint64_t connectionId) = 0; + + // Get The current virtual timestamp + virtual uint64_t GetTimestamp() = 0; + + // Enable auto sync function + virtual void EnableAutoSync(bool enable) = 0; + + // delete specified device's watermark + virtual int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) = 0; + + // delete specified device's and table's watermark + virtual int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, + const std::string &tableName) = 0; + + // Local data changed callback + virtual void LocalDataChanged(int notifyEvent) = 0; + + // Get manual sync queue size + virtual int GetQueuedSyncSize(int *queuedSyncSize) const = 0; + + // Set manual sync queue limit + virtual int SetQueuedSyncLimit(const int *queuedSyncLimit) = 0; + + // Get manual sync queue limit + virtual int GetQueuedSyncLimit(int *queuedSyncLimit) const = 0; + + // Disable add new manual sync, for rekey + virtual int DisableManualSync(void) = 0; + + // Enable add new manual sync, for rekey + virtual int EnableManualSync(void) = 0; + + // Get local deviceId, is hashed + virtual int GetLocalIdentity(std::string &outTarget) const = 0; + + // Set stale data wipe policy + virtual int SetStaleDataWipePolicy(WipePolicy policy) = 0; + + // Set Manual Sync retry config + virtual int SetSyncRetry(bool isRetry) = 0; + + // Set an equal identifier for this database, After this called, send msg to the target will use this identifier + virtual int SetEqualIdentifier(const std::string &identifier, const std::vector &targets) = 0; + + virtual void Dump(int fd) = 0; + + virtual SyncerBasicInfo DumpSyncerBasicInfo() = 0; +}; +} // namespace DistributedDB + +#endif // I_SYNCER_H diff --git a/mock/distributeddb/syncer/include/syncer_proxy.h b/mock/distributeddb/syncer/include/syncer_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..d8ce88ceb038bccb7ae32eb4d62ed7c3948b8f0d --- /dev/null +++ b/mock/distributeddb/syncer/include/syncer_proxy.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNCER_PROXY_H +#define SYNCER_PROXY_H + +#include +#include +#include +#include + +#include "isyncer.h" + +namespace DistributedDB { +class SyncerProxy : public ISyncer { +public: + SyncerProxy(); + ~SyncerProxy() {}; + + // Init the Syncer modules + int Initialize(ISyncInterface *syncInterface, bool isNeedActive) override; + + // Close the syncer + int Close(bool isClosedOperation) override; + + // Sync function. + // param devices: The device id list. + // param mode: Sync mode, see SyncMode. + // param onComplete: The syncer finish callback. set by caller + // param onFinalize: will be callback when this Sync Operation finalized. + // return a Sync id. It will return a positive value if failed, + int Sync(const std::vector &devices, int mode, + const std::function &)> &onComplete, + const std::function &onFinalize, bool wait) override; + + // Sync function. use SyncParma to reduce parameter. + int Sync(const SyncParma ¶m, uint64_t connectionId) override; + + // Remove the operation, with the given syncId, used to clean resource if sync finished or failed. + int RemoveSyncOperation(int syncId) override; + + int StopSync(uint64_t connectionId) override; + + // Get The current virtual timestamp + uint64_t GetTimestamp() override; + + // Enable auto sync function + void EnableAutoSync(bool enable) override; + + // delete specified device's watermark + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) override; + + // delete specified device's and table's watermark + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, + const std::string &tableName) override; + + // Local data changed callback + void LocalDataChanged(int notifyEvent) override; + + // Get manual sync queue size + int GetQueuedSyncSize(int *queuedSyncSize) const override; + + // Set manual sync queue limit + int SetQueuedSyncLimit(const int *queuedSyncLimit) override; + + // Get manual sync queue limit + int GetQueuedSyncLimit(int *queuedSyncLimit) const override; + + // Disable add new manual sync, for rekey + int DisableManualSync(void) override; + + // Enable add new manual sync, for rekey + int EnableManualSync(void) override; + + // Get local deviceId, is hashed + int GetLocalIdentity(std::string &outTarget) const override; + + // Set stale data wipe policy + int SetStaleDataWipePolicy(WipePolicy policy) override; + + // Set Manual Sync retry config + int SetSyncRetry(bool isRetry) override; + + // Set an equal identifier for this database, After this called, send msg to the target will use this identifier + int SetEqualIdentifier(const std::string &identifier, const std::vector &targets) override; + + // Dump syncer info + void Dump(int fd) override; + + // Dump syncer basic info + SyncerBasicInfo DumpSyncerBasicInfo() override; + +private: + std::mutex syncerLock_; + std::shared_ptr syncer_; +}; +} // namespace DistributedDB + +#endif // SYNCER_PROXY_H diff --git a/mock/distributeddb/syncer/src/ability_sync.cpp b/mock/distributeddb/syncer/src/ability_sync.cpp new file mode 100644 index 0000000000000000000000000000000000000000..94d7510517c517ea2089fc635bb4c898ec623e30 --- /dev/null +++ b/mock/distributeddb/syncer/src/ability_sync.cpp @@ -0,0 +1,1208 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ability_sync.h" + +#include "message_transform.h" +#include "version.h" +#include "db_errno.h" +#include "log_print.h" +#include "sync_types.h" +#include "db_common.h" +#include "single_ver_kvdb_sync_interface.h" +#include "single_ver_sync_task_context.h" +#include "single_ver_kv_sync_task_context.h" +#ifdef RELATIONAL_STORE +#include "relational_db_sync_interface.h" +#include "single_ver_relational_sync_task_context.h" +#endif + +namespace DistributedDB { +AbilitySyncRequestPacket::AbilitySyncRequestPacket() + : protocolVersion_(ABILITY_SYNC_VERSION_V1), + sendCode_(E_OK), + softwareVersion_(SOFTWARE_VERSION_CURRENT), + secLabel_(0), + secFlag_(0), + schemaType_(0), + dbCreateTime_(0) +{ +} + +AbilitySyncRequestPacket::~AbilitySyncRequestPacket() +{ +} + +void AbilitySyncRequestPacket::SetProtocolVersion(uint32_t protocolVersion) +{ + protocolVersion_ = protocolVersion; +} + +uint32_t AbilitySyncRequestPacket::GetProtocolVersion() const +{ + return protocolVersion_; +} + +void AbilitySyncRequestPacket::SetSendCode(int32_t sendCode) +{ + sendCode_ = sendCode; +} + +int32_t AbilitySyncRequestPacket::GetSendCode() const +{ + return sendCode_; +} + +void AbilitySyncRequestPacket::SetSoftwareVersion(uint32_t swVersion) +{ + softwareVersion_ = swVersion; +} + +uint32_t AbilitySyncRequestPacket::GetSoftwareVersion() const +{ + return softwareVersion_; +} + +void AbilitySyncRequestPacket::SetSchema(const std::string &schema) +{ + schema_ = schema; +} + +std::string AbilitySyncRequestPacket::GetSchema() const +{ + return schema_; +} + +void AbilitySyncRequestPacket::SetSchemaType(uint32_t schemaType) +{ + schemaType_ = schemaType; +} + +uint32_t AbilitySyncRequestPacket::GetSchemaType() const +{ + return schemaType_; +} + +void AbilitySyncRequestPacket::SetSecLabel(int32_t secLabel) +{ + secLabel_ = secLabel; +} + +int32_t AbilitySyncRequestPacket::GetSecLabel() const +{ + return secLabel_; +} + +void AbilitySyncRequestPacket::SetSecFlag(int32_t secFlag) +{ + secFlag_ = secFlag; +} + +int32_t AbilitySyncRequestPacket::GetSecFlag() const +{ + return secFlag_; +} + +void AbilitySyncRequestPacket::SetDbCreateTime(uint64_t dbCreateTime) +{ + dbCreateTime_ = dbCreateTime; +} + +uint64_t AbilitySyncRequestPacket::GetDbCreateTime() const +{ + return dbCreateTime_; +} + +uint32_t AbilitySyncRequestPacket::CalculateLen() const +{ + uint64_t len = 0; + len += Parcel::GetUInt32Len(); // protocolVersion_ + len += Parcel::GetIntLen(); // sendCode_ + len += Parcel::GetUInt32Len(); // softwareVersion_ + uint32_t schemLen = Parcel::GetStringLen(schema_); + if (schemLen == 0) { + LOGE("[AbilitySyncRequestPacket][CalculateLen] schemLen err!"); + return 0; + } + len += schemLen; + len += Parcel::GetIntLen(); // secLabel_ + len += Parcel::GetIntLen(); // secFlag_ + len += Parcel::GetUInt32Len(); // schemaType_ + len += Parcel::GetUInt64Len(); // dbCreateTime_ + len += DbAbility::CalculateLen(dbAbility_); // dbAbility_ + // the reason why not 8-byte align is that old version is not 8-byte align + // so it is not possible to set 8-byte align for high version. + if (len > INT32_MAX) { + LOGE("[AbilitySyncRequestPacket][CalculateLen] err len:%" PRIu64, len); + return 0; + } + return len; +} + +DbAbility AbilitySyncRequestPacket::GetDbAbility() const +{ + return dbAbility_; +} + +void AbilitySyncRequestPacket::SetDbAbility(const DbAbility &dbAbility) +{ + dbAbility_ = dbAbility; +} + +AbilitySyncAckPacket::AbilitySyncAckPacket() + : protocolVersion_(ABILITY_SYNC_VERSION_V1), + softwareVersion_(SOFTWARE_VERSION_CURRENT), + ackCode_(E_OK), + secLabel_(0), + secFlag_(0), + schemaType_(0), + permitSync_(0), + requirePeerConvert_(0), + dbCreateTime_(0) +{ +} + +AbilitySyncAckPacket::~AbilitySyncAckPacket() +{ +} + +void AbilitySyncAckPacket::SetProtocolVersion(uint32_t protocolVersion) +{ + protocolVersion_ = protocolVersion; +} + +void AbilitySyncAckPacket::SetSoftwareVersion(uint32_t swVersion) +{ + softwareVersion_ = swVersion; +} + +uint32_t AbilitySyncAckPacket::GetSoftwareVersion() const +{ + return softwareVersion_; +} + +uint32_t AbilitySyncAckPacket::GetProtocolVersion() const +{ + return protocolVersion_; +} + +void AbilitySyncAckPacket::SetAckCode(int32_t ackCode) +{ + ackCode_ = ackCode; +} + +int32_t AbilitySyncAckPacket::GetAckCode() const +{ + return ackCode_; +} + +void AbilitySyncAckPacket::SetSchema(const std::string &schema) +{ + schema_ = schema; +} + +std::string AbilitySyncAckPacket::GetSchema() const +{ + return schema_; +} + +void AbilitySyncAckPacket::SetSchemaType(uint32_t schemaType) +{ + schemaType_ = schemaType; +} + +uint32_t AbilitySyncAckPacket::GetSchemaType() const +{ + return schemaType_; +} + +void AbilitySyncAckPacket::SetSecLabel(int32_t secLabel) +{ + secLabel_ = secLabel; +} + +int32_t AbilitySyncAckPacket::GetSecLabel() const +{ + return secLabel_; +} + +void AbilitySyncAckPacket::SetSecFlag(int32_t secFlag) +{ + secFlag_ = secFlag; +} + +int32_t AbilitySyncAckPacket::GetSecFlag() const +{ + return secFlag_; +} + +void AbilitySyncAckPacket::SetPermitSync(uint32_t permitSync) +{ + permitSync_ = permitSync; +} + +uint32_t AbilitySyncAckPacket::GetPermitSync() const +{ + return permitSync_; +} + +void AbilitySyncAckPacket::SetRequirePeerConvert(uint32_t requirePeerConvert) +{ + requirePeerConvert_ = requirePeerConvert; +} + +uint32_t AbilitySyncAckPacket::GetRequirePeerConvert() const +{ + return requirePeerConvert_; +} + +void AbilitySyncAckPacket::SetDbCreateTime(uint64_t dbCreateTime) +{ + dbCreateTime_ = dbCreateTime; +} + +uint64_t AbilitySyncAckPacket::GetDbCreateTime() const +{ + return dbCreateTime_; +} + +uint32_t AbilitySyncAckPacket::CalculateLen() const +{ + uint64_t len = 0; + len += Parcel::GetUInt32Len(); + len += Parcel::GetUInt32Len(); + len += Parcel::GetIntLen(); + uint32_t schemLen = Parcel::GetStringLen(schema_); + if (schemLen == 0) { + LOGE("[AbilitySyncAckPacket][CalculateLen] schemLen err!"); + return 0; + } + len += schemLen; + len += Parcel::GetIntLen(); // secLabel_ + len += Parcel::GetIntLen(); // secFlag_ + len += Parcel::GetUInt32Len(); // schemaType_ + len += Parcel::GetUInt32Len(); // permitSync_ + len += Parcel::GetUInt32Len(); // requirePeerConvert_ + len += Parcel::GetUInt64Len(); // dbCreateTime_ + len += DbAbility::CalculateLen(dbAbility_); // dbAbility_ + len += SchemaNegotiate::CalculateParcelLen(relationalSyncOpinion_); + if (len > INT32_MAX) { + LOGE("[AbilitySyncAckPacket][CalculateLen] err len:%" PRIu64, len); + return 0; + } + return len; +} + +DbAbility AbilitySyncAckPacket::GetDbAbility() const +{ + return dbAbility_; +} + +void AbilitySyncAckPacket::SetDbAbility(const DbAbility &dbAbility) +{ + dbAbility_ = dbAbility; +} + +void AbilitySyncAckPacket::SetRelationalSyncOpinion(const RelationalSyncOpinion &relationalSyncOpinion) +{ + relationalSyncOpinion_ = relationalSyncOpinion; +} + +RelationalSyncOpinion AbilitySyncAckPacket::GetRelationalSyncOpinion() const +{ + return relationalSyncOpinion_; +} + +AbilitySync::AbilitySync() + : communicator_(nullptr), + storageInterface_(nullptr), + metadata_(nullptr), + syncFinished_(false) +{ +} + +AbilitySync::~AbilitySync() +{ + communicator_ = nullptr; + storageInterface_ = nullptr; + metadata_ = nullptr; +} + +int AbilitySync::Initialize(ICommunicator *inCommunicator, ISyncInterface *inStorage, + std::shared_ptr &inMetadata, const std::string &deviceId) +{ + if (inCommunicator == nullptr || inStorage == nullptr || deviceId.empty() || inMetadata == nullptr) { + return -E_INVALID_ARGS; + } + communicator_ = inCommunicator; + storageInterface_ = inStorage; + metadata_ = inMetadata; + deviceId_ = deviceId; + return E_OK; +} + +int AbilitySync::SyncStart(uint32_t sessionId, uint32_t sequenceId, uint16_t remoteCommunicatorVersion, + const CommErrHandler &handler) +{ + AbilitySyncRequestPacket packet; + int errCode = SetAbilityRequestBodyInfo(packet, remoteCommunicatorVersion); + if (errCode != E_OK) { + return errCode; + } + Message *message = new (std::nothrow) Message(ABILITY_SYNC_MESSAGE); + if (message == nullptr) { + return -E_OUT_OF_MEMORY; + } + message->SetMessageType(TYPE_REQUEST); + errCode = message->SetCopiedObject<>(packet); + if (errCode != E_OK) { + LOGE("[AbilitySync][SyncStart] SetCopiedObject failed, err %d", errCode); + delete message; + message = nullptr; + return errCode; + } + message->SetVersion(MSG_VERSION_EXT); + message->SetSessionId(sessionId); + message->SetSequenceId(sequenceId); + SendConfig conf; + SetSendConfigParam(storageInterface_->GetDbProperties(), deviceId_, false, SEND_TIME_OUT, conf); + errCode = communicator_->SendMessage(deviceId_, message, conf, handler); + if (errCode != E_OK) { + LOGE("[AbilitySync][SyncStart] SendPacket failed, err %d", errCode); + delete message; + message = nullptr; + } + return errCode; +} + +int AbilitySync::AckRecv(const Message *message, ISyncTaskContext *context) +{ + int errCode = AckMsgCheck(message, context); + if (errCode != E_OK) { + return errCode; + } + const AbilitySyncAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + uint32_t remoteSoftwareVersion = packet->GetSoftwareVersion(); + context->SetRemoteSoftwareVersion(remoteSoftwareVersion); + if (remoteSoftwareVersion > SOFTWARE_VERSION_RELEASE_2_0) { + errCode = AckRecvWithHighVersion(message, context, packet); + } else { + std::string schema = packet->GetSchema(); + uint8_t schemaType = packet->GetSchemaType(); + bool isCompatible = static_cast(storageInterface_)->CheckCompatible(schema, schemaType); + if (!isCompatible) { + (static_cast(context))->SetTaskErrCode(-E_SCHEMA_MISMATCH); + LOGE("[AbilitySync][AckRecv] scheme check failed"); + return -E_SCHEMA_MISMATCH; + } + LOGI("[AbilitySync][AckRecv]remoteSoftwareVersion = %u, isCompatible = %d,", remoteSoftwareVersion, + isCompatible); + } + return errCode; +} + +int AbilitySync::RequestRecv(const Message *message, ISyncTaskContext *context) +{ + if (message == nullptr || context == nullptr) { + return -E_INVALID_ARGS; + } + const AbilitySyncRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + if (packet->GetSendCode() == -E_VERSION_NOT_SUPPORT) { + AbilitySyncAckPacket ackPacket; + (void)SendAck(message, -E_VERSION_NOT_SUPPORT, false, ackPacket); + LOGI("[AbilitySync][RequestRecv] version can not support, remote version is %u", packet->GetProtocolVersion()); + return -E_VERSION_NOT_SUPPORT; + } + + std::string schema = packet->GetSchema(); + uint8_t schemaType = packet->GetSchemaType(); + bool isCompatible = static_cast(storageInterface_)->CheckCompatible(schema, schemaType); + if (!isCompatible) { + (static_cast(context))->SetTaskErrCode(-E_SCHEMA_MISMATCH); + } + uint32_t remoteSoftwareVersion = packet->GetSoftwareVersion(); + context->SetRemoteSoftwareVersion(remoteSoftwareVersion); + return HandleRequestRecv(message, context, isCompatible); +} + +int AbilitySync::AckNotifyRecv(const Message *message, ISyncTaskContext *context) +{ + if (message == nullptr || context == nullptr) { + return -E_INVALID_ARGS; + } + if (message->GetErrorNo() == E_FEEDBACK_UNKNOWN_MESSAGE) { + LOGE("[AbilitySync][AckNotifyRecv] Remote device dose not support this message id"); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_EARLIEST); + return -E_FEEDBACK_UNKNOWN_MESSAGE; + } + const AbilitySyncAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = packet->GetAckCode(); + if (errCode != E_OK) { + LOGE("[AbilitySync][AckNotifyRecv] received an errCode %d", errCode); + return errCode; + } + std::string schema = packet->GetSchema(); + uint32_t remoteSoftwareVersion = packet->GetSoftwareVersion(); + context->SetRemoteSoftwareVersion(remoteSoftwareVersion); + AbilitySyncAckPacket sendPacket; + errCode = HandleVersionV3AckSchemaParam(packet, sendPacket, context, false); + int ackCode = errCode; + LOGI("[AckNotifyRecv] receive dev = %s ack notify, remoteSoftwareVersion = %u, ackCode = %d", + STR_MASK(deviceId_), remoteSoftwareVersion, errCode); + if (errCode == E_OK) { + (static_cast(context))->SetIsSchemaSync(true); + ackCode = AbilitySync::LAST_NOTIFY; + } + (void)SendAckWithEmptySchema(message, ackCode, true); + return errCode; +} + +bool AbilitySync::GetAbilitySyncFinishedStatus() const +{ + return syncFinished_; +} + +void AbilitySync::SetAbilitySyncFinishedStatus(bool syncFinished) +{ + syncFinished_ = syncFinished; +} + +bool AbilitySync::SecLabelCheck(const AbilitySyncRequestPacket *packet) const +{ + int32_t remoteSecLabel = packet->GetSecLabel(); + int32_t remoteSecFlag = packet->GetSecFlag(); + if (remoteSecLabel == NOT_SURPPORT_SEC_CLASSIFICATION || remoteSecLabel == SecurityLabel::NOT_SET) { + return true; + } + SecurityOption option; + int errCode = (static_cast(storageInterface_))->GetSecurityOption(option); + LOGI("[AbilitySync][RequestRecv] local l:%d, f:%d, errCode:%d", option.securityLabel, option.securityFlag, errCode); + if (errCode == -E_NOT_SUPPORT || option.securityLabel == SecurityLabel::NOT_SET) { + return true; + } + if (remoteSecLabel == FAILED_GET_SEC_CLASSIFICATION || errCode != E_OK) { + LOGE("[AbilitySync][RequestRecv] check error remoteL:%d, errCode:%d", remoteSecLabel, errCode); + return false; + } + if (remoteSecLabel == option.securityLabel) { + return true; + } else { + LOGE("[AbilitySync][RequestRecv] check error remote:%d , %d local:%d , %d", + remoteSecLabel, remoteSecFlag, option.securityLabel, option.securityFlag); + return false; + } +} + +void AbilitySync::HandleVersionV3RequestParam(const AbilitySyncRequestPacket *packet, ISyncTaskContext *context) const +{ + int32_t remoteSecLabel = packet->GetSecLabel(); + int32_t remoteSecFlag = packet->GetSecFlag(); + DbAbility remoteDbAbility = packet->GetDbAbility(); + (static_cast(context))->SetDbAbility(remoteDbAbility); + (static_cast(context))->SetRemoteSeccurityOption({remoteSecLabel, remoteSecFlag}); + (static_cast(context))->SetReceivcPermitCheck(false); + LOGI("[AbilitySync][HandleVersionV3RequestParam] remoteSecLabel = %d, remoteSecFlag = %d, remoteSchemaType = %u", + remoteSecLabel, remoteSecFlag, packet->GetSchemaType()); +} + +void AbilitySync::HandleVersionV3AckSecOptionParam(const AbilitySyncAckPacket *packet, + ISyncTaskContext *context) const +{ + int32_t remoteSecLabel = packet->GetSecLabel(); + int32_t remoteSecFlag = packet->GetSecFlag(); + SecurityOption secOption = {remoteSecLabel, remoteSecFlag}; + (static_cast(context))->SetRemoteSeccurityOption(secOption); + (static_cast(context))->SetSendPermitCheck(false); + LOGI("[AbilitySync][AckRecv] remoteSecLabel = %d, remoteSecFlag = %d", remoteSecLabel, remoteSecFlag); +} + +int AbilitySync::HandleVersionV3AckSchemaParam(const AbilitySyncAckPacket *recvPacket, + AbilitySyncAckPacket &sendPacket, ISyncTaskContext *context, bool sendOpinion) const +{ + if (IsSingleRelationalVer()) { + return HandleRelationAckSchemaParam(recvPacket, sendPacket, context, sendOpinion); + } + HandleKvAckSchemaParam(recvPacket, context, sendPacket); + return E_OK; +} + +void AbilitySync::GetPacketSecOption(SecurityOption &option) const +{ + int errCode = -E_NOT_SUPPORT; + if (IsSingleKvVer()) { + errCode = + (static_cast(storageInterface_))->GetSecurityOption(option); + } + if (errCode == -E_NOT_SUPPORT) { + LOGE("[AbilitySync][SyncStart] GetSecOpt not surpport sec classification"); + option.securityLabel = NOT_SURPPORT_SEC_CLASSIFICATION; + } else if (errCode != E_OK) { + LOGE("[AbilitySync][SyncStart] GetSecOpt errCode:%d", errCode); + option.securityLabel = FAILED_GET_SEC_CLASSIFICATION; + } +} + +int AbilitySync::RegisterTransformFunc() +{ + TransformFunc func; + func.computeFunc = std::bind(&AbilitySync::CalculateLen, std::placeholders::_1); + func.serializeFunc = std::bind(&AbilitySync::Serialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + func.deserializeFunc = std::bind(&AbilitySync::DeSerialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + return MessageTransform::RegTransformFunction(ABILITY_SYNC_MESSAGE, func); +} + +uint32_t AbilitySync::CalculateLen(const Message *inMsg) +{ + if ((inMsg == nullptr) || (inMsg->GetMessageId() != ABILITY_SYNC_MESSAGE)) { + return 0; + } + int errCode; + uint32_t len = 0; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + errCode = RequestPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + LOGE("[AbilitySync][CalculateLen] request packet calc length err %d", errCode); + } + break; + case TYPE_RESPONSE: + errCode = AckPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + LOGE("[AbilitySync][CalculateLen] ack packet calc length err %d", errCode); + } + break; + case TYPE_NOTIFY: + errCode = AckPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + LOGE("[AbilitySync][CalculateLen] ack packet calc length err %d", errCode); + } + break; + default: + LOGE("[AbilitySync][CalculateLen] message type not support, type %d", inMsg->GetMessageType()); + break; + } + return len; +} + +int AbilitySync::Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || (inMsg == nullptr)) { + return -E_INVALID_ARGS; + } + + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return RequestPacketSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckPacketSerialization(buffer, length, inMsg); + case TYPE_NOTIFY: + return AckPacketSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +int AbilitySync::DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || (inMsg == nullptr)) { + return -E_INVALID_ARGS; + } + + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return RequestPacketDeSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckPacketDeSerialization(buffer, length, inMsg); + case TYPE_NOTIFY: + return AckPacketDeSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +int AbilitySync::RequestPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + const AbilitySyncRequestPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + len = packet->CalculateLen(); + return E_OK; +} + +int AbilitySync::AckPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + const AbilitySyncAckPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + len = packet->CalculateLen(); + return E_OK; +} + +int AbilitySync::RequestPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + const AbilitySyncRequestPacket *packet = inMsg->GetObject(); + if ((packet == nullptr) || (length != packet->CalculateLen())) { + return -E_INVALID_ARGS; + } + + Parcel parcel(buffer, length); + parcel.WriteUInt32(packet->GetProtocolVersion()); + parcel.WriteInt(packet->GetSendCode()); + parcel.WriteUInt32(packet->GetSoftwareVersion()); + parcel.WriteString(packet->GetSchema()); + parcel.WriteInt(packet->GetSecLabel()); + parcel.WriteInt(packet->GetSecFlag()); + parcel.WriteUInt32(packet->GetSchemaType()); + parcel.WriteUInt64(packet->GetDbCreateTime()); + int errCode = DbAbility::Serialize(parcel, packet->GetDbAbility()); + if (parcel.IsError() || errCode != E_OK) { + return -E_PARSE_FAIL; + } + return E_OK; +} + +int AbilitySync::AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + const AbilitySyncAckPacket *packet = inMsg->GetObject(); + if ((packet == nullptr) || (length != packet->CalculateLen())) { + return -E_INVALID_ARGS; + } + + Parcel parcel(buffer, length); + parcel.WriteUInt32(ABILITY_SYNC_VERSION_V1); + parcel.WriteUInt32(SOFTWARE_VERSION_CURRENT); + parcel.WriteInt(packet->GetAckCode()); + parcel.WriteString(packet->GetSchema()); + parcel.WriteInt(packet->GetSecLabel()); + parcel.WriteInt(packet->GetSecFlag()); + parcel.WriteUInt32(packet->GetSchemaType()); + parcel.WriteUInt32(packet->GetPermitSync()); + parcel.WriteUInt32(packet->GetRequirePeerConvert()); + parcel.WriteUInt64(packet->GetDbCreateTime()); + int errCode = DbAbility::Serialize(parcel, packet->GetDbAbility()); + if (parcel.IsError() || errCode != E_OK) { + return -E_PARSE_FAIL; + } + errCode = SchemaNegotiate::SerializeData(packet->GetRelationalSyncOpinion(), parcel); + if (parcel.IsError() || errCode != E_OK) { + return -E_PARSE_FAIL; + } + return E_OK; +} + +int AbilitySync::RequestPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + auto *packet = new (std::nothrow) AbilitySyncRequestPacket(); + if (packet == nullptr) { + return -E_OUT_OF_MEMORY; + } + + Parcel parcel(const_cast(buffer), length); + uint32_t version = 0; + uint32_t softwareVersion = 0; + std::string schema; + int32_t sendCode = 0; + int errCode = -E_PARSE_FAIL; + + parcel.ReadUInt32(version); + if (parcel.IsError()) { + goto ERROR_OUT; + } + packet->SetProtocolVersion(version); + if (version > ABILITY_SYNC_VERSION_V1) { + packet->SetSendCode(-E_VERSION_NOT_SUPPORT); + errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + goto ERROR_OUT; + } + return errCode; + } + parcel.ReadInt(sendCode); + parcel.ReadUInt32(softwareVersion); + parcel.ReadString(schema); + errCode = RequestPacketDeSerializationTailPart(parcel, packet, softwareVersion); + if (parcel.IsError() || errCode != E_OK) { + goto ERROR_OUT; + } + packet->SetSendCode(sendCode); + packet->SetSoftwareVersion(softwareVersion); + packet->SetSchema(schema); + + errCode = inMsg->SetExternalObject<>(packet); + if (errCode == E_OK) { + return E_OK; + } + +ERROR_OUT: + delete packet; + packet = nullptr; + return errCode; +} + +int AbilitySync::RequestPacketDeSerializationTailPart(Parcel &parcel, AbilitySyncRequestPacket *packet, + uint32_t version) +{ + if (!parcel.IsError() && version > SOFTWARE_VERSION_RELEASE_2_0) { + int32_t secLabel = 0; + int32_t secFlag = 0; + uint32_t schemaType = 0; + parcel.ReadInt(secLabel); + parcel.ReadInt(secFlag); + parcel.ReadUInt32(schemaType); + packet->SetSecLabel(secLabel); + packet->SetSecFlag(secFlag); + packet->SetSchemaType(schemaType); + } + if (!parcel.IsError() && version > SOFTWARE_VERSION_RELEASE_3_0) { + uint64_t dbCreateTime = 0; + parcel.ReadUInt64(dbCreateTime); + packet->SetDbCreateTime(dbCreateTime); + } + DbAbility remoteDbAbility; + int errCode = DbAbility::DeSerialize(parcel, remoteDbAbility); + if (errCode != E_OK) { + LOGE("[AbilitySync] request packet DeSerializ failed."); + return errCode; + } + packet->SetDbAbility(remoteDbAbility); + return E_OK; +} + +int AbilitySync::AckPacketDeSerializationTailPart(Parcel &parcel, AbilitySyncAckPacket *packet, uint32_t version) +{ + if (!parcel.IsError() && version > SOFTWARE_VERSION_RELEASE_2_0) { + int32_t secLabel = 0; + int32_t secFlag = 0; + uint32_t schemaType = 0; + uint32_t permitSync = 0; + uint32_t requirePeerConvert = 0; + parcel.ReadInt(secLabel); + parcel.ReadInt(secFlag); + parcel.ReadUInt32(schemaType); + parcel.ReadUInt32(permitSync); + parcel.ReadUInt32(requirePeerConvert); + packet->SetSecLabel(secLabel); + packet->SetSecFlag(secFlag); + packet->SetSchemaType(schemaType); + packet->SetPermitSync(permitSync); + packet->SetRequirePeerConvert(requirePeerConvert); + } + if (!parcel.IsError() && version > SOFTWARE_VERSION_RELEASE_3_0) { + uint64_t dbCreateTime = 0; + parcel.ReadUInt64(dbCreateTime); + packet->SetDbCreateTime(dbCreateTime); + } + DbAbility remoteDbAbility; + int errCode = DbAbility::DeSerialize(parcel, remoteDbAbility); + if (errCode != E_OK) { + LOGE("[AbilitySync] ack packet DeSerializ failed."); + return errCode; + } + packet->SetDbAbility(remoteDbAbility); + RelationalSyncOpinion relationalSyncOpinion; + errCode = SchemaNegotiate::DeserializeData(parcel, relationalSyncOpinion); + if (errCode != E_OK) { + LOGE("[AbilitySync] ack packet DeSerializ RelationalSyncOpinion failed."); + return errCode; + } + packet->SetRelationalSyncOpinion(relationalSyncOpinion); + return E_OK; +} + +int AbilitySync::AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + auto *packet = new (std::nothrow) AbilitySyncAckPacket(); + if (packet == nullptr) { + return -E_OUT_OF_MEMORY; + } + + Parcel parcel(const_cast(buffer), length); + uint32_t version = 0; + uint32_t softwareVersion = 0; + int32_t ackCode = E_OK; + std::string schema; + int errCode; + parcel.ReadUInt32(version); + if (parcel.IsError()) { + LOGE("[AbilitySync][RequestDeSerialization] read version failed!"); + errCode = -E_PARSE_FAIL; + goto ERROR_OUT; + } + packet->SetProtocolVersion(version); + if (version > ABILITY_SYNC_VERSION_V1) { + packet->SetAckCode(-E_VERSION_NOT_SUPPORT); + errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + goto ERROR_OUT; + } + return errCode; + } + parcel.ReadUInt32(softwareVersion); + parcel.ReadInt(ackCode); + parcel.ReadString(schema); + errCode = AckPacketDeSerializationTailPart(parcel, packet, softwareVersion); + if (parcel.IsError() || errCode != E_OK) { + LOGE("[AbilitySync][RequestDeSerialization] DeSerialization failed!"); + errCode = -E_PARSE_FAIL; + goto ERROR_OUT; + } + packet->SetSoftwareVersion(softwareVersion); + packet->SetAckCode(ackCode); + packet->SetSchema(schema); + errCode = inMsg->SetExternalObject<>(packet); + if (errCode == E_OK) { + return E_OK; + } + +ERROR_OUT: + delete packet; + packet = nullptr; + return errCode; +} + +int AbilitySync::SetAbilityRequestBodyInfo(AbilitySyncRequestPacket &packet, uint16_t remoteCommunicatorVersion) const +{ + uint64_t dbCreateTime; + int errCode = + (static_cast(storageInterface_))->GetDatabaseCreateTimestamp(dbCreateTime); + if (errCode != E_OK) { + LOGE("[AbilitySync][FillAbilityRequest] GetDatabaseCreateTimestamp failed, err %d", errCode); + return errCode; + } + SecurityOption option; + GetPacketSecOption(option); + std::string schemaStr; + uint32_t schemaType = 0; + if (IsSingleKvVer()) { + SchemaObject schemaObj = (static_cast(storageInterface_))->GetSchemaInfo(); + schemaStr = schemaObj.ToSchemaString(); + schemaType = static_cast(schemaObj.GetSchemaType()); + } else if (IsSingleRelationalVer()) { + auto schemaObj = (static_cast(storageInterface_))->GetSchemaInfo(); + schemaStr = schemaObj.ToSchemaString(); + schemaType = static_cast(schemaObj.GetSchemaType()); + } + DbAbility dbAbility; + errCode = GetDbAbilityInfo(dbAbility); + if (errCode != E_OK) { + LOGE("[AbilitySync][FillAbilityRequest] GetDbAbility failed, err %d", errCode); + return errCode; + } + // 102 version is forbidden to sync with 103 json-schema or flatbuffer-schema + // so schema should put null string while remote is 102 version to avoid this bug. + if (remoteCommunicatorVersion == 1) { + packet.SetSchema(""); + packet.SetSchemaType(0); + } else { + packet.SetSchema(schemaStr); + packet.SetSchemaType(schemaType); + } + packet.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + packet.SetSecLabel(option.securityLabel); + packet.SetSecFlag(option.securityFlag); + packet.SetDbCreateTime(dbCreateTime); + packet.SetDbAbility(dbAbility); + LOGI("[AbilitySync][FillRequest] ver=%u,Lab=%d,Flag=%d,dbCreateTime=%" PRId64, SOFTWARE_VERSION_CURRENT, + option.securityLabel, option.securityFlag, dbCreateTime); + return E_OK; +} + +int AbilitySync::SetAbilityAckBodyInfo(AbilitySyncAckPacket &ackPacket, int ackCode, bool isAckNotify) const +{ + int errCode = E_OK; + ackPacket.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + ackPacket.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + if (!isAckNotify) { + SecurityOption option; + GetPacketSecOption(option); + ackPacket.SetSecLabel(option.securityLabel); + ackPacket.SetSecFlag(option.securityFlag); + uint64_t dbCreateTime = 0; + errCode = + (static_cast(storageInterface_))->GetDatabaseCreateTimestamp(dbCreateTime); + if (errCode != E_OK) { + LOGE("[AbilitySync][SyncStart] GetDatabaseCreateTimestamp failed, err %d", errCode); + ackCode = errCode; + } + DbAbility dbAbility; + errCode = GetDbAbilityInfo(dbAbility); + if (errCode != E_OK) { + LOGE("[AbilitySync][FillAbilityRequest] GetDbAbility failed, err %d", errCode); + return errCode; + } + ackPacket.SetDbCreateTime(dbCreateTime); + ackPacket.SetDbAbility(dbAbility); + } + ackPacket.SetAckCode(ackCode); + return E_OK; +} + +void AbilitySync::SetAbilityAckSchemaInfo(AbilitySyncAckPacket &ackPacket, const ISchema &schemaObj) const +{ + ackPacket.SetSchema(schemaObj.ToSchemaString()); + ackPacket.SetSchemaType(static_cast(schemaObj.GetSchemaType())); +} + +void AbilitySync::SetAbilityAckSyncOpinionInfo(AbilitySyncAckPacket &ackPacket, SyncOpinion localOpinion) const +{ + ackPacket.SetPermitSync(localOpinion.permitSync); + ackPacket.SetRequirePeerConvert(localOpinion.requirePeerConvert); +} + +int AbilitySync::GetDbAbilityInfo(DbAbility &dbAbility) const +{ + int errCode = E_OK; + for (const auto &item : SyncConfig::ABILITYBITS) { + errCode = dbAbility.SetAbilityItem(item, SUPPORT_MARK); + if (errCode != E_OK) { + return errCode; + } + } + return errCode; +} + +int AbilitySync::AckMsgCheck(const Message *message, ISyncTaskContext *context) const +{ + if (message == nullptr || context == nullptr) { + return -E_INVALID_ARGS; + } + if (message->GetErrorNo() == E_FEEDBACK_UNKNOWN_MESSAGE) { + LOGE("[AbilitySync][AckMsgCheck] Remote device dose not support this message id"); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_EARLIEST); + context->SetTaskErrCode(-E_FEEDBACK_UNKNOWN_MESSAGE); + return -E_FEEDBACK_UNKNOWN_MESSAGE; + } + if (message->GetErrorNo() == E_FEEDBACK_COMMUNICATOR_NOT_FOUND) { + LOGE("[AbilitySync][AckMsgCheck] Remote db is closed"); + context->SetTaskErrCode(-E_FEEDBACK_COMMUNICATOR_NOT_FOUND); + return -E_FEEDBACK_COMMUNICATOR_NOT_FOUND; + } + const AbilitySyncAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + int ackCode = packet->GetAckCode(); + if (ackCode != E_OK) { + LOGE("[AbilitySync][AckMsgCheck] received an errCode %d", ackCode); + context->SetTaskErrCode(ackCode); + return ackCode; + } + return E_OK; +} + +bool AbilitySync::IsSingleKvVer() const +{ + return storageInterface_->GetInterfaceType() == ISyncInterface::SYNC_SVD; +} +bool AbilitySync::IsSingleRelationalVer() const +{ +#ifdef RELATIONAL_STORE + return storageInterface_->GetInterfaceType() == ISyncInterface::SYNC_RELATION; +#elif + return false; +#endif +} + +int AbilitySync::HandleRequestRecv(const Message *message, ISyncTaskContext *context, bool isCompatible) +{ + const AbilitySyncRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + uint32_t remoteSoftwareVersion = packet->GetSoftwareVersion(); + int ackCode; + std::string schema = packet->GetSchema(); + if (remoteSoftwareVersion <= SOFTWARE_VERSION_RELEASE_2_0) { + LOGI("[AbilitySync][RequestRecv] remote version = %u, CheckSchemaCompatible = %d", + remoteSoftwareVersion, isCompatible); + return SendAckWithEmptySchema(message, E_OK, false); + } + HandleVersionV3RequestParam(packet, context); + if (SecLabelCheck(packet)) { + ackCode = E_OK; + } else { + ackCode = -E_SECURITY_OPTION_CHECK_ERROR; + } + if (ackCode == E_OK && remoteSoftwareVersion > SOFTWARE_VERSION_RELEASE_3_0) { + ackCode = metadata_->SetDbCreateTime(deviceId_, packet->GetDbCreateTime(), true); + } + AbilitySyncAckPacket ackPacket; + if (IsSingleRelationalVer()) { + ackPacket.SetRelationalSyncOpinion(MakeRelationSyncOpnion(packet, schema)); + } else { + SetAbilityAckSyncOpinionInfo(ackPacket, MakeKvSyncOpnion(packet, schema)); + } + LOGI("[AbilitySync][RequestRecv] remote dev=%s,ver=%u,schemaCompatible=%d", STR_MASK(deviceId_), + remoteSoftwareVersion, isCompatible); + return SendAck(message, ackCode, false, ackPacket); +} + +int AbilitySync::SendAck(const Message *message, int ackCode, bool isAckNotify, AbilitySyncAckPacket &ackPacket) +{ + int errCode = SetAbilityAckBodyInfo(ackPacket, ackCode, isAckNotify); + if (errCode != E_OK) { + return errCode; + } + if (IsSingleRelationalVer()) { + auto schemaObj = (static_cast(storageInterface_))->GetSchemaInfo(); + SetAbilityAckSchemaInfo(ackPacket, schemaObj); + } else if (IsSingleKvVer()) { + SchemaObject schemaObject = static_cast(storageInterface_)->GetSchemaInfo(); + SetAbilityAckSchemaInfo(ackPacket, schemaObject); + } + return SendAck(message, ackPacket, isAckNotify); +} + +int AbilitySync::SendAckWithEmptySchema(const Message *message, int ackCode, + bool isAckNotify) +{ + AbilitySyncAckPacket ackPacket; + int errCode = SetAbilityAckBodyInfo(ackPacket, ackCode, isAckNotify); + if (errCode != E_OK) { + return errCode; + } + SetAbilityAckSchemaInfo(ackPacket, SchemaObject()); + return SendAck(message, ackPacket, isAckNotify); +} + +int AbilitySync::SendAck(const Message *inMsg, const AbilitySyncAckPacket &ackPacket, bool isAckNotify) +{ + Message *ackMessage = new (std::nothrow) Message(ABILITY_SYNC_MESSAGE); + if (ackMessage == nullptr) { + LOGE("[AbilitySync][SendAck] message create failed, may be memleak!"); + return -E_OUT_OF_MEMORY; + } + int errCode = ackMessage->SetCopiedObject<>(ackPacket); + if (errCode != E_OK) { + LOGE("[AbilitySync][SendAck] SetCopiedObject failed, err %d", errCode); + delete ackMessage; + ackMessage = nullptr; + return errCode; + } + (!isAckNotify) ? ackMessage->SetMessageType(TYPE_RESPONSE) : ackMessage->SetMessageType(TYPE_NOTIFY); + ackMessage->SetTarget(deviceId_); + ackMessage->SetSessionId(inMsg->GetSessionId()); + ackMessage->SetSequenceId(inMsg->GetSequenceId()); + SendConfig conf; + SetSendConfigParam(storageInterface_->GetDbProperties(), deviceId_, false, SEND_TIME_OUT, conf); + errCode = communicator_->SendMessage(deviceId_, ackMessage, conf); + if (errCode != E_OK) { + LOGE("[AbilitySync][SendAck] SendPacket failed, err %d", errCode); + delete ackMessage; + ackMessage = nullptr; + } + return errCode; +} + +SyncOpinion AbilitySync::MakeKvSyncOpnion(const AbilitySyncRequestPacket *packet, const std::string &remoteSchema) const +{ + uint8_t remoteSchemaType = packet->GetSchemaType(); + SchemaObject localSchema = (static_cast(storageInterface_))->GetSchemaInfo(); + SyncOpinion localSyncOpinion = SchemaNegotiate::MakeLocalSyncOpinion(localSchema, remoteSchema, remoteSchemaType); + return localSyncOpinion; +} + +RelationalSyncOpinion AbilitySync::MakeRelationSyncOpnion(const AbilitySyncRequestPacket *packet, + const std::string &remoteSchema) const +{ + uint8_t remoteSchemaType = packet->GetSchemaType(); + RelationalSchemaObject localSchema = (static_cast(storageInterface_))->GetSchemaInfo(); + return SchemaNegotiate::MakeLocalSyncOpinion(localSchema, remoteSchema, remoteSchemaType); +} + +void AbilitySync::HandleKvAckSchemaParam(const AbilitySyncAckPacket *recvPacket, + ISyncTaskContext *context, AbilitySyncAckPacket &sendPacket) const +{ + std::string remoteSchema = recvPacket->GetSchema(); + uint8_t remoteSchemaType = recvPacket->GetSchemaType(); + bool permitSync = static_cast(recvPacket->GetPermitSync()); + bool requirePeerConvert = static_cast(recvPacket->GetRequirePeerConvert()); + SyncOpinion remoteOpinion = {permitSync, requirePeerConvert, true}; + SchemaObject localSchema = (static_cast(storageInterface_))->GetSchemaInfo(); + SyncOpinion syncOpinion = SchemaNegotiate::MakeLocalSyncOpinion(localSchema, remoteSchema, remoteSchemaType); + SyncStrategy localStrategy = SchemaNegotiate::ConcludeSyncStrategy(syncOpinion, remoteOpinion); + SetAbilityAckSyncOpinionInfo(sendPacket, syncOpinion); + (static_cast(context))->SetSyncStrategy(localStrategy); +} + +int AbilitySync::HandleRelationAckSchemaParam(const AbilitySyncAckPacket *recvPacket, AbilitySyncAckPacket &sendPacket, + ISyncTaskContext *context, bool sendOpinion) const +{ + std::string remoteSchema = recvPacket->GetSchema(); + uint8_t remoteSchemaType = recvPacket->GetSchemaType(); + auto localSchema = (static_cast(storageInterface_))->GetSchemaInfo(); + auto localOpinion = SchemaNegotiate::MakeLocalSyncOpinion(localSchema, remoteSchema, remoteSchemaType); + auto localStrategy = SchemaNegotiate::ConcludeSyncStrategy(localOpinion, + recvPacket->GetRelationalSyncOpinion()); + (static_cast(context))->SetRelationalSyncStrategy(localStrategy); + int errCode = (static_cast(storageInterface_))-> + CreateDistributedDeviceTable(context->GetDeviceId(), localStrategy); + if (errCode != E_OK) { + LOGE("[AbilitySync][AckRecv] create distributed device table failed,errCode=%d", errCode); + } + if (sendOpinion) { + sendPacket.SetRelationalSyncOpinion(localOpinion); + } + return errCode; +} + +int AbilitySync::AckRecvWithHighVersion(const Message *message, ISyncTaskContext *context, + const AbilitySyncAckPacket *packet) +{ + HandleVersionV3AckSecOptionParam(packet, context); + AbilitySyncAckPacket ackPacket; + int errCode = HandleVersionV3AckSchemaParam(packet, ackPacket, context, true); + if (errCode != E_OK) { + return errCode; + } + auto singleVerContext = static_cast(context); + auto query = singleVerContext->GetQuery(); + bool permitSync = (singleVerContext->GetSyncStrategy(query)).permitSync; + if (!permitSync) { + (static_cast(context))->SetTaskErrCode(-E_SCHEMA_MISMATCH); + LOGE("[AbilitySync][AckRecv] scheme check failed"); + return -E_SCHEMA_MISMATCH; + } + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_3_0) { + errCode = metadata_->SetDbCreateTime(deviceId_, packet->GetDbCreateTime(), true); + if (errCode != E_OK) { + LOGE("[AbilitySync][AckRecv] set db create time failed,errCode=%d", errCode); + context->SetTaskErrCode(errCode); + return errCode; + } + } + DbAbility remoteDbAbility = packet->GetDbAbility(); + (static_cast(context))->SetDbAbility(remoteDbAbility); + (void)SendAck(message, AbilitySync::CHECK_SUCCESS, true, ackPacket); + (static_cast(context))->SetIsSchemaSync(true); + return E_OK; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/ability_sync.h b/mock/distributeddb/syncer/src/ability_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..05f0465ff8c023e569f5418e605883d40279343f --- /dev/null +++ b/mock/distributeddb/syncer/src/ability_sync.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ABILITY_SYNC_H +#define ABILITY_SYNC_H + +#include +#include + +#include "db_ability.h" +#include "icommunicator.h" +#include "ikvdb_sync_interface.h" +#include "isync_task_context.h" +#include "parcel.h" +#ifdef RELATIONAL_STORE +#include "ischema.h" +#endif +#include "schema_negotiate.h" + +namespace DistributedDB { +class AbilitySyncRequestPacket { +public: + AbilitySyncRequestPacket(); + ~AbilitySyncRequestPacket(); + + void SetProtocolVersion(uint32_t protocolVersion); + uint32_t GetProtocolVersion() const; + + void SetSendCode(int32_t sendCode); + int32_t GetSendCode() const; + + void SetSoftwareVersion(uint32_t swVersion); + uint32_t GetSoftwareVersion() const; + + void SetSchema(const std::string &schema); + std::string GetSchema() const; + + void SetSchemaType(uint32_t schemaType); + uint32_t GetSchemaType() const; + + void SetSecLabel(int32_t secLabel); + int32_t GetSecLabel() const; + + void SetSecFlag(int32_t secFlag); + int32_t GetSecFlag() const; + + void SetDbCreateTime(uint64_t dbCreateTime); + uint64_t GetDbCreateTime() const; + + uint32_t CalculateLen() const; + + DbAbility GetDbAbility() const; + + void SetDbAbility(const DbAbility &dbAbility); + +private: + uint32_t protocolVersion_; + int32_t sendCode_; + uint32_t softwareVersion_; + std::string schema_; + int32_t secLabel_; + int32_t secFlag_; + uint32_t schemaType_; + uint64_t dbCreateTime_; + DbAbility dbAbility_; +}; + +class AbilitySyncAckPacket { +public: + AbilitySyncAckPacket(); + ~AbilitySyncAckPacket(); + + void SetProtocolVersion(uint32_t protocolVersion); + uint32_t GetProtocolVersion() const; + + void SetSoftwareVersion(uint32_t swVersion); + uint32_t GetSoftwareVersion() const; + + void SetAckCode(int32_t ackCode); + int32_t GetAckCode() const; + + void SetSchema(const std::string &schema); + std::string GetSchema() const; + + void SetSchemaType(uint32_t schemaType); + uint32_t GetSchemaType() const; + + void SetSecLabel(int32_t secLabel); + int32_t GetSecLabel() const; + + void SetSecFlag(int32_t secFlag); + int32_t GetSecFlag() const; + + void SetPermitSync(uint32_t permitSync); + uint32_t GetPermitSync() const; + + void SetRequirePeerConvert(uint32_t requirePeerConvert); + uint32_t GetRequirePeerConvert() const; + + void SetDbCreateTime(uint64_t dbCreateTime); + uint64_t GetDbCreateTime() const; + + uint32_t CalculateLen() const; + + DbAbility GetDbAbility() const; + + void SetDbAbility(const DbAbility &dbAbility); + + void SetRelationalSyncOpinion(const RelationalSyncOpinion &relationalSyncOpinion); + + RelationalSyncOpinion GetRelationalSyncOpinion() const; + +private: + uint32_t protocolVersion_; + uint32_t softwareVersion_; + int32_t ackCode_; + std::string schema_; + int32_t secLabel_; + int32_t secFlag_; + uint32_t schemaType_; + uint32_t permitSync_; + uint32_t requirePeerConvert_; + uint64_t dbCreateTime_; + DbAbility dbAbility_; + RelationalSyncOpinion relationalSyncOpinion_; +}; + +class AbilitySync { +public: + static const int CHECK_SUCCESS = 0; + static const int LAST_NOTIFY = 0xfe; + AbilitySync(); + ~AbilitySync(); + + // Start Ability Sync + int SyncStart(uint32_t sessionId, uint32_t sequenceId, uint16_t remoteCommunicatorVersion, + const CommErrHandler &handler = nullptr); + + int Initialize(ICommunicator *inCommunicator, ISyncInterface *inStorage, std::shared_ptr &inMetadata, + const std::string &deviceId); + + int AckRecv(const Message *message, ISyncTaskContext *context); + + int RequestRecv(const Message *message, ISyncTaskContext *context); + + int AckNotifyRecv(const Message *message, ISyncTaskContext *context); + + bool GetAbilitySyncFinishedStatus() const; + + void SetAbilitySyncFinishedStatus(bool syncFinished); + + static int RegisterTransformFunc(); + + static uint32_t CalculateLen(const Message *inMsg); + + static int Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg); // register to communicator + + static int DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); // register to communicator + +private: + + static int RequestPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int RequestPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static int AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static int RequestPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int AckPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int RequestPacketDeSerializationTailPart(Parcel &parcel, AbilitySyncRequestPacket *packet, + uint32_t version); + + static int AckPacketDeSerializationTailPart(Parcel &parcel, AbilitySyncAckPacket *packet, uint32_t version); + + bool SecLabelCheck(const AbilitySyncRequestPacket *packet) const; + + void HandleVersionV3RequestParam(const AbilitySyncRequestPacket *packet, ISyncTaskContext *context) const; + + void HandleVersionV3AckSecOptionParam(const AbilitySyncAckPacket *packet, + ISyncTaskContext *context) const; + + int HandleVersionV3AckSchemaParam(const AbilitySyncAckPacket *recvPacket, + AbilitySyncAckPacket &sendPacket, ISyncTaskContext *context, bool sendOpinion) const; + + void HandleKvAckSchemaParam(const AbilitySyncAckPacket *recvPacket, + ISyncTaskContext *context, AbilitySyncAckPacket &sendPacket) const; + + int HandleRelationAckSchemaParam(const AbilitySyncAckPacket *recvPacket, + AbilitySyncAckPacket &sendPacket, ISyncTaskContext *context, bool sendOpinion) const; + + void GetPacketSecOption(SecurityOption &option) const; + + int SetAbilityRequestBodyInfo(AbilitySyncRequestPacket &packet, uint16_t remoteCommunicatorVersion) const; + + int SetAbilityAckBodyInfo(AbilitySyncAckPacket &ackPacket, int ackCode, bool isAckNotify) const; + + void SetAbilityAckSchemaInfo(AbilitySyncAckPacket &ackPacket, const ISchema &schemaObj) const; + + void SetAbilityAckSyncOpinionInfo(AbilitySyncAckPacket &ackPacket, SyncOpinion localOpinion) const; + + int GetDbAbilityInfo(DbAbility &dbAbility) const; + + int AckMsgCheck(const Message *message, ISyncTaskContext *context) const; + + bool IsSingleKvVer() const; + + bool IsSingleRelationalVer() const; + + int SendAck(const Message *message, int ackCode, bool isAckNotify, AbilitySyncAckPacket &ackPacket); + + int SendAckWithEmptySchema(const Message *message, int ackCode, bool isAckNotify); + + int SendAck(const Message *message, const AbilitySyncAckPacket &ackPacket, bool isAckNotify); + + int HandleRequestRecv(const Message *message, ISyncTaskContext *context, bool isCompatible); + + SyncOpinion MakeKvSyncOpnion(const AbilitySyncRequestPacket *packet, const std::string &remoteSchema) const; + + RelationalSyncOpinion MakeRelationSyncOpnion(const AbilitySyncRequestPacket *packet, + const std::string &remoteSchema) const; + + int AckRecvWithHighVersion(const Message *message, ISyncTaskContext *context, const AbilitySyncAckPacket *packet); + + ICommunicator *communicator_; + ISyncInterface *storageInterface_; + std::shared_ptr metadata_; + std::string deviceId_; + bool syncFinished_; + static const int FAILED_GET_SEC_CLASSIFICATION = 0x55; +}; +} // namespace DistributedDB +#endif // ABILITY_SYNC_H diff --git a/mock/distributeddb/syncer/src/commit_history_sync.cpp b/mock/distributeddb/syncer/src/commit_history_sync.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb5f3a16c8314cb2c2ec6572c838f51a9365cdca --- /dev/null +++ b/mock/distributeddb/syncer/src/commit_history_sync.cpp @@ -0,0 +1,717 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "commit_history_sync.h" + +#include "sync_engine.h" +#include "parcel.h" +#include "log_print.h" +#include "message_transform.h" +#include "performance_analysis.h" +#include "db_constant.h" + +namespace DistributedDB { +// Class CommitHistorySyncRequestPacket +uint32_t CommitHistorySyncRequestPacket::CalculateLen() const +{ + uint64_t len = Parcel::GetUInt64Len(); + // commitMap len + for (const auto &iter : commitMap_) { + len += Parcel::GetStringLen(iter.first); + len += Parcel::GetMultiVerCommitLen(iter.second); + if (len > INT32_MAX) { + return 0; + } + } + len += Parcel::GetUInt32Len(); // version + len += Parcel::GetVectorLen(reserved_); // reserved + len = Parcel::GetEightByteAlign(len); + if (len > INT32_MAX) { + return 0; + } + return len; +} + +void CommitHistorySyncRequestPacket::SetCommitMap(std::map &inMap) +{ + commitMap_ = std::move(inMap); +} + +void CommitHistorySyncRequestPacket::GetCommitMap(std::map &outMap) const +{ + outMap = commitMap_; +} + +void CommitHistorySyncRequestPacket::SetVersion(uint32_t version) +{ + version_ = version; +} + +uint32_t CommitHistorySyncRequestPacket::GetVersion() const +{ + return version_; +} + +void CommitHistorySyncRequestPacket::SetReserved(std::vector &reserved) +{ + reserved_ = std::move(reserved); +} + +std::vector CommitHistorySyncRequestPacket::GetReserved() const +{ + return reserved_; +} + +uint32_t CommitHistorySyncAckPacket::CalculateLen() const +{ + uint64_t len = Parcel::GetIntLen(); // errCode + len += Parcel::GetUInt32Len(); // version + len = Parcel::GetEightByteAlign(len); + + // commits vector len + len += Parcel::GetMultiVerCommitsLen(commits_); + len += Parcel::GetVectorLen(reserved_); // reserved + len = Parcel::GetEightByteAlign(len); + if (len > INT32_MAX) { + return 0; + } + return len; +} + +void CommitHistorySyncAckPacket::SetData(std::vector &inData) +{ + commits_ = std::move(inData); +} + +void CommitHistorySyncAckPacket::GetData(std::vector &outData) const +{ + outData = commits_; +} + +void CommitHistorySyncAckPacket::SetErrorCode(int32_t errCode) +{ + errorCode_ = errCode; +} + +void CommitHistorySyncAckPacket::GetErrorCode(int32_t &errCode) const +{ + errCode = errorCode_; +} + +void CommitHistorySyncAckPacket::SetVersion(uint32_t version) +{ + version_ = version; +} + +uint32_t CommitHistorySyncAckPacket::GetVersion() const +{ + return version_; +} + +void CommitHistorySyncAckPacket::SetReserved(std::vector &reserved) +{ + reserved_ = std::move(reserved); +} + +std::vector CommitHistorySyncAckPacket::GetReserved() const +{ + return reserved_; +} + +// Class CommitHistorySync +CommitHistorySync::~CommitHistorySync() +{ + storagePtr_ = nullptr; + communicateHandle_ = nullptr; +} + +int CommitHistorySync::Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return -E_MESSAGE_ID_ERROR; + } + + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return RequestPacketSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckPacketSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +int CommitHistorySync::DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return -E_MESSAGE_ID_ERROR; + } + + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return RequestPacketDeSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckPacketDeSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +uint32_t CommitHistorySync::CalculateLen(const Message *inMsg) +{ + if (!(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return 0; + } + + uint32_t len = 0; + int errCode = E_OK; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + errCode = RequestPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + return 0; + } + return len; + case TYPE_RESPONSE: + errCode = AckPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + return 0; + } + return len; + default: + return 0; + } +} + +int CommitHistorySync::RegisterTransformFunc() +{ + TransformFunc func; + func.computeFunc = std::bind(&CommitHistorySync::CalculateLen, std::placeholders::_1); + func.serializeFunc = std::bind(&CommitHistorySync::Serialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + func.deserializeFunc = std::bind(&CommitHistorySync::DeSerialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + return MessageTransform::RegTransformFunction(COMMIT_HISTORY_SYNC_MESSAGE, func); +} + +int CommitHistorySync::Initialize(MultiVerKvDBSyncInterface *storagePtr, ICommunicator *communicateHandle) +{ + if ((storagePtr == nullptr) || (communicateHandle == nullptr)) { + return -E_INVALID_ARGS; + } + storagePtr_ = storagePtr; + communicateHandle_ = communicateHandle; + return E_OK; +} + +void CommitHistorySync::TimeOutCallback(MultiVerSyncTaskContext *context, const Message *message) const +{ + (void)context; + (void)message; + return; +} + +int CommitHistorySync::SyncStart(MultiVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_GET_DEVICE_LATEST_COMMIT); + } + std::map commitMap; + int errCode = GetDeviceLatestCommit(commitMap); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_GET_DEVICE_LATEST_COMMIT); + } + if ((errCode != E_OK) && (errCode != -E_NOT_FOUND)) { + return errCode; + } + + LOGD("CommitHistorySync::commitMap size = %zu, dst=%s{private}", commitMap.size(), context->GetDeviceId().c_str()); + return SendRequestPacket(context, commitMap); +} + +int CommitHistorySync::RequestRecvCallback(const MultiVerSyncTaskContext *context, const Message *message) +{ + if (!IsPacketValid(message, TYPE_REQUEST) || context == nullptr) { + return -E_INVALID_ARGS; + } + const CommitHistorySyncRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + std::vector commits; + int errCode = RunPermissionCheck(context->GetDeviceId()); + if (errCode == -E_NOT_PERMIT) { + LOGE("CommitHistorySync::RequestRecvCallback RunPermissionCheck not pass"); + SendAckPacket(context, commits, errCode, message); + return errCode; + } + std::map commitMap; + packet->GetCommitMap(commitMap); + uint32_t ver = packet->GetVersion(); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_GET_COMMIT_TREE); + } + errCode = GetCommitTree(commitMap, commits); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_GET_COMMIT_TREE); + } + if (errCode != E_OK) { + LOGE("CommitHistorySync::RequestRecvCallback : GetCommitTree ERR, errno = %d", errCode); + } + + errCode = SendAckPacket(context, commits, errCode, message); + LOGD("CommitHistorySync::RequestRecvCallback:SendAckPacket, errno = %d, dst=%s{private}, ver = %u, myversion = %u", + errCode, context->GetDeviceId().c_str(), ver, SOFTWARE_VERSION_CURRENT); + if (errCode == E_OK) { + if (commitMap.empty()) { + LOGD("[CommitHistorySync][RequestRecvCallback] no need to start SyncResponse"); + return -E_NOT_FOUND; + } + } + return errCode; +} + +int CommitHistorySync::AckRecvCallback(MultiVerSyncTaskContext *context, const Message *message) +{ + if (!IsPacketValid(message, TYPE_RESPONSE) || (context == nullptr)) { + return -E_INVALID_ARGS; + } + + std::vector commits; + int32_t errCode; + + const CommitHistorySyncAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + packet->GetErrorCode(errCode); + if (errCode == -E_NOT_PERMIT) { + LOGE("CommitHistorySync::AckRecvCallback RunPermissionCheck not pass"); + return errCode; + } + packet->GetData(commits); + uint32_t ver = packet->GetVersion(); + context->SetCommits(commits); + context->SetCommitIndex(0); + context->SetCommitsSize(static_cast(commits.size())); + LOGD("CommitHistorySync::AckRecvCallback end, CommitsSize = %zu, dst = %s{private}, ver = %d, myversion = %u", + commits.size(), context->GetDeviceId().c_str(), ver, SOFTWARE_VERSION_CURRENT); + return E_OK; +} + +int CommitHistorySync::RequestPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + if (inMsg == nullptr) { + return -E_INVALID_ARGS; + } + const CommitHistorySyncRequestPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + if ((inMsg->GetMessageId() != COMMIT_HISTORY_SYNC_MESSAGE) || (inMsg->GetMessageType() != TYPE_REQUEST)) { + return -E_INVALID_ARGS; + } + len = packet->CalculateLen(); + return E_OK; +} + +int CommitHistorySync::RequestPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || (inMsg == nullptr)) { + return -E_INVALID_ARGS; + } + const CommitHistorySyncRequestPacket *packet = inMsg->GetObject(); + if ((packet == nullptr) || (length != packet->CalculateLen())) { + return -E_INVALID_ARGS; + } + + Parcel parcel(buffer, length); + std::map commitMap; + packet->GetCommitMap(commitMap); + + int errCode = parcel.WriteUInt64(commitMap.size()); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + // commitMap Serialization + for (auto &iter : commitMap) { + errCode = parcel.WriteString(iter.first); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + errCode = parcel.WriteMultiVerCommit(iter.second); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + } + errCode = parcel.WriteUInt32(packet->GetVersion()); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + errCode = parcel.WriteVector(packet->GetReserved()); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + parcel.EightByteAlign(); + return errCode; +} + +int CommitHistorySync::RequestPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || (inMsg == nullptr)) { + return -E_INVALID_ARGS; + } + + uint64_t packLen = 0; + uint64_t len = 0; + Parcel parcel(const_cast(buffer), length); + packLen += parcel.ReadUInt64(len); + if (len > DBConstant::MAX_DEVICES_SIZE) { + LOGE("CommitHistorySync::RequestPacketDeSerialization : commitMap size too large = %" PRIu64, len); + return -E_INVALID_ARGS; + } + // commitMap DeSerialization + std::map commitMap; + while (len > 0) { + std::string key; + MultiVerCommitNode val; + packLen += parcel.ReadString(key); + packLen += parcel.ReadMultiVerCommit(val); + commitMap[key] = val; + len--; + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + } + uint32_t version; + std::vector reserved; + packLen += parcel.ReadUInt32(version); + packLen += parcel.ReadVector(reserved); + packLen = Parcel::GetEightByteAlign(packLen); + if (packLen != length || parcel.IsError()) { + LOGE("CommitHistorySync::RequestPacketDeSerialization : length error, input len = %" PRIu32 + ", cac len = %" PRIu64, length, packLen); + return -E_INVALID_ARGS; + } + CommitHistorySyncRequestPacket *packet = new (std::nothrow) CommitHistorySyncRequestPacket(); + if (packet == nullptr) { + LOGE("CommitHistorySync::RequestPacketDeSerialization : new packet error"); + return -E_OUT_OF_MEMORY; + } + packet->SetCommitMap(commitMap); + packet->SetVersion(version); + packet->SetReserved(reserved); + int errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + } + return errCode; +} + +int CommitHistorySync::AckPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + if (inMsg == nullptr) { + return -E_INVALID_ARGS; + } + const CommitHistorySyncAckPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + if ((inMsg->GetMessageId() != COMMIT_HISTORY_SYNC_MESSAGE) || (inMsg->GetMessageType() != TYPE_RESPONSE)) { + return -E_INVALID_ARGS; + } + len = packet->CalculateLen(); + return E_OK; +} + +int CommitHistorySync::AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || (inMsg == nullptr)) { + return -E_INVALID_ARGS; + } + const CommitHistorySyncAckPacket *packet = inMsg->GetObject(); + if ((packet == nullptr) || (length != packet->CalculateLen())) { + return -E_INVALID_ARGS; + } + + Parcel parcel(buffer, length); + int32_t ackErrCode; + std::vector commits; + + packet->GetData(commits); + packet->GetErrorCode(ackErrCode); + // errCode Serialization + int errCode = parcel.WriteInt(ackErrCode); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + errCode = parcel.WriteUInt32(packet->GetVersion()); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + parcel.EightByteAlign(); + + // commits vector Serialization + errCode = parcel.WriteMultiVerCommits(commits); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + errCode = parcel.WriteVector(packet->GetReserved()); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + parcel.EightByteAlign(); + return errCode; +} + +int CommitHistorySync::AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + std::vector commits; + uint32_t packLen = 0; + Parcel parcel(const_cast(buffer), length); + int32_t pktErrCode; + uint32_t version; + std::vector reserved; + + // errCode DeSerialization + packLen += parcel.ReadInt(pktErrCode); + packLen += parcel.ReadUInt32(version); + parcel.EightByteAlign(); + packLen = Parcel::GetEightByteAlign(packLen); + // commits vector DeSerialization + packLen += parcel.ReadMultiVerCommits(commits); + packLen += parcel.ReadVector(reserved); + packLen = Parcel::GetEightByteAlign(packLen); + if (packLen != length || parcel.IsError()) { + LOGE("CommitHistorySync::AckPacketDeSerialization : packet len error, input len = %u, cal len = %u", + length, packLen); + return -E_INVALID_ARGS; + } + CommitHistorySyncAckPacket *packet = new (std::nothrow) CommitHistorySyncAckPacket(); + if (packet == nullptr) { + LOGE("CommitHistorySync::AckPacketDeSerialization : new packet error"); + return -E_OUT_OF_MEMORY; + } + packet->SetData(commits); + packet->SetErrorCode(pktErrCode); + packet->SetVersion(version); + packet->SetReserved(reserved); + int errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + } + return errCode; +} + +bool CommitHistorySync::IsPacketValid(const Message *inMsg, uint16_t messageType) +{ + if ((inMsg == nullptr) || (inMsg->GetMessageId() != COMMIT_HISTORY_SYNC_MESSAGE)) { + return false; + } + if (messageType != inMsg->GetMessageType()) { + return false; + } + return true; +} + +int CommitHistorySync::Send(const DeviceID &deviceId, const Message *inMsg) +{ + SendConfig conf = {false, false, SEND_TIME_OUT, {}}; + int errCode = communicateHandle_->SendMessage(deviceId, inMsg, conf); + if (errCode != E_OK) { + LOGE("CommitHistorySync::Send ERR! err = %d", errCode); + } + return errCode; +} + +int CommitHistorySync::GetDeviceLatestCommit(std::map &commitMap) +{ + std::map readCommitMap; + int errCode = storagePtr_->GetDeviceLatestCommit(readCommitMap); + if (errCode != E_OK) { + return errCode; + } + + std::string localDevice; + errCode = GetLocalDeviceInfo(localDevice); + LOGD("GetLocalDeviceInfo : %s{private}, errCode = %d", localDevice.c_str(), errCode); + if (errCode != E_OK) { + return errCode; + } + + for (auto &item : readCommitMap) { + errCode = storagePtr_->TransferSyncCommitDevInfo(item.second, localDevice, false); + if (errCode != E_OK) { + break; + } + commitMap.insert(std::make_pair(item.second.deviceInfo, item.second)); + } + + return errCode; +} + +int CommitHistorySync::GetCommitTree(const std::map &commitMap, + std::vector &commits) +{ + std::map newCommitMap; + + std::string localDevice; + int errCode = GetLocalDeviceInfo(localDevice); + LOGD("GetLocalDeviceInfo : %s{private}, errCode = %d", localDevice.c_str(), errCode); + if (errCode != E_OK) { + return errCode; + } + + for (const auto &item : commitMap) { + MultiVerCommitNode commitNode = item.second; + errCode = storagePtr_->TransferSyncCommitDevInfo(commitNode, localDevice, true); + if (errCode != E_OK) { + return errCode; + } + newCommitMap.insert(std::make_pair(commitNode.deviceInfo, commitNode)); + } + + errCode = storagePtr_->GetCommitTree(newCommitMap, commits); + if (errCode != E_OK) { + return errCode; + } + for (auto &commit : commits) { + errCode = storagePtr_->TransferSyncCommitDevInfo(commit, localDevice, false); + if (errCode != E_OK) { + break; + } + } + return errCode; +} + +int CommitHistorySync::SendRequestPacket(const MultiVerSyncTaskContext *context, + std::map &commitMap) +{ + CommitHistorySyncRequestPacket *packet = new (std::nothrow) CommitHistorySyncRequestPacket(); + if (packet == nullptr) { + LOGE("CommitHistorySync::SendRequestPacket : new packet error"); + return -E_OUT_OF_MEMORY; + } + packet->SetCommitMap(commitMap); + packet->SetVersion(SOFTWARE_VERSION_CURRENT); + Message *message = new (std::nothrow) Message(COMMIT_HISTORY_SYNC_MESSAGE); + if (message == nullptr) { + LOGE("CommitHistorySync::SendRequestPacket : new message error"); + delete packet; + packet = nullptr; + return -E_OUT_OF_MEMORY; + } + message->SetMessageType(TYPE_REQUEST); + message->SetTarget(context->GetDeviceId()); + int errCode = message->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete message; + message = nullptr; + LOGE("CommitHistorySync::SendRequestPacket : SetExternalObject failed errCode:%d", errCode); + return errCode; + } + message->SetSessionId(context->GetRequestSessionId()); + message->SetSequenceId(context->GetSequenceId()); + + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_COMMIT_SEND_REQUEST_TO_ACK_RECV); + } + errCode = Send(message->GetTarget(), message); + if (errCode != E_OK) { + LOGE("CommitHistorySync::SendRequestPacket : Send failed errCode:%d", errCode); + delete message; + message = nullptr; + } + return errCode; +} + +int CommitHistorySync::SendAckPacket(const MultiVerSyncTaskContext *context, + std::vector &commits, int ackCode, const Message *message) +{ + if (message == nullptr) { + LOGE("CommitHistorySync::SendAckPacket : message is nullptr"); + return -E_INVALID_ARGS; + } + CommitHistorySyncAckPacket *packet = new (std::nothrow) CommitHistorySyncAckPacket(); + if (packet == nullptr) { + LOGE("CommitHistorySync::SendAckPacket : packet is nullptr"); + return -E_OUT_OF_MEMORY; + } + Message *ackMessage = new (std::nothrow) Message(COMMIT_HISTORY_SYNC_MESSAGE); + if (ackMessage == nullptr) { + LOGE("CommitHistorySync::SendAckPacket : new message error"); + delete packet; + packet = nullptr; + return -E_OUT_OF_MEMORY; + } + + packet->SetData(commits); + packet->SetErrorCode(static_cast(ackCode)); + packet->SetVersion(SOFTWARE_VERSION_CURRENT); + ackMessage->SetMessageType(TYPE_RESPONSE); + ackMessage->SetTarget(context->GetDeviceId()); + int errCode = ackMessage->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete ackMessage; + ackMessage = nullptr; + LOGE("CommitHistorySync::SendAckPacket : SetExternalObject failed errCode:%d", errCode); + return errCode; + } + ackMessage->SetSequenceId(message->GetSequenceId()); + ackMessage->SetSessionId(message->GetSessionId()); + errCode = Send(ackMessage->GetTarget(), ackMessage); + if (errCode != E_OK) { + LOGE("CommitHistorySync::SendAckPacket : Send failed errCode:%d", errCode); + delete ackMessage; + ackMessage = nullptr; + } + return errCode; +} + +int CommitHistorySync::GetLocalDeviceInfo(std::string &deviceInfo) +{ + return communicateHandle_->GetLocalIdentity(deviceInfo); +} + +int CommitHistorySync::RunPermissionCheck(const std::string &deviceId) const +{ + std::string appId = storagePtr_->GetDbProperties().GetStringProp(KvDBProperties::APP_ID, ""); + std::string userId = storagePtr_->GetDbProperties().GetStringProp(KvDBProperties::USER_ID, ""); + std::string storeId = storagePtr_->GetDbProperties().GetStringProp(KvDBProperties::STORE_ID, ""); + uint8_t flag = CHECK_FLAG_SEND; + int errCode = RuntimeContext::GetInstance()->RunPermissionCheck(userId, appId, storeId, deviceId, flag); + if (errCode != E_OK) { + LOGE("[CommitHistorySync] RunPermissionCheck not pass errCode:%d, flag:%d", errCode, flag); + return -E_NOT_PERMIT; + } + return errCode; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/commit_history_sync.h b/mock/distributeddb/syncer/src/commit_history_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..5c7869fe5f3aafe1bda3f7e18f4cb5c6b930e0d1 --- /dev/null +++ b/mock/distributeddb/syncer/src/commit_history_sync.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMIT_HISTORY_SYNC_H +#define COMMIT_HISTORY_SYNC_H + +#ifndef OMIT_MULTI_VER +#include +#include + +#include "icommunicator.h" +#include "multi_ver_kvdb_sync_interface.h" +#include "multi_ver_sync_task_context.h" +#include "sync_types.h" +#include "version.h" + +namespace DistributedDB { +class CommitHistorySyncRequestPacket { +public: + CommitHistorySyncRequestPacket() {}; + ~CommitHistorySyncRequestPacket() {}; + + uint32_t CalculateLen() const; + + void SetCommitMap(std::map &inMap); + + void GetCommitMap(std::map &outMap) const; + + void SetVersion(uint32_t version); + + uint32_t GetVersion() const; + + void SetReserved(std::vector &reserved); + + std::vector GetReserved() const; + +private: + std::map commitMap_; + uint32_t version_ = SOFTWARE_VERSION_CURRENT; + std::vector reserved_; +}; + +class CommitHistorySyncAckPacket { +public: + CommitHistorySyncAckPacket() : errorCode_(0) {}; + ~CommitHistorySyncAckPacket() {}; + + uint32_t CalculateLen() const; + + void SetData(std::vector &inData); + + void GetData(std::vector &outData) const; + + void SetErrorCode(int32_t errCode); + + void GetErrorCode(int32_t &errCode) const; + + void SetVersion(uint32_t version); + + uint32_t GetVersion() const; + + void SetReserved(std::vector &reserved); + + std::vector GetReserved() const; + +private: + int32_t errorCode_; + uint32_t version_ = SOFTWARE_VERSION_CURRENT; + std::vector commits_; + std::vector reserved_; +}; + +class CommitHistorySync { +public: + CommitHistorySync() : storagePtr_(nullptr), communicateHandle_(nullptr) {}; + ~CommitHistorySync(); + DISABLE_COPY_ASSIGN_MOVE(CommitHistorySync); + + static int RegisterTransformFunc(); + + int Initialize(MultiVerKvDBSyncInterface *storagePtr, ICommunicator *communicateHandle); + + static int Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static uint32_t CalculateLen(const Message *inMsg); + + void TimeOutCallback(MultiVerSyncTaskContext *context, const Message *message) const; + + int SyncStart(MultiVerSyncTaskContext *context); + + int RequestRecvCallback(const MultiVerSyncTaskContext *context, const Message *message); + + int AckRecvCallback(MultiVerSyncTaskContext *context, const Message *message); + +private: + static int RequestPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int RequestPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int RequestPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static int AckPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static bool IsPacketValid(const Message *inMsg, uint16_t messageType); + + int Send(const DeviceID &deviceId, const Message *inMsg); + + int GetDeviceLatestCommit(std::map &); + + int GetCommitTree(const std::map &, std::vector &); + + int SendRequestPacket(const MultiVerSyncTaskContext *context, + std::map &commitMap); + + int SendAckPacket(const MultiVerSyncTaskContext *context, std::vector &commits, + int ackCode, const Message *message); + + int GetLocalDeviceInfo(std::string &deviceInfo); + + int RunPermissionCheck(const std::string &deviceId) const; + + MultiVerKvDBSyncInterface *storagePtr_; + ICommunicator *communicateHandle_; +}; +} // namespace DistributedDB + +#endif +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/communicator_proxy.cpp b/mock/distributeddb/syncer/src/communicator_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e1f0b290e537b6bc72535b993a106c9d3ef7c83 --- /dev/null +++ b/mock/distributeddb/syncer/src/communicator_proxy.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "communicator_proxy.h" +#include "db_constant.h" +#include "db_common.h" +#include "db_dump_helper.h" +#include "log_print.h" + +namespace DistributedDB { +CommunicatorProxy::CommunicatorProxy() : mainComm_(nullptr) +{ +} + +CommunicatorProxy::~CommunicatorProxy() +{ + if (mainComm_ != nullptr) { + RefObject::DecObjRef(mainComm_); + } + mainComm_ = nullptr; + + std::lock_guard lock(devCommMapLock_); + for (const auto &iter : devCommMap_) { + RefObject::DecObjRef(devCommMap_[iter.first].second); + } + devCommMap_.clear(); +} + +int CommunicatorProxy::RegOnMessageCallback(const OnMessageCallback &onMessage, const Finalizer &inOper) +{ + if (mainComm_ != nullptr) { + (void) mainComm_->RegOnMessageCallback(onMessage, inOper); + } + + std::lock_guard lock(devCommMapLock_); + for (const auto &iter : devCommMap_) { + (void) devCommMap_[iter.first].second->RegOnMessageCallback(onMessage, inOper); + } + return E_OK; +} + +int CommunicatorProxy::RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) +{ + if (mainComm_ != nullptr) { + (void) mainComm_->RegOnConnectCallback(onConnect, inOper); + } + + std::lock_guard lock(devCommMapLock_); + for (const auto &iter : devCommMap_) { + (void) devCommMap_[iter.first].second->RegOnConnectCallback(onConnect, inOper); + } + + return E_OK; +} + +int CommunicatorProxy::RegOnSendableCallback(const std::function &onSendable, const Finalizer &inOper) +{ + if (mainComm_ != nullptr) { + (void) mainComm_->RegOnSendableCallback(onSendable, inOper); + } + + std::lock_guard lock(devCommMapLock_); + for (const auto &iter : devCommMap_) { + (void) devCommMap_[iter.first].second->RegOnSendableCallback(onSendable, inOper); + } + + return E_OK; +} + +void CommunicatorProxy::Activate() +{ + if (mainComm_ != nullptr) { + mainComm_->Activate(); + } + + // use temp map to avoid active in lock + std::map tempMap; + { + std::lock_guard lock(devCommMapLock_); + for (const auto &iter : devCommMap_) { + tempMap[iter.first] = devCommMap_[iter.first].second; + RefObject::IncObjRef(devCommMap_[iter.first].second); + } + } + + for (const auto &iter : tempMap) { + tempMap[iter.first]->Activate(); + RefObject::DecObjRef(tempMap[iter.first]); + } +} + +uint32_t CommunicatorProxy::GetCommunicatorMtuSize() const +{ + if (mainComm_ == nullptr) { + return DBConstant::MIN_MTU_SIZE; + } + return mainComm_->GetCommunicatorMtuSize(); +} + +uint32_t CommunicatorProxy::GetCommunicatorMtuSize(const std::string &target) const +{ + ICommunicator *targetCommunicator = nullptr; + { + std::lock_guard lock(devCommMapLock_); + if (devCommMap_.count(target) != 0) { + targetCommunicator = devCommMap_.at(target).second; + RefObject::IncObjRef(targetCommunicator); + } + } + if (targetCommunicator != nullptr) { + uint32_t mtuSize = targetCommunicator->GetCommunicatorMtuSize(target); + RefObject::DecObjRef(targetCommunicator); + return mtuSize; + } + + if (mainComm_ != nullptr) { + return mainComm_->GetCommunicatorMtuSize(target); + } + + return DBConstant::MIN_MTU_SIZE; +} + +uint32_t CommunicatorProxy::GetTimeout() const +{ + if (mainComm_ == nullptr) { + return DBConstant::MIN_TIMEOUT; + } + return mainComm_->GetTimeout(); +} + +uint32_t CommunicatorProxy::GetTimeout(const std::string &target) const +{ + ICommunicator *targetCommunicator = nullptr; + { + std::lock_guard lock(devCommMapLock_); + if (devCommMap_.count(target) != 0) { + targetCommunicator = devCommMap_.at(target).second; + RefObject::IncObjRef(targetCommunicator); + } + } + if (targetCommunicator != nullptr) { + uint32_t timeout = targetCommunicator->GetTimeout(target); + RefObject::DecObjRef(targetCommunicator); + return timeout; + } + + if (mainComm_ != nullptr) { + return mainComm_->GetTimeout(target); + } + + return DBConstant::MIN_TIMEOUT; +} + +bool CommunicatorProxy::IsDeviceOnline(const std::string &device) const +{ + return mainComm_->IsDeviceOnline(device); +} + +int CommunicatorProxy::GetLocalIdentity(std::string &outTarget) const +{ + return mainComm_->GetLocalIdentity(outTarget); +} + +int CommunicatorProxy::GetRemoteCommunicatorVersion(const std::string &target, uint16_t &outVersion) const +{ + ICommunicator *targetCommunicator = nullptr; + { + std::lock_guard lock(devCommMapLock_); + if (devCommMap_.count(target) != 0) { + targetCommunicator = devCommMap_.at(target).second; + RefObject::IncObjRef(targetCommunicator); + } + } + if (targetCommunicator != nullptr) { + int errCode = targetCommunicator->GetRemoteCommunicatorVersion(target, outVersion); + RefObject::DecObjRef(targetCommunicator); + return errCode; + } + + if (mainComm_ != nullptr) { + return mainComm_->GetRemoteCommunicatorVersion(target, outVersion); + } + + return -E_NOT_INIT; +} + +int CommunicatorProxy::SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config) +{ + return SendMessage(dstTarget, inMsg, config, nullptr); +} + +int CommunicatorProxy::SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config, + const OnSendEnd &onEnd) +{ + ICommunicator *targetCommunicator = nullptr; + { + std::lock_guard lock(devCommMapLock_); + if (devCommMap_.count(dstTarget) != 0) { + targetCommunicator = devCommMap_[dstTarget].second; + RefObject::IncObjRef(targetCommunicator); + } + } + if (targetCommunicator != nullptr) { + LOGD("[CommProxy] use equal label to send data"); + int errCode = targetCommunicator->SendMessage(dstTarget, inMsg, config, onEnd); + RefObject::DecObjRef(targetCommunicator); + return errCode; + } + + if (mainComm_ != nullptr) { + return mainComm_->SendMessage(dstTarget, inMsg, config, onEnd); + } + + return -E_NOT_INIT; +} + +void CommunicatorProxy::SetMainCommunicator(ICommunicator *communicator) +{ + mainComm_ = communicator; + RefObject::IncObjRef(mainComm_); +} + +void CommunicatorProxy::SetEqualCommunicator(ICommunicator *communicator, const std::string &identifier, + const std::vector &targets) +{ + std::lock_guard lock(devCommMapLock_); + // Clear offline target + for (auto dev = devCommMap_.begin(); dev != devCommMap_.end();) { + if (identifier != dev->second.first) { + dev++; + continue; + } + auto iter = std::find_if(targets.begin(), targets.end(), + [&dev](const std::string &target) { + return target == dev->first; + }); + if (iter == targets.end()) { + RefObject::DecObjRef(devCommMap_[dev->first].second); + dev = devCommMap_.erase(dev); + continue; + } + dev++; + } + + // Add new online target + for (const auto &target : targets) { + if (devCommMap_.count(target) != 0) { + // change the identifier and dev relation + RefObject::DecObjRef(devCommMap_[target].second); + } + RefObject::IncObjRef(communicator); + devCommMap_[target] = {identifier, communicator}; + } +} + +void CommunicatorProxy::Dump(int fd) +{ + std::lock_guard lock(devCommMapLock_); + for (const auto &[target, communicator] : devCommMap_) { + std::string label = DBCommon::TransferStringToHex(communicator.first); + DBDumpHelper::Dump(fd, "\t\ttarget = %s, label = %s\n", target.c_str(), label.c_str()); + } +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/communicator_proxy.h b/mock/distributeddb/syncer/src/communicator_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..47f7fc19a0ab814a7511f56c6da9e4d55734df22 --- /dev/null +++ b/mock/distributeddb/syncer/src/communicator_proxy.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATOR_PROXY_H +#define COMMUNICATOR_PROXY_H + +#include +#include +#include +#include +#include +#include +#include "icommunicator.h" +#include "message.h" + +namespace DistributedDB { +class CommunicatorProxy : public ICommunicator { +public: + CommunicatorProxy(); + ~CommunicatorProxy(); + + int RegOnMessageCallback(const OnMessageCallback &onMessage, const Finalizer &inOper) override; + int RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) override; + int RegOnSendableCallback(const std::function &onSendable, const Finalizer &inOper) override; + void Activate() override; + uint32_t GetCommunicatorMtuSize() const override; + uint32_t GetCommunicatorMtuSize(const std::string &target) const override; + uint32_t GetTimeout() const override; + uint32_t GetTimeout(const std::string &target) const override; + bool IsDeviceOnline(const std::string &device) const override; + int GetLocalIdentity(std::string &outTarget) const override; + int GetRemoteCommunicatorVersion(const std::string &target, uint16_t &outVersion) const override; + int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config) override; + int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config, + const OnSendEnd &onEnd) override; + + // Set an Main communicator for this database, used userid & appId & storeId + void SetMainCommunicator(ICommunicator *communicator); + + // Set an equal communicator for this database, After this called, send msg to the target will use this communicator + void SetEqualCommunicator(ICommunicator *communicator, const std::string &identifier, + const std::vector &targets); + + void Dump(int fd); + +private: + ICommunicator *mainComm_; + mutable std::mutex devCommMapLock_; + // key: device value: + std::map> devCommMap_; +}; +} // namespace DistributedDB +#endif // COMMUNICATOR_PROXY_H diff --git a/mock/distributeddb/syncer/src/db_ability.cpp b/mock/distributeddb/syncer/src/db_ability.cpp new file mode 100644 index 0000000000000000000000000000000000000000..54631a3ccd31875232e5acc2fe97fb17379d9390 --- /dev/null +++ b/mock/distributeddb/syncer/src/db_ability.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "db_ability.h" + +#include +#include "db_errno.h" +#include "types_export.h" + +namespace DistributedDB { +DbAbility::DbAbility() +{ + for (const auto &item : SyncConfig::ABILITYBITS) { + dbAbilityItemSet_.insert(item); + } + dbAbility_.resize(SyncConfig::ABILITYBITS.back().first + SyncConfig::ABILITYBITS.back().second); +} + +DbAbility::DbAbility(const DbAbility &other) +{ + if (&other != this) { + dbAbility_ = other.dbAbility_; + dbAbilityItemSet_ = other.dbAbilityItemSet_; + } +} + +DbAbility& DbAbility::operator=(const DbAbility &other) +{ + if (&other != this) { + dbAbility_ = other.dbAbility_; + dbAbilityItemSet_ = other.dbAbilityItemSet_; + } + return *this; +} + +bool DbAbility::operator==(const DbAbility &other) const +{ + return (dbAbility_ == other.dbAbility_) && (dbAbilityItemSet_ == other.dbAbilityItemSet_); +} + +int DbAbility::Serialize(Parcel &parcel, const DbAbility &curAbility) +{ + uint32_t div = curAbility.GetAbilityBitsLen() / SERIALIZE_BIT_SIZE; + uint32_t buffLen = (curAbility.GetAbilityBitsLen() % SERIALIZE_BIT_SIZE) ? div + 1 : div; + std::vector dstBuf(buffLen, 0); + uint32_t buffOffset = 0; + uint32_t innerBuffOffset = 0; + const std::vector &abilityBuff = curAbility.GetDbAbilityBuff(); + for (uint32_t pos = 0; pos < curAbility.GetAbilityBitsLen(); pos++, innerBuffOffset++) { + if (innerBuffOffset >= SERIALIZE_BIT_SIZE) { + innerBuffOffset = 0; + buffOffset++; + } + uint64_t value = static_cast(abilityBuff[pos]) << innerBuffOffset; + dstBuf[buffOffset] = dstBuf[buffOffset] | value; + } + int errCode = parcel.WriteVector(dstBuf); + if (errCode != E_OK) { + return errCode; + } + return E_OK; +} + +int DbAbility::DeSerialize(Parcel &parcel, DbAbility &curAbility) +{ + if (!parcel.IsContinueRead()) { + return E_OK; + } + std::vector dstBuf; + parcel.ReadVector(dstBuf); + if (parcel.IsError()) { + LOGE("[DbAbility][DeSerialize] deserialize failed."); + return -E_LENGTH_ERROR; + } + if (dstBuf.size() == 0) { + LOGE("[DbAbility][DeSerialize] buf length get failed."); + return -E_LENGTH_ERROR; + } + std::vector targetBuff(SyncConfig::ABILITYBITS.back().first + SyncConfig::ABILITYBITS.back().second); + uint32_t buffOffset = 0; + uint32_t innerBuffOffset = 0; + for (uint32_t pos = 0; pos < targetBuff.size() && pos < SERIALIZE_BIT_SIZE * dstBuf.size(); pos++) { + if (innerBuffOffset >= SERIALIZE_BIT_SIZE) { + innerBuffOffset = 0; + buffOffset++; + } + targetBuff[pos] = (dstBuf[buffOffset] >> innerBuffOffset) & 0x1; + innerBuffOffset++; + } + curAbility.SetDbAbilityBuff(targetBuff); + return E_OK; +} + +uint32_t DbAbility::CalculateLen(const DbAbility &curAbility) +{ + uint32_t div = curAbility.GetAbilityBitsLen() / SERIALIZE_BIT_SIZE; + uint32_t buffLen = (curAbility.GetAbilityBitsLen() % SERIALIZE_BIT_SIZE) ? div + 1 : div; + return Parcel::GetVectorLen(std::vector(buffLen, 0)); +} + +void DbAbility::SetDbAbilityBuff(std::vector &buff) +{ + dbAbility_ = buff; +} + +const std::vector &DbAbility::GetDbAbilityBuff() const +{ + return dbAbility_; +} + +uint32_t DbAbility::GetAbilityBitsLen() const +{ + return dbAbility_.size(); +} + +uint8_t DbAbility::GetAbilityItem(const AbilityItem &abilityType) const +{ + uint8_t data = 0; + auto iter = dbAbilityItemSet_.find(abilityType); + if (iter != dbAbilityItemSet_.end()) { + if ((iter->first + iter->second) > dbAbility_.size()) { + LOGE("[DbAbility] abilityType is error, start=%" PRIu32 ", use_bit=%" PRIu32 ", totalLen=%zu", + iter->first, iter->second, dbAbility_.size()); + return 0; + } + uint32_t skip = 0; + // dbAbility_ bit[0..len] : low-->high, skip range 0..7 + for (uint32_t pos = iter->first; pos < (iter->first + iter->second); pos++, skip++) { + if (dbAbility_[pos]) { + data += (static_cast(dbAbility_[pos])) << skip; + } + } + } + return data; +} + +int DbAbility::SetAbilityItem(const AbilityItem &abilityType, uint8_t data) +{ + auto iter = dbAbilityItemSet_.find(abilityType); + if (iter != dbAbilityItemSet_.end()) { + if (data >= pow(2, iter->second)) { // 2: means binary + LOGE("[DbAbility] value is invalid, data=%d, use_bit=%d", data, iter->second); + return -E_INTERNAL_ERROR; + } + if ((iter->first + iter->second) > dbAbility_.size()) { + dbAbility_.resize(iter->first + iter->second); + } + int pos = iter->first; + while (data) { + dbAbility_[pos] = data % 2; // 2: means binary + data = (data >> 1); + pos++; + } + } + return E_OK; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/db_ability.h b/mock/distributeddb/syncer/src/db_ability.h new file mode 100644 index 0000000000000000000000000000000000000000..a1bb45eb93f2c2d4158c09e87318aa23168887ac --- /dev/null +++ b/mock/distributeddb/syncer/src/db_ability.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DB_ABILITY_H +#define DB_ABILITY_H + +#include +#include +#include +#include "macro_utils.h" +#include "parcel.h" +#include "sync_config.h" + +namespace DistributedDB { +class DbAbility { +public: + DbAbility(); + DbAbility(const DbAbility &other); + DbAbility& operator=(const DbAbility &other); + ~DbAbility() = default; + + bool operator==(const DbAbility &other) const; + // translate dbAbility_ to std::vector + static int Serialize(Parcel &parcel, const DbAbility &curAbility); + + static int DeSerialize(Parcel &parcel, DbAbility &curAbility); + + static uint32_t CalculateLen(const DbAbility &curAbility); + + void SetDbAbilityBuff(std::vector &buff); + + const std::vector &GetDbAbilityBuff() const; + + uint32_t GetAbilityBitsLen() const; + + uint8_t GetAbilityItem(const AbilityItem &abilityType) const; + + int SetAbilityItem(const AbilityItem &abilityType, uint8_t data); +private: + constexpr static int SERIALIZE_BIT_SIZE = 64; // uint64_t bit size + + std::vector dbAbility_; + std::set dbAbilityItemSet_; +}; +} // namespace DistributedDB + +#endif // DB_ABILITY_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/device_manager.cpp b/mock/distributeddb/syncer/src/device_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b773d69a28c6d20bb5baccfa6fdb1fadeaa9570a --- /dev/null +++ b/mock/distributeddb/syncer/src/device_manager.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device_manager.h" + +#include + +#include "message_transform.h" +#include "parcel.h" +#include "db_errno.h" +#include "message.h" +#include "log_print.h" +#include "performance_analysis.h" +#include "sync_types.h" + +namespace DistributedDB { +DeviceManager::DeviceManager() : communicator_(nullptr) +{ +} + +DeviceManager::~DeviceManager() +{ + if (communicator_ != nullptr) { + RefObject::DecObjRef(communicator_); + communicator_ = nullptr; + } +} + +uint32_t DeviceManager::CalculateLen() +{ + return Parcel::GetUInt64Len(); +} + +int DeviceManager::RegisterTransformFunc() +{ + TransformFunc func; + func.computeFunc = [](const Message *msg) { + (void) msg; + return DeviceManager::CalculateLen(); + }; + // LocalDataChanged has no dataPct + func.serializeFunc = [](uint8_t *buffer, uint32_t length, const Message *inMsg) { + (void) buffer; + (void) length; + (void) inMsg; + return E_OK; + }; + func.deserializeFunc = [](const uint8_t *buffer, uint32_t length, Message *inMsg) { + (void) buffer; + (void) length; + (void) inMsg; + return E_OK; + }; + return MessageTransform::RegTransformFunction(LOCAL_DATA_CHANGED, func); +} + +// Initialize the DeviceManager +int DeviceManager::Initialize(ICommunicator *communicator, const std::function &onlineCallback, + const std::function &offlineCallback) +{ + if (communicator == nullptr) { + return -E_INVALID_ARGS; + } + RefObject::IncObjRef(communicator); + communicator_ = communicator; + RegDeviceOnLineCallBack(onlineCallback); + RegDeviceOffLineCallBack(offlineCallback); + return E_OK; +} + +void DeviceManager::RegDeviceOnLineCallBack(const std::function &callback) +{ + onlineCallback_ = callback; +} + +void DeviceManager::RegDeviceOffLineCallBack(const std::function &callback) +{ + offlineCallback_ = callback; +} + +void DeviceManager::OnDeviceConnectCallback(const std::string &targetDev, bool isConnect) +{ + LOGD("[DeviceManager] DeviceConnectCallback dev = %s{private}, status = %d", targetDev.c_str(), isConnect); + if (targetDev.empty()) { + LOGE("[DeviceManager] DeviceConnectCallback invalid device!"); + } + if (isConnect) { + { + std::lock_guard lockOnline(devicesLock_); + devices_.insert(targetDev); + } + if (onlineCallback_) { + onlineCallback_(targetDev); + LOGD("[DeviceManager] DeviceConnectCallback call online callback"); + } + } else { + { + std::lock_guard lockOffline(devicesLock_); + devices_.erase(targetDev); + } + if (offlineCallback_) { + offlineCallback_(targetDev); + LOGD("[DeviceManager] DeviceConnectCallback call offline callback"); + } + } +} + +void DeviceManager::GetOnlineDevices(std::vector &devices) const +{ + std::lock_guard lock(devicesLock_); + devices.assign(devices_.begin(), devices_.end()); +} + +int DeviceManager::SendBroadCast(uint32_t msgId) +{ + if (msgId == LOCAL_DATA_CHANGED) { + return SendLocalDataChanged(); + } + LOGE("[DeviceManager] invalid BroadCast msgId:%u", msgId); + return -E_INVALID_ARGS; +} + +int DeviceManager::SendLocalDataChanged() +{ + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_SEND_LOCAL_DATA_CHANGED_TO_COMMIT_REQUEST_RECV); + } + std::vector copyDevices; + GetOnlineDevices(copyDevices); + if (copyDevices.empty()) { + LOGI("[DeviceManager] no device online to SendLocalDataChanged!"); + } + for (const auto &deviceId : copyDevices) { + Message *msg = new (std::nothrow) Message(); + if (msg == nullptr) { + LOGE("[DeviceManager] Message alloc failed when SendBroadCast!"); + return -E_OUT_OF_MEMORY; + } + msg->SetMessageId(LOCAL_DATA_CHANGED); + msg->SetTarget(deviceId); + SendConfig conf = {false, false, SEND_TIME_OUT, {}}; + int errCode = communicator_->SendMessage(deviceId, msg, conf); + if (errCode != E_OK) { + LOGE("[DeviceManager] SendLocalDataChanged to dev %s{private} failed. err %d", + deviceId.c_str(), errCode); + delete msg; + msg = nullptr; + } + } + return E_OK; +} + +bool DeviceManager::IsDeviceOnline(const std::string &deviceId) const +{ + std::lock_guard lock(devicesLock_); + auto iter = std::find(devices_.begin(), devices_.end(), deviceId); + return (iter != devices_.end()); +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/device_manager.h b/mock/distributeddb/syncer/src/device_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..5b638b1e20dbd1e333559ad8e0d180adb6cae33e --- /dev/null +++ b/mock/distributeddb/syncer/src/device_manager.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DEVICE_MANAGER_H +#define DEVICE_MANAGER_H + +#include +#include + +#include "icommunicator.h" + +namespace DistributedDB { +class DeviceManager final { +public: + DeviceManager(); + ~DeviceManager(); + + DISABLE_COPY_ASSIGN_MOVE(DeviceManager); + + static int RegisterTransformFunc(); + + // Calculate the length of message. + static uint32_t CalculateLen(); + + // Initialize the DeviceManager. + int Initialize(ICommunicator *communicator, const std::function &onlineCallback, + const std::function &offlineCallback); + + // Set The Device online Callback. + void RegDeviceOnLineCallBack(const std::function &callback); + + // Set The Device offline Callback. + void RegDeviceOffLineCallBack(const std::function &callback); + + // The Device connect message callback, registered to the ICommunicator + void OnDeviceConnectCallback(const std::string &targetDev, bool isConnect); + + // Get The online devices list. + void GetOnlineDevices(std::vector &devices) const; + + // Send a BroadCast to all online device. + int SendBroadCast(uint32_t msgId); + + // Determine if the device is online. + bool IsDeviceOnline(const std::string &deviceId) const; + +private: + + // Send a local data changed broadcast. + int SendLocalDataChanged(); + + std::set devices_; + std::function onlineCallback_; + std::function offlineCallback_; + ICommunicator *communicator_; + mutable std::mutex devicesLock_; +}; +} // namespace DistributedDB + +#endif // DEVICE_MANAGER_H diff --git a/mock/distributeddb/syncer/src/generic_syncer.cpp b/mock/distributeddb/syncer/src/generic_syncer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a0d817a350de9c145a27f580b703c9e12ed4bacb --- /dev/null +++ b/mock/distributeddb/syncer/src/generic_syncer.cpp @@ -0,0 +1,818 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "generic_syncer.h" + +#include "db_common.h" +#include "db_errno.h" +#include "log_print.h" +#include "ref_object.h" +#include "sqlite_single_ver_natural_store.h" +#include "time_sync.h" +#include "single_ver_data_sync.h" +#ifndef OMIT_MULTI_VER +#include "commit_history_sync.h" +#include "multi_ver_data_sync.h" +#include "value_slice_sync.h" +#endif +#include "device_manager.h" +#include "db_constant.h" +#include "ability_sync.h" +#include "single_ver_serialize_manager.h" + +namespace DistributedDB { +const int GenericSyncer::MIN_VALID_SYNC_ID = 1; +std::mutex GenericSyncer::moduleInitLock_; +int GenericSyncer::currentSyncId_ = 0; +std::mutex GenericSyncer::syncIdLock_; +GenericSyncer::GenericSyncer() + : syncEngine_(nullptr), + syncInterface_(nullptr), + timeHelper_(nullptr), + metadata_(nullptr), + initialized_(false), + queuedManualSyncSize_(0), + queuedManualSyncLimit_(DBConstant::QUEUED_SYNC_LIMIT_DEFAULT), + manualSyncEnable_(true), + closing_(false), + engineFinalize_(false) +{ +} + +GenericSyncer::~GenericSyncer() +{ + LOGD("[GenericSyncer] ~GenericSyncer!"); + if (syncEngine_ != nullptr) { + syncEngine_->OnKill([this]() { this->syncEngine_->Close(); }); + RefObject::KillAndDecObjRef(syncEngine_); + // waiting all thread exist + std::mutex engineMutex; + std::unique_lock cvLock(engineMutex); + bool engineFinalize = engineFinalizeCv_.wait_for(cvLock, std::chrono::milliseconds(DBConstant::MIN_TIMEOUT), + [this]() { return engineFinalize_; }); + if (!engineFinalize) { + LOGW("syncer finalize before engine finalize!"); + } + syncEngine_ = nullptr; + } + timeHelper_ = nullptr; + metadata_ = nullptr; + syncInterface_ = nullptr; +} + +int GenericSyncer::Initialize(ISyncInterface *syncInterface, bool isNeedActive) +{ + if (syncInterface == nullptr) { + LOGE("[Syncer] Init failed, the syncInterface is null!"); + return -E_INVALID_ARGS; + } + + { + std::lock_guard lock(syncerLock_); + if (initialized_) { + return E_OK; + } + if (closing_) { + LOGE("[Syncer] Syncer is closing, return!"); + return -E_BUSY; + } + std::vector label = syncInterface->GetIdentifier(); + label.resize(3); // only show 3 Bytes enough + label_ = DBCommon::VectorToHexString(label); + + // As metadata_ will be used in EraseDeviceWaterMark, it should not be clear even if engine init failed. + // It will be clear in destructor. + int errCodeMetadata = InitMetaData(syncInterface); + + // As timeHelper_ will be used in GetTimestamp, it should not be clear even if engine init failed. + // It will be clear in destructor. + int errCodeTimeHelper = InitTimeHelper(syncInterface); + if (errCodeMetadata != E_OK || errCodeTimeHelper != E_OK) { + return -E_INTERNAL_ERROR; + } + int errCode = CheckSyncActive(syncInterface, isNeedActive); + if (errCode != E_OK) { + return errCode; + } + + if (!RuntimeContext::GetInstance()->IsCommunicatorAggregatorValid()) { + LOGW("[Syncer] Communicator component not ready!"); + return -E_NOT_INIT; + } + + errCode = SyncModuleInit(); + if (errCode != E_OK) { + LOGE("[Syncer] Sync ModuleInit ERR!"); + return -E_INTERNAL_ERROR; + } + + errCode = InitSyncEngine(syncInterface); + if (errCode != E_OK) { + return errCode; + } + syncEngine_->SetEqualIdentifier(); + initialized_ = true; + } + + // RegConnectCallback may start an auto sync, this function can not in syncerLock_ + syncEngine_->RegConnectCallback(); + return E_OK; +} + +int GenericSyncer::Close(bool isClosedOperation) +{ + { + std::lock_guard lock(syncerLock_); + if (!initialized_) { + LOGW("[Syncer] Syncer[%s] don't need to close, because it has no been init", label_.c_str()); + timeHelper_ = nullptr; + metadata_ = nullptr; + return -E_NOT_INIT; + } + initialized_ = false; + if (closing_) { + LOGE("[Syncer] Syncer is closing, return!"); + return -E_BUSY; + } + closing_ = true; + } + ClearSyncOperations(isClosedOperation); + if (syncEngine_ != nullptr) { + syncEngine_->Close(); + LOGD("[Syncer] Close SyncEngine!"); + std::lock_guard lock(syncerLock_); + closing_ = false; + } + timeHelper_ = nullptr; + metadata_ = nullptr; + return E_OK; +} + +int GenericSyncer::Sync(const std::vector &devices, int mode, + const std::function &)> &onComplete, + const std::function &onFinalize, bool wait = false) +{ + SyncParma param; + param.devices = devices; + param.mode = mode; + param.onComplete = onComplete; + param.onFinalize = onFinalize; + param.wait = wait; + return Sync(param); +} + +int GenericSyncer::Sync(const InternalSyncParma ¶m) +{ + SyncParma syncParam; + syncParam.devices = param.devices; + syncParam.mode = param.mode; + syncParam.isQuerySync = param.isQuerySync; + syncParam.syncQuery = param.syncQuery; + return Sync(syncParam); +} + +int GenericSyncer::Sync(const SyncParma ¶m) +{ + return Sync(param, DBConstant::IGNORE_CONNECTION_ID); +} + +int GenericSyncer::Sync(const SyncParma ¶m, uint64_t connectionId) +{ + int errCode = SyncParamCheck(param); + if (errCode != E_OK) { + return errCode; + } + errCode = AddQueuedManualSyncSize(param.mode, param.wait); + if (errCode != E_OK) { + return errCode; + } + + uint32_t syncId = GenerateSyncId(); + errCode = PrepareSync(param, syncId, connectionId); + if (errCode != E_OK) { + LOGE("[Syncer] PrepareSync failed when sync called, err %d", errCode); + return errCode; + } + PerformanceAnalysis::GetInstance()->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_SYNC_TOTAL); + return E_OK; +} + +int GenericSyncer::PrepareSync(const SyncParma ¶m, uint32_t syncId, uint64_t connectionId) +{ + auto *operation = + new (std::nothrow) SyncOperation(syncId, param.devices, param.mode, param.onComplete, param.wait); + if (operation == nullptr) { + SubQueuedSyncSize(); + return -E_OUT_OF_MEMORY; + } + operation->SetIdentifier(syncInterface_->GetIdentifier()); + { + std::lock_guard autoLock(syncerLock_); + PerformanceAnalysis::GetInstance()->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_SYNC_TOTAL); + InitSyncOperation(operation, param); + LOGI("[Syncer] GenerateSyncId %" PRIu32 ", mode = %d, wait = %d, label = %s, devices = %s", syncId, param.mode, + param.wait, label_.c_str(), GetSyncDevicesStr(param.devices).c_str()); + AddSyncOperation(operation); + PerformanceAnalysis::GetInstance()->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_SYNC_TOTAL); + } + if (!param.wait && connectionId != DBConstant::IGNORE_CONNECTION_ID) { + std::lock_guard lockGuard(syncIdLock_); + connectionIdMap_[connectionId].push_back(static_cast(syncId)); + syncIdMap_[static_cast(syncId)] = connectionId; + } + if (operation->CheckIsAllFinished()) { + operation->Finished(); + RefObject::KillAndDecObjRef(operation); + } else { + operation->WaitIfNeed(); + RefObject::DecObjRef(operation); + } + return E_OK; +} + +int GenericSyncer::RemoveSyncOperation(int syncId) +{ + SyncOperation *operation = nullptr; + std::unique_lock lock(operationMapLock_); + auto iter = syncOperationMap_.find(syncId); + if (iter != syncOperationMap_.end()) { + LOGD("[Syncer] RemoveSyncOperation id:%d.", syncId); + operation = iter->second; + syncOperationMap_.erase(syncId); + lock.unlock(); + if ((!operation->IsAutoSync()) && (!operation->IsBlockSync()) && (!operation->IsAutoControlCmd())) { + SubQueuedSyncSize(); + } + operation->NotifyIfNeed(); + RefObject::KillAndDecObjRef(operation); + operation = nullptr; + std::lock_guard lockGuard(syncIdLock_); + if (syncIdMap_.find(syncId) == syncIdMap_.end()) { + return E_OK; + } + uint64_t connectionId = syncIdMap_[syncId]; + if (connectionIdMap_.find(connectionId) != connectionIdMap_.end()) { + connectionIdMap_[connectionId].remove(syncId); + } + syncIdMap_.erase(syncId); + return E_OK; + } + return -E_INVALID_ARGS; +} + +int GenericSyncer::StopSync(uint64_t connectionId) +{ + std::list syncIdList; + { + std::lock_guard lockGuard(syncIdLock_); + if (connectionIdMap_.find(connectionId) == connectionIdMap_.end()) { + return E_OK; + } + syncIdList = connectionIdMap_[connectionId]; + connectionIdMap_.erase(connectionId); + } + for (auto syncId : syncIdList) { + RemoveSyncOperation(syncId); + } + return E_OK; +} + +uint64_t GenericSyncer::GetTimestamp() +{ + if (timeHelper_ == nullptr) { + return TimeHelper::GetSysCurrentTime(); + } + return timeHelper_->GetTime(); +} + +void GenericSyncer::QueryAutoSync(const InternalSyncParma ¶m) +{ + (void)param; +} + +void GenericSyncer::AddSyncOperation(SyncOperation *operation) +{ + if (operation == nullptr) { + return; + } + + LOGD("[Syncer] AddSyncOperation."); + syncEngine_->AddSyncOperation(operation); + + if (operation->CheckIsAllFinished()) { + return; + } + + std::lock_guard lock(operationMapLock_); + syncOperationMap_.insert(std::pair(operation->GetSyncId(), operation)); + // To make sure operation alive before WaitIfNeed out + RefObject::IncObjRef(operation); +} + +void GenericSyncer::SyncOperationKillCallbackInner(int syncId) +{ + if (syncEngine_ != nullptr) { + LOGI("[Syncer] Operation on kill id = %d", syncId); + syncEngine_->RemoveSyncOperation(syncId); + } +} + +void GenericSyncer::SyncOperationKillCallback(int syncId) +{ + SyncOperationKillCallbackInner(syncId); +} + +int GenericSyncer::InitMetaData(ISyncInterface *syncInterface) +{ + if (metadata_ != nullptr) { + return E_OK; + } + + metadata_ = std::make_shared(); + int errCode = metadata_->Initialize(syncInterface); + if (errCode != E_OK) { + LOGE("[Syncer] metadata Initializeate failed! err %d.", errCode); + metadata_ = nullptr; + } + return errCode; +} + +int GenericSyncer::InitTimeHelper(ISyncInterface *syncInterface) +{ + if (timeHelper_ != nullptr) { + return E_OK; + } + + timeHelper_ = std::make_shared(); + int errCode = timeHelper_->Initialize(syncInterface, metadata_); + if (errCode != E_OK) { + LOGE("[Syncer] TimeHelper init failed! err:%d.", errCode); + timeHelper_ = nullptr; + } + return errCode; +} + +int GenericSyncer::InitSyncEngine(ISyncInterface *syncInterface) +{ + if (syncEngine_ != nullptr && syncEngine_->IsEngineActive()) { + LOGI("[Syncer] syncEngine is active"); + return E_OK; + } + int errCode = BuildSyncEngine(); + if (errCode != E_OK) { + return errCode; + } + const std::function onlineFunc = std::bind(&GenericSyncer::RemoteDataChanged, + this, std::placeholders::_1); + const std::function offlineFunc = std::bind(&GenericSyncer::RemoteDeviceOffline, + this, std::placeholders::_1); + const std::function queryAutoSyncFunc = + std::bind(&GenericSyncer::QueryAutoSync, this, std::placeholders::_1); + errCode = syncEngine_->Initialize(syncInterface, metadata_, onlineFunc, offlineFunc, queryAutoSyncFunc); + if (errCode == E_OK) { + syncInterface_ = syncInterface; + syncInterface->IncRefCount(); + label_ = syncEngine_->GetLabel(); + return E_OK; + } else { + LOGE("[Syncer] SyncEngine init failed! err:%d.", errCode); + if (syncEngine_ != nullptr) { + RefObject::KillAndDecObjRef(syncEngine_); + syncEngine_ = nullptr; + } + return errCode; + } +} + +int GenericSyncer::CheckSyncActive(ISyncInterface *syncInterface, bool isNeedActive) +{ + bool isSyncDualTupleMode = syncInterface->GetDbProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, + false); + if (!isSyncDualTupleMode || isNeedActive) { + return E_OK; + } + LOGI("[Syncer] syncer no need to active"); + int errCode = BuildSyncEngine(); + if (errCode != E_OK) { + return errCode; + } + return -E_NO_NEED_ACTIVE; +} + +uint32_t GenericSyncer::GenerateSyncId() +{ + std::lock_guard lock(syncIdLock_); + currentSyncId_++; + // if overflow, reset to 1 + if (currentSyncId_ <= 0) { + currentSyncId_ = MIN_VALID_SYNC_ID; + } + return currentSyncId_; +} + +bool GenericSyncer::IsValidMode(int mode) const +{ + if ((mode >= SyncModeType::INVALID_MODE) || (mode < SyncModeType::PUSH)) { + LOGE("[Syncer] Sync mode is not valid!"); + return false; + } + return true; +} + +int GenericSyncer::SyncConditionCheck(QuerySyncObject &query, int mode, bool isQuerySync, + const std::vector &devices) const +{ + (void)query; + (void)mode; + (void)isQuerySync; + (void)(devices); + return E_OK; +} + +bool GenericSyncer::IsValidDevices(const std::vector &devices) const +{ + if (devices.empty()) { + LOGE("[Syncer] devices is empty!"); + return false; + } + return true; +} + +void GenericSyncer::ClearSyncOperations(bool isClosedOperation) +{ + std::vector syncOperation; + { + std::lock_guard lock(operationMapLock_); + for (auto &item : syncOperationMap_) { + bool isBlockSync = item.second->IsBlockSync(); + if (isBlockSync || !isClosedOperation) { + int status = (!isClosedOperation) ? SyncOperation::OP_USER_CHANGED : SyncOperation::OP_FAILED; + item.second->SetUnfinishedDevStatus(status); + RefObject::IncObjRef(item.second); + syncOperation.push_back(item.second); + } + } + } + for (auto &operation : syncOperation) { + // block sync operation or userChange will trigger remove sync operation + // caller won't blocked for block sync + // caller won't blocked for userChange operation no mater it is block or non-block sync + TriggerSyncFinished(operation); + RefObject::DecObjRef(operation); + } + { + std::lock_guard lock(operationMapLock_); + for (auto &iter : syncOperationMap_) { + RefObject::KillAndDecObjRef(iter.second); + iter.second = nullptr; + } + syncOperationMap_.clear(); + } + { + std::lock_guard lock(syncIdLock_); + connectionIdMap_.clear(); + syncIdMap_.clear(); + } +} + +void GenericSyncer::TriggerSyncFinished(SyncOperation *operation) +{ + if (operation != nullptr && operation->CheckIsAllFinished()) { + operation->Finished(); + } +} + +void GenericSyncer::OnSyncFinished(int syncId) +{ + (void)(RemoveSyncOperation(syncId)); +} + +int GenericSyncer::SyncModuleInit() +{ + static bool isInit = false; + std::lock_guard lock(moduleInitLock_); + if (!isInit) { + int errCode = SyncResourceInit(); + if (errCode != E_OK) { + return errCode; + } + isInit = true; + return E_OK; + } + return E_OK; +} + +int GenericSyncer::SyncResourceInit() +{ + int errCode = TimeSync::RegisterTransformFunc(); + if (errCode != E_OK) { + LOGE("Register timesync message transform func ERR!"); + return errCode; + } + errCode = SingleVerSerializeManager::RegisterTransformFunc(); + if (errCode != E_OK) { + LOGE("Register SingleVerDataSync message transform func ERR!"); + return errCode; + } +#ifndef OMIT_MULTI_VER + errCode = CommitHistorySync::RegisterTransformFunc(); + if (errCode != E_OK) { + LOGE("Register CommitHistorySync message transform func ERR!"); + return errCode; + } + errCode = MultiVerDataSync::RegisterTransformFunc(); + if (errCode != E_OK) { + LOGE("Register MultiVerDataSync message transform func ERR!"); + return errCode; + } + errCode = ValueSliceSync::RegisterTransformFunc(); + if (errCode != E_OK) { + LOGE("Register ValueSliceSync message transform func ERR!"); + return errCode; + } +#endif + errCode = DeviceManager::RegisterTransformFunc(); + if (errCode != E_OK) { + LOGE("Register DeviceManager message transform func ERR!"); + return errCode; + } + errCode = AbilitySync::RegisterTransformFunc(); + if (errCode != E_OK) { + LOGE("Register AbilitySync message transform func ERR!"); + return errCode; + } + return E_OK; +} + +int GenericSyncer::GetQueuedSyncSize(int *queuedSyncSize) const +{ + if (queuedSyncSize == nullptr) { + return -E_INVALID_ARGS; + } + std::lock_guard lock(queuedManualSyncLock_); + *queuedSyncSize = queuedManualSyncSize_; + LOGI("[GenericSyncer] GetQueuedSyncSize:%d", queuedManualSyncSize_); + return E_OK; +} + +int GenericSyncer::SetQueuedSyncLimit(const int *queuedSyncLimit) +{ + if (queuedSyncLimit == nullptr) { + return -E_INVALID_ARGS; + } + std::lock_guard lock(queuedManualSyncLock_); + queuedManualSyncLimit_ = *queuedSyncLimit; + LOGI("[GenericSyncer] SetQueuedSyncLimit:%d", queuedManualSyncLimit_); + return E_OK; +} + +int GenericSyncer::GetQueuedSyncLimit(int *queuedSyncLimit) const +{ + if (queuedSyncLimit == nullptr) { + return -E_INVALID_ARGS; + } + std::lock_guard lock(queuedManualSyncLock_); + *queuedSyncLimit = queuedManualSyncLimit_; + LOGI("[GenericSyncer] GetQueuedSyncLimit:%d", queuedManualSyncLimit_); + return E_OK; +} + +bool GenericSyncer::IsManualSync(int inMode) const +{ + int mode = SyncOperation::TransferSyncMode(inMode); + if ((mode == SyncModeType::PULL) || (mode == SyncModeType::PUSH) || (mode == SyncModeType::PUSH_AND_PULL) || + (mode == SyncModeType::SUBSCRIBE_QUERY) || (mode == SyncModeType::UNSUBSCRIBE_QUERY)) { + return true; + } + return false; +} + +int GenericSyncer::AddQueuedManualSyncSize(int mode, bool wait) +{ + if (IsManualSync(mode) && (!wait)) { + std::lock_guard lock(queuedManualSyncLock_); + if (!manualSyncEnable_) { + LOGI("[GenericSyncer] manualSyncEnable is Disable"); + return -E_BUSY; + } + queuedManualSyncSize_++; + } + return E_OK; +} + +bool GenericSyncer::IsQueuedManualSyncFull(int mode, bool wait) const +{ + std::lock_guard lock(queuedManualSyncLock_); + if (IsManualSync(mode) && (!manualSyncEnable_)) { + LOGI("[GenericSyncer] manualSyncEnable_:false"); + return true; + } + if (IsManualSync(mode) && (!wait)) { + if (queuedManualSyncSize_ < queuedManualSyncLimit_) { + return false; + } else { + LOGD("[GenericSyncer] queuedManualSyncSize_:%d < queuedManualSyncLimit_:%d", queuedManualSyncSize_, + queuedManualSyncLimit_); + return true; + } + } else { + return false; + } +} + +void GenericSyncer::SubQueuedSyncSize(void) +{ + std::lock_guard lock(queuedManualSyncLock_); + queuedManualSyncSize_--; + if (queuedManualSyncSize_ < 0) { + LOGE("[GenericSyncer] queuedManualSyncSize_ < 0!"); + queuedManualSyncSize_ = 0; + } +} + +int GenericSyncer::DisableManualSync(void) +{ + std::lock_guard lock(queuedManualSyncLock_); + if (queuedManualSyncSize_ > 0) { + LOGD("[GenericSyncer] DisableManualSync fail, queuedManualSyncSize_:%d", queuedManualSyncSize_); + return -E_BUSY; + } + manualSyncEnable_ = false; + LOGD("[GenericSyncer] DisableManualSync ok"); + return E_OK; +} + +int GenericSyncer::EnableManualSync(void) +{ + std::lock_guard lock(queuedManualSyncLock_); + manualSyncEnable_ = true; + LOGD("[GenericSyncer] EnableManualSync ok"); + return E_OK; +} + +int GenericSyncer::GetLocalIdentity(std::string &outTarget) const +{ + std::string deviceId; + int errCode = RuntimeContext::GetInstance()->GetLocalIdentity(deviceId); + if (errCode != E_OK) { + LOGE("[GenericSyncer] GetLocalIdentity fail errCode:%d", errCode); + return errCode; + } + outTarget = DBCommon::TransferHashString(deviceId); + return E_OK; +} + +void GenericSyncer::GetOnlineDevices(std::vector &devices) const +{ + // Get devices from AutoLaunch first. + if (syncInterface_ == nullptr) { + LOGI("[Syncer] GetOnlineDevices syncInterface_ is nullptr"); + return; + } + bool isSyncDualTupleMode = syncInterface_->GetDbProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, + false); + std::string identifier; + if (isSyncDualTupleMode) { + identifier = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::DUAL_TUPLE_IDENTIFIER_DATA, ""); + } else { + identifier = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + } + RuntimeContext::GetInstance()->GetAutoLaunchSyncDevices(identifier, devices); + if (!devices.empty()) { + return; + } + std::lock_guard lock(syncerLock_); + if (closing_) { + LOGE("[Syncer] Syncer is closing, return!"); + return; + } + if (syncEngine_ != nullptr) { + syncEngine_->GetOnlineDevices(devices); + } +} + +int GenericSyncer::SetSyncRetry(bool isRetry) +{ + if (syncEngine_ == nullptr) { + return -E_NOT_INIT; + } + syncEngine_->SetSyncRetry(isRetry); + return E_OK; +} + +int GenericSyncer::SetEqualIdentifier(const std::string &identifier, const std::vector &targets) +{ + std::lock_guard lock(syncerLock_); + if (syncEngine_ == nullptr) { + return -E_NOT_INIT; + } + int errCode = syncEngine_->SetEqualIdentifier(identifier, targets); + if (errCode == E_OK) { + syncEngine_->SetEqualIdentifierMap(identifier, targets); + } + return errCode; +} + +std::string GenericSyncer::GetSyncDevicesStr(const std::vector &devices) const +{ + std::string syncDevices; + for (const auto &dev:devices) { + syncDevices += STR_MASK(dev); + syncDevices += ","; + } + return syncDevices.substr(0, syncDevices.size() - 1); +} + +int GenericSyncer::StatusCheck() const +{ + if (!initialized_) { + LOGE("[Syncer] Syncer is not initialized, return!"); + return -E_NOT_INIT; + } + if (closing_) { + LOGE("[Syncer] Syncer is closing, return!"); + return -E_BUSY; + } + return E_OK; +} + +int GenericSyncer::SyncParamCheck(const SyncParma ¶m) const +{ + std::lock_guard lock(syncerLock_); + int errCode = StatusCheck(); + if (errCode != E_OK) { + return errCode; + } + if (!IsValidDevices(param.devices) || !IsValidMode(param.mode)) { + return -E_INVALID_ARGS; + } + if (IsQueuedManualSyncFull(param.mode, param.wait)) { + LOGE("[Syncer] -E_BUSY"); + return -E_BUSY; + } + QuerySyncObject syncQuery = param.syncQuery; + return SyncConditionCheck(syncQuery, param.mode, param.isQuerySync, param.devices); +} + +void GenericSyncer::InitSyncOperation(SyncOperation *operation, const SyncParma ¶m) +{ + operation->SetIdentifier(syncInterface_->GetIdentifier()); + operation->Initialize(); + operation->OnKill(std::bind(&GenericSyncer::SyncOperationKillCallback, this, operation->GetSyncId())); + std::function onFinished = std::bind(&GenericSyncer::OnSyncFinished, this, std::placeholders::_1); + operation->SetOnSyncFinished(onFinished); + operation->SetOnSyncFinalize(param.onFinalize); + if (param.isQuerySync) { + operation->SetQuery(param.syncQuery); + } +} + +int GenericSyncer::BuildSyncEngine() +{ + if (syncEngine_ != nullptr) { + return E_OK; + } + syncEngine_ = CreateSyncEngine(); + if (syncEngine_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + syncEngine_->OnLastRef([this]() { + LOGD("[Syncer] SyncEngine finalized"); + engineFinalize_ = true; + engineFinalizeCv_.notify_all(); + }); + return E_OK; +} + +void GenericSyncer::Dump(int fd) +{ + if (syncEngine_ == nullptr) { + return; + } + syncEngine_->Dump(fd); +} + +SyncerBasicInfo GenericSyncer::DumpSyncerBasicInfo() +{ + SyncerBasicInfo baseInfo; + RefObject::IncObjRef(syncEngine_); + if (syncEngine_ == nullptr) { + return baseInfo; + } + baseInfo.isSyncActive = syncEngine_->IsEngineActive(); + RefObject::DecObjRef(syncEngine_); + return baseInfo; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/generic_syncer.h b/mock/distributeddb/syncer/src/generic_syncer.h new file mode 100644 index 0000000000000000000000000000000000000000..0d399e90a3eb7df46833e73d9c35825e0029773e --- /dev/null +++ b/mock/distributeddb/syncer/src/generic_syncer.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GENRIC_SYNCER_H +#define GENRIC_SYNCER_H + +#include +#include +#include + +#include "isyncer.h" +#include "isync_engine.h" +#include "meta_data.h" +#include "sync_operation.h" +#include "time_helper.h" + +namespace DistributedDB { +class GenericSyncer : public virtual ISyncer { +using DataChangedFunc = std::function; + +public: + GenericSyncer(); + ~GenericSyncer() override; + + // Init the Syncer modules + int Initialize(ISyncInterface *syncInterface, bool isNeedActive) override; + + // Close + int Close(bool isClosedOperation) override; + + // Sync function. + // param devices: The device id list. + // param mode: Sync mode, see SyncMode. + // param onComplete: The syncer finish callback. set by caller + // param onFinalize: will be callback when this Sync Operation finalized. + // return a Sync id. It will return a positive value if failed, + int Sync(const std::vector &devices, int mode, + const std::function &)> &onComplete, + const std::function &onFinalize, bool wait) override; + + // Sync function. use SyncParma to reduce parameter. + int Sync(const SyncParma ¶m); + + int Sync(const SyncParma ¶m, uint64_t connectionId) override; + + // Remove the operation, with the given syncId, used to clean resource if sync finished or failed. + int RemoveSyncOperation(int syncId) override; + + int StopSync(uint64_t connectionId) override; + + // Get The current virtual timestamp + uint64_t GetTimestamp() override; + + // Get manual sync queue size + int GetQueuedSyncSize(int *queuedSyncSize) const override; + + // Set manual sync queue limit + int SetQueuedSyncLimit(const int *queuedSyncLimit) override; + + // Get manual sync queue limit + int GetQueuedSyncLimit(int *queuedSyncLimit) const override; + + // Disable add new manual sync, for rekey + int DisableManualSync(void) override; + + // Enable add new manual sync, for rekey + int EnableManualSync(void) override; + + // Get local deviceId, is hashed + int GetLocalIdentity(std::string &outTarget) const override; + + // Set Manual Sync retry config + int SetSyncRetry(bool isRetry) override; + + // Set an equal identifier for this database, After this called, send msg to the target will use this identifier + int SetEqualIdentifier(const std::string &identifier, const std::vector &targets) override; + + // Inner function, Used for subscribe sync + int Sync(const InternalSyncParma ¶m); + + // Remote data changed callback + virtual void RemoteDataChanged(const std::string &device) = 0; + + virtual void RemoteDeviceOffline(const std::string &device) = 0; + + void Dump(int fd) override; + + SyncerBasicInfo DumpSyncerBasicInfo() override; + +protected: + + // trigger query auto sync or auto subscribe + // trigger auto subscribe only when subscribe task is failed triggered by remote db opened + // it won't be triggered again when subscribe task success + virtual void QueryAutoSync(const InternalSyncParma ¶m); + + // Create a sync engine, if has memory error, will return nullptr. + virtual ISyncEngine *CreateSyncEngine() = 0; + + virtual int PrepareSync(const SyncParma ¶m, uint32_t syncId, uint64_t connectionId); + + // Add a Sync Operation, after call this function, the operation will be start + virtual void AddSyncOperation(SyncOperation *operation); + + // Used to set to the SyncOperation Onkill + virtual void SyncOperationKillCallbackInner(int syncId); + + // Used to set to the SyncOperation Onkill + void SyncOperationKillCallback(int syncId); + + // Init the metadata + int InitMetaData(ISyncInterface *syncInterface); + + // Init the TimeHelper + int InitTimeHelper(ISyncInterface *syncInterface); + + // Init the Sync engine + int InitSyncEngine(ISyncInterface *syncInterface); + + int CheckSyncActive(ISyncInterface *syncInterface, bool isNeedActive); + + // Used to general a sync id, maybe it is currentSyncId++; + // The return value is sync id. + uint32_t GenerateSyncId(); + + // Check if the mode arg is valid + bool IsValidMode(int mode) const; + + virtual int SyncConditionCheck(QuerySyncObject &query, int mode, bool isQuerySync, + const std::vector &devices) const; + + // Check if the devices arg is valid + bool IsValidDevices(const std::vector &devices) const; + + // Used Clear all SyncOperations. + // isClosedOperation is false while userChanged + void ClearSyncOperations(bool isClosedOperation); + + void TriggerSyncFinished(SyncOperation *operation); + + // Callback when the special sync finished. + void OnSyncFinished(int syncId); + + bool IsManualSync(int inMode) const; + + int AddQueuedManualSyncSize(int mode, bool wait); + + bool IsQueuedManualSyncFull(int mode, bool wait) const; + + void SubQueuedSyncSize(void); + + void GetOnlineDevices(std::vector &devices) const; + + std::string GetSyncDevicesStr(const std::vector &devices) const; + + void InitSyncOperation(SyncOperation *operation, const SyncParma ¶m); + + int StatusCheck() const; + + int SyncParamCheck(const SyncParma ¶m) const; + + int BuildSyncEngine(); + + static int SyncModuleInit(); + + static int SyncResourceInit(); + + static const int MIN_VALID_SYNC_ID; + static std::mutex moduleInitLock_; + + // Used to general the next sync id. + static int currentSyncId_; + static std::mutex syncIdLock_; + // For sync in progress. + std::map> connectionIdMap_; + std::map syncIdMap_; + + ISyncEngine *syncEngine_; + ISyncInterface *syncInterface_; + std::shared_ptr timeHelper_; + std::shared_ptr metadata_; + bool initialized_; + std::mutex operationMapLock_; + std::map syncOperationMap_; + int queuedManualSyncSize_; + int queuedManualSyncLimit_; + bool manualSyncEnable_; + bool closing_; + mutable std::mutex queuedManualSyncLock_; + mutable std::mutex syncerLock_; + std::string label_; + bool engineFinalize_; + std::condition_variable engineFinalizeCv_; +}; +} // namespace DistributedDB + +#endif // GENRIC_SYNCER_H diff --git a/mock/distributeddb/syncer/src/isync_engine.h b/mock/distributeddb/syncer/src/isync_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..dcf47dfff24ac206085c91a2fdc7e56fc5398978 --- /dev/null +++ b/mock/distributeddb/syncer/src/isync_engine.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_SYNC_ENGINE_H +#define I_SYNC_ENGINE_H + +#include +#include + +#include "ikvdb_sync_interface.h" +#include "meta_data.h" +#include "ref_object.h" +#include "sync_operation.h" + +namespace DistributedDB { +class ISyncEngine : public virtual RefObject { +public: + // Do some init things + virtual int Initialize(ISyncInterface *syncInterface, std::shared_ptr &metadata, + const std::function &onRemoteDataChanged, + const std::function &offlineChanged, + const std::function &queryAutoSyncCallback) = 0; + + // Do some things, when db close. + virtual int Close() = 0; + + // Alloc and Add sync SyncTarget + // return E_OK if operator success. + virtual int AddSyncOperation(SyncOperation *operation) = 0; + + // Clear the SyncTarget matched the syncId. + virtual void RemoveSyncOperation(int syncId) = 0; + + // notify other devices data has changed + virtual void BroadCastDataChanged() const = 0; + + // Get Online devices + virtual void GetOnlineDevices(std::vector &devices) const = 0; + + // Register the device connect callback, this function must be called after Engine initted + virtual void RegConnectCallback() = 0; + + // Get the database identifier + virtual std::string GetLabel() const = 0; + + // Set Manual Sync retry config + virtual void SetSyncRetry(bool isRetry) = 0; + + // Set an equal identifier for this database, After this called, send msg to the target will use this identifier + virtual int SetEqualIdentifier(const std::string &identifier, const std::vector &targets) = 0; + + // Set record device equal identifier when called in import/rekey scene when restart syncer + virtual void SetEqualIdentifier() = 0; + + virtual void SetEqualIdentifierMap(const std::string &identifier, const std::vector &targets) = 0; + + // Add auto subscribe timer when start sync engine, used for auto subscribe failed subscribe task when db online + virtual int StartAutoSubscribeTimer() = 0; + + // Stop auto subscribe timer when start sync engine + virtual void StopAutoSubscribeTimer() = 0; + + // Check if number of subscriptions out of limit + virtual int SubscribeLimitCheck(const std::vector &devices, QuerySyncObject &query) const = 0; + + // Check if the Sync Engine is active, some times synchronization is not allowed + virtual bool IsEngineActive() const = 0; + + virtual void SchemaChange() = 0; + + virtual void Dump(int fd) = 0; + +protected: + virtual ~ISyncEngine() {}; +}; +} // namespace DistributedDB + +#endif // I_SYNC_ENGINE_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/isync_state_machine.h b/mock/distributeddb/syncer/src/isync_state_machine.h new file mode 100644 index 0000000000000000000000000000000000000000..ac0ed90d6ea86812b00b533ea551aea3c2a57a6a --- /dev/null +++ b/mock/distributeddb/syncer/src/isync_state_machine.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_SYNC_STATE_MACHINE_H +#define I_SYNC_STATE_MACHINE_H + +#include + +#include "icommunicator.h" +#include "ikvdb_sync_interface.h" +#include "query_sync_object.h" +#include "sync_target.h" +#include "sync_task_context.h" + +namespace DistributedDB { +class ISyncStateMachine { +public: + virtual ~ISyncStateMachine() {}; + + // Init the SyncStateMachine, this function must be called before any other call. + virtual int Initialize(ISyncTaskContext *context, ISyncInterface *syncInterface, + std::shared_ptr &metadata, ICommunicator *communicator) = 0; + + // start a sync step + virtual int StartSync() = 0; + + // send Message to the StateMachine + virtual int ReceiveMessageCallback(Message *inMsg) = 0; + + // call when timeout + virtual int TimeoutCallback(TimerId timerId) = 0; + + // Force stop the state machine + virtual void Abort() = 0; + + // Called by CommErrHandler, Sub class should realize this function to abort sync when handle err + virtual void CommErrAbort() = 0; + + // start a timer to ResetWatchDog when sync data one (key,value) size bigger than mtu + virtual bool StartFeedDogForSync(uint32_t time, SyncDirectionFlag flag) = 0; + + // stop timer to ResetWatchDog when sync data one (key,value) size bigger than mtu + virtual void StopFeedDogForSync(SyncDirectionFlag flag) = 0; + + // check if need trigger query auto sync and get query from inMsg + virtual bool IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query) = 0; +}; +} // namespace DistributedDB + +#endif // I_SYNC_STATE_MACHINE_H diff --git a/mock/distributeddb/syncer/src/isync_target.h b/mock/distributeddb/syncer/src/isync_target.h new file mode 100644 index 0000000000000000000000000000000000000000..e8fd05cf24eb47d490a88420c5722dfd54817ace --- /dev/null +++ b/mock/distributeddb/syncer/src/isync_target.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_SYNC_TARGET_H +#define I_SYNC_TARGET_H + +#include "sync_operation.h" + +namespace DistributedDB { +class ISyncTarget { +public: + enum TaskType { + REQUEST = 1, + RESPONSE + }; + + virtual ~ISyncTarget() {}; + + // Get the Sync Id of this task + virtual int GetSyncId() const = 0; + + // Set the type of this task request or response + virtual void SetTaskType(int taskType) = 0; + + // Get the type of this task request or response + virtual int GetTaskType() const = 0; + + // Set the mode of this task request or response + virtual void SetMode(int mode) = 0; + + // Get the mode of this task request or response + virtual int GetMode() const = 0; + + // Set a SyncOperation + virtual void SetSyncOperation(SyncOperation *operation) = 0; + + // Get a SyncOperation + virtual void GetSyncOperation(SyncOperation *&operation) const = 0; + + // Is this target is an auto sync + virtual bool IsAutoSync() const = 0; + + virtual uint32_t GetResponseSessionId() const = 0; +}; +} // namespace DistributedDB + +#endif // I_SYNC_TARGET_H diff --git a/mock/distributeddb/syncer/src/isync_task_context.h b/mock/distributeddb/syncer/src/isync_task_context.h new file mode 100644 index 0000000000000000000000000000000000000000..6ae77de11df4e7b2b8fb19171650ae01d7298296 --- /dev/null +++ b/mock/distributeddb/syncer/src/isync_task_context.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_SYNC_TASK_CONTEXT_H +#define I_SYNC_TASK_CONTEXT_H + + +#include "icommunicator.h" +#include "ikvdb_sync_interface.h" +#include "meta_data.h" +#include "query_sync_object.h" +#include "runtime_context.h" +#include "sync_operation.h" +#include "sync_target.h" +#include "time_helper.h" +namespace DistributedDB { +using CommErrHandler = std::function; + +class ISyncTaskContext : public virtual RefObject { +public: + enum RETRY_STATUS { NO_NEED_RETRY, NEED_RETRY }; + + enum TASK_EXEC_STATUS { INIT, RUNNING, FAILED, FINISHED }; + + // Initialize the context + virtual int Initialize(const std::string &deviceId, ISyncInterface *syncInterface, + std::shared_ptr &metadata, ICommunicator *communicator) = 0; + + // Add a sync task target with the operation to the queue + virtual int AddSyncOperation(SyncOperation *operation) = 0; + + // Add a sync task target to the queue + virtual int AddSyncTarget(ISyncTarget *target) = 0; + + // Set the status of this task cotext + virtual void SetOperationStatus(int status) = 0; + + // Clear the data of this context + virtual void Clear() = 0; + + // Remove a sync target by syncId + virtual int RemoveSyncOperation(int syncId) = 0; + + // If the targetQueue is empty + virtual bool IsTargetQueueEmpty() const = 0; + + // Get the status of this task + virtual int GetOperationStatus() const = 0; + + // Set the mode of this task + virtual void SetMode(int mode) = 0; + + // Get the mode of this task + virtual int GetMode() const = 0; + + // Move to next target to sync + virtual void MoveToNextTarget() = 0; + + // Get the current task syncId + virtual uint32_t GetSyncId() const = 0; + + // Get the current task deviceId. + virtual std::string GetDeviceId() const = 0; + + virtual void SetTaskExecStatus(int status) = 0; + + virtual int GetTaskExecStatus() const = 0; + + virtual bool IsAutoSync() const = 0; + + virtual bool IsSyncTaskNeedRetry() const = 0; + + virtual void SetSyncRetry(bool isRetry) = 0; + + virtual int GetSyncRetryTimes() const = 0; + + virtual int GetSyncRetryTimeout(int retryTime) const = 0; + + // Set a Timer used for timeout + virtual int StartTimer() = 0; + + // delete timer + virtual void StopTimer() = 0; + + // modify timer + virtual int ModifyTimer(int milliSeconds) = 0; + + // Set a RetryTime for the sync task + virtual void SetRetryTime(int retryTime) = 0; + + // Get a RetryTime for the sync task + virtual int GetRetryTime() const = 0; + + // Set Retry status for the sync task + virtual void SetRetryStatus(int isNeedRetry) = 0; + + // Get Retry status for the sync task + virtual int GetRetryStatus() const = 0; + + virtual TimerId GetTimerId() const = 0; + + virtual void IncSequenceId() = 0; + + virtual uint32_t GetSequenceId() const = 0; + + virtual void ReSetSequenceId() = 0; + + virtual uint32_t GetRequestSessionId() const = 0; + + virtual int GetTimeoutTime() const = 0; + + virtual void SetTimeOffset(TimeOffset offset) = 0; + + virtual TimeOffset GetTimeOffset() const = 0; + + virtual void SetTimeoutCallback(const TimerAction &timeOutCallback) = 0; + + virtual int StartStateMachine() = 0; + + virtual int ReceiveMessageCallback(Message *inMsg) = 0; + + virtual void RegOnSyncTask(const std::function &callback) = 0; + + virtual int IncUsedCount() = 0; + + virtual void SafeExit() = 0; + + // Get current localtime from TimeHelper + virtual Timestamp GetCurrentLocalTime() const = 0; + + // Set the remount software version num + virtual void SetRemoteSoftwareVersion(uint32_t version) = 0; + + // Get the remount software version num + virtual uint32_t GetRemoteSoftwareVersion() const = 0; + + // Get the remount software version id, when called GetRemoteSoftwareVersion this id will be increase. + // Used to check if the version num is is overdue + virtual uint64_t GetRemoteSoftwareVersionId() const = 0; + + // Judge if the communicator is normal + virtual bool IsCommNormal() const = 0; + + // Judge if the sec option check is err + virtual int GetTaskErrCode() const = 0; + + virtual void SetTaskErrCode(int errCode) = 0; + + virtual void ClearAllSyncTask() = 0; + + virtual bool IsAutoLiftWaterMark() const = 0; + + virtual void IncNegotiationCount() = 0; + + virtual bool IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query) = 0; + + virtual bool IsAutoSubscribe() const = 0; + + // some sync task can be skipped if there is no change in data base since last sync + virtual bool IsCurrentSyncTaskCanBeSkipped() const = 0; + + virtual void SetIsNeedResetAbilitySync(bool isNeedReset) = 0; + + virtual void SchemaChange() = 0; + + virtual void Dump(int fd) = 0; +protected: + virtual ~ISyncTaskContext() {}; +}; +} // namespace DistributedDB + +#endif // I_SYNC_TASK_CONTEXT_H diff --git a/mock/distributeddb/syncer/src/meta_data.cpp b/mock/distributeddb/syncer/src/meta_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc97fb4479db82733a9d11f57550100d547ea6df --- /dev/null +++ b/mock/distributeddb/syncer/src/meta_data.cpp @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "meta_data.h" + +#include +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "hash.h" +#include "log_print.h" +#include "securec.h" +#include "sync_types.h" +#include "time_helper.h" + +namespace DistributedDB { +namespace { + const int STR_TO_LL_BY_DEVALUE = 10; + // store local timeoffset;this is a special key; + const std::string LOCALTIME_OFFSET_KEY = "localTimeOffset"; + const std::string DEVICEID_PREFIX_KEY = "deviceId"; +} + +Metadata::Metadata() + : localTimeOffset_(0), + naturalStoragePtr_(nullptr), + lastLocalTime_(0) +{} + +Metadata::~Metadata() +{ + naturalStoragePtr_ = nullptr; + metadataMap_.clear(); +} + +int Metadata::Initialize(ISyncInterface* storage) +{ + naturalStoragePtr_ = storage; + std::vector key; + std::vector timeOffset; + DBCommon::StringToVector(LOCALTIME_OFFSET_KEY, key); + + int errCode = GetMetadataFromDb(key, timeOffset); + if (errCode == -E_NOT_FOUND) { + int err = SaveLocalTimeOffset(TimeHelper::BASE_OFFSET); + if (err != E_OK) { + LOGD("[Metadata][Initialize]SaveLocalTimeOffset failed errCode:%d", err); + return err; + } + } else if (errCode == E_OK) { + localTimeOffset_ = StringToLong(timeOffset); + } else { + LOGE("Metadata::Initialize get meatadata from db failed,err=%d", errCode); + return errCode; + } + { + std::lock_guard lockGuard(metadataLock_); + metadataMap_.clear(); + } + (void)querySyncWaterMarkHelper_.Initialize(storage); + return LoadAllMetadata(); +} + +int Metadata::SaveTimeOffset(const DeviceID &deviceId, TimeOffset inValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata, true); + metadata.timeOffset = inValue; + metadata.lastUpdateTime = TimeHelper::GetSysCurrentTime(); + LOGD("Metadata::SaveTimeOffset = %" PRId64 " dev %s", inValue, STR_MASK(deviceId)); + return SaveMetaDataValue(deviceId, metadata); +} + +void Metadata::GetTimeOffset(const DeviceID &deviceId, TimeOffset &outValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata, true); + outValue = metadata.timeOffset; +} + +void Metadata::GetLocalWaterMark(const DeviceID &deviceId, uint64_t &outValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata, true); + outValue = metadata.localWaterMark; +} + +int Metadata::SaveLocalWaterMark(const DeviceID &deviceId, uint64_t inValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata, true); + metadata.localWaterMark = inValue; + LOGD("Metadata::SaveLocalWaterMark = %" PRIu64, inValue); + return SaveMetaDataValue(deviceId, metadata); +} + +void Metadata::GetPeerWaterMark(const DeviceID &deviceId, uint64_t &outValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata, true); + outValue = metadata.peerWaterMark; +} + +int Metadata::SavePeerWaterMark(const DeviceID &deviceId, uint64_t inValue, bool isNeedHash) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata, isNeedHash); + metadata.peerWaterMark = inValue; + LOGD("Metadata::SavePeerWaterMark = %" PRIu64, inValue); + return SaveMetaDataValue(deviceId, metadata); +} + +int Metadata::SaveLocalTimeOffset(TimeOffset timeOffset) +{ + std::string timeOffsetString = std::to_string(timeOffset); + std::vector timeOffsetValue(timeOffsetString.begin(), timeOffsetString.end()); + std::vector localTimeOffsetValue( + LOCALTIME_OFFSET_KEY.begin(), LOCALTIME_OFFSET_KEY.end()); + + std::lock_guard lockGuard(localTimeOffsetLock_); + localTimeOffset_ = timeOffset; + LOGD("Metadata::SaveLocalTimeOffset offset = %" PRId64, timeOffset); + int errCode = SetMetadataToDb(localTimeOffsetValue, timeOffsetValue); + if (errCode != E_OK) { + LOGE("Metadata::SaveLocalTimeOffset SetMetadataToDb failed errCode:%d", errCode); + } + return errCode; +} + +TimeOffset Metadata::GetLocalTimeOffset() const +{ + TimeOffset localTimeOffset = localTimeOffset_.load(std::memory_order_seq_cst); + return localTimeOffset; +} + +int Metadata::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) +{ + return EraseDeviceWaterMark(deviceId, isNeedHash, ""); +} + +int Metadata::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, const std::string &tableName) +{ + // try to erase all the waterMark + // erase deleteSync recv waterMark + WaterMark waterMark = 0; + int errCodeDeleteSync = SetRecvDeleteSyncWaterMark(deviceId, waterMark); + // erase querySync recv waterMark + int errCodeQuerySync = ResetRecvQueryWaterMark(deviceId, tableName); + // peerWaterMark must be erased at last + int errCode = SavePeerWaterMark(deviceId, 0, isNeedHash); + if (errCode != E_OK) { + LOGE("[Metadata] erase peerWaterMark failed errCode:%d", errCode); + return errCode; + } + if (errCodeQuerySync != E_OK) { + LOGE("[Metadata] erase queryWaterMark failed errCode:%d", errCodeQuerySync); + return errCodeQuerySync; + } + if (errCodeDeleteSync != E_OK) { + LOGE("[Metadata] erase deleteWaterMark failed errCode:%d", errCodeDeleteSync); + return errCodeDeleteSync; + } + return E_OK; +} + +void Metadata::SetLastLocalTime(Timestamp lastLocalTime) +{ + std::lock_guard lock(lastLocalTimeLock_); + if (lastLocalTime > lastLocalTime_) { + lastLocalTime_ = lastLocalTime; + } +} + +Timestamp Metadata::GetLastLocalTime() const +{ + std::lock_guard lock(lastLocalTimeLock_); + return lastLocalTime_; +} + +int Metadata::SaveMetaDataValue(const DeviceID &deviceId, const MetaDataValue &inValue) +{ + std::vector value; + int errCode = SerializeMetaData(inValue, value); + if (errCode != E_OK) { + return errCode; + } + + DeviceID hashDeviceId; + GetHashDeviceId(deviceId, hashDeviceId, true); + std::vector key; + DBCommon::StringToVector(hashDeviceId, key); + errCode = SetMetadataToDb(key, value); + if (errCode != E_OK) { + LOGE("Metadata::SetMetadataToDb failed errCode:%d", errCode); + return errCode; + } + PutMetadataToMap(hashDeviceId, inValue); + return E_OK; +} + +void Metadata::GetMetaDataValue(const DeviceID &deviceId, MetaDataValue &outValue, bool isNeedHash) +{ + DeviceID hashDeviceId; + GetHashDeviceId(deviceId, hashDeviceId, isNeedHash); + GetMetadataFromMap(hashDeviceId, outValue); +} + +int Metadata::SerializeMetaData(const MetaDataValue &inValue, std::vector &outValue) +{ + outValue.resize(sizeof(MetaDataValue)); + errno_t err = memcpy_s(&outValue[0], outValue.size(), &inValue, sizeof(MetaDataValue)); + if (err != EOK) { + return -E_SECUREC_ERROR; + } + return E_OK; +} + +int Metadata::DeSerializeMetaData(const std::vector &inValue, MetaDataValue &outValue) const +{ + if (inValue.empty()) { + return -E_INVALID_ARGS; + } + + errno_t err = memcpy_s(&outValue, sizeof(MetaDataValue), &inValue[0], inValue.size()); + if (err != EOK) { + return -E_SECUREC_ERROR; + } + return E_OK; +} + +int Metadata::GetMetadataFromDb(const std::vector &key, std::vector &outValue) const +{ + if (naturalStoragePtr_ == nullptr) { + return -E_INVALID_DB; + } + return naturalStoragePtr_->GetMetaData(key, outValue); +} + +int Metadata::SetMetadataToDb(const std::vector &key, const std::vector &inValue) +{ + if (naturalStoragePtr_ == nullptr) { + return -E_INVALID_DB; + } + return naturalStoragePtr_->PutMetaData(key, inValue); +} + +int Metadata::DeleteMetaDataFromDB(const std::vector &keys) const +{ + if (naturalStoragePtr_ == nullptr) { + return -E_INVALID_DB; + } + return naturalStoragePtr_->DeleteMetaData(keys); +} + +void Metadata::PutMetadataToMap(const DeviceID &deviceId, const MetaDataValue &value) +{ + metadataMap_[deviceId] = value; +} + +void Metadata::GetMetadataFromMap(const DeviceID &deviceId, MetaDataValue &outValue) +{ + outValue = metadataMap_[deviceId]; +} + +int64_t Metadata::StringToLong(const std::vector &value) const +{ + std::string valueString(value.begin(), value.end()); + int64_t longData = std::strtoll(valueString.c_str(), nullptr, STR_TO_LL_BY_DEVALUE); + LOGD("Metadata::StringToLong longData = %" PRId64, longData); + return longData; +} + +int Metadata::GetAllMetadataKey(std::vector> &keys) +{ + if (naturalStoragePtr_ == nullptr) { + return -E_INVALID_DB; + } + return naturalStoragePtr_->GetAllMetaKeys(keys); +} + +namespace { +bool IsMetaDataKey(const Key &inKey, const std::string &expectPrefix) +{ + if (inKey.size() < expectPrefix.size()) { + return false; + } + std::string prefixInKey(inKey.begin(), inKey.begin() + expectPrefix.size()); + if (prefixInKey != expectPrefix) { + return false; + } + return true; +} +} + +int Metadata::LoadAllMetadata() +{ + std::vector> metaDataKeys; + int errCode = GetAllMetadataKey(metaDataKeys); + if (errCode != E_OK) { + LOGE("[Metadata] get all metadata key failed err=%d", errCode); + return errCode; + } + + std::vector> querySyncIds; + for (const auto &deviceId : metaDataKeys) { + if (IsMetaDataKey(deviceId, DEVICEID_PREFIX_KEY)) { + errCode = LoadDeviceIdDataToMap(deviceId); + if (errCode != E_OK) { + return errCode; + } + } else if (IsMetaDataKey(deviceId, QuerySyncWaterMarkHelper::GetQuerySyncPrefixKey())) { + querySyncIds.push_back(deviceId); + } else if (IsMetaDataKey(deviceId, QuerySyncWaterMarkHelper::GetDeleteSyncPrefixKey())) { + errCode = querySyncWaterMarkHelper_.LoadDeleteSyncDataToCache(deviceId); + if (errCode != E_OK) { + return errCode; + } + } + } + return querySyncWaterMarkHelper_.RemoveLeastUsedQuerySyncItems(querySyncIds); +} + +int Metadata::LoadDeviceIdDataToMap(const Key &key) +{ + std::vector value; + int errCode = GetMetadataFromDb(key, value); + if (errCode != E_OK) { + return errCode; + } + MetaDataValue metaValue; + std::string metaKey(key.begin(), key.end()); + errCode = DeSerializeMetaData(value, metaValue); + if (errCode != E_OK) { + return errCode; + } + std::lock_guard lockGuard(metadataLock_); + PutMetadataToMap(metaKey, metaValue); + return errCode; +} + +uint64_t Metadata::GetRandTimeOffset() const +{ + const int randOffsetLength = 2; // 2 byte + uint8_t randBytes[randOffsetLength] = { 0 }; + RAND_bytes(randBytes, randOffsetLength); + + // use a 16 bit rand data to make a rand timeoffset + uint64_t randTimeOffset = (static_cast(randBytes[1]) << 8) | randBytes[0]; // 16 bit data, 8 is offset + randTimeOffset = randTimeOffset * 1000 * 1000 * 10; // second, 1000 is scale + LOGD("[Metadata] GetRandTimeOffset %" PRIu64, randTimeOffset); + return randTimeOffset; +} + +void Metadata::GetHashDeviceId(const DeviceID &deviceId, DeviceID &hashDeviceId, bool isNeedHash) +{ + if (!isNeedHash) { + hashDeviceId = deviceId; + return; + } + if (deviceIdToHashDeviceIdMap_.count(deviceId) == 0) { + hashDeviceId = DEVICEID_PREFIX_KEY + DBCommon::TransferHashString(deviceId); + deviceIdToHashDeviceIdMap_.insert(std::pair(deviceId, hashDeviceId)); + } else { + hashDeviceId = deviceIdToHashDeviceIdMap_[deviceId]; + } +} + +int Metadata::GetRecvQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, WaterMark &waterMark) +{ + QueryWaterMark queryWaterMark; + int errCode = querySyncWaterMarkHelper_.GetQueryWaterMark(queryIdentify, deviceId, queryWaterMark); + if (errCode != E_OK) { + return errCode; + } + WaterMark peerWaterMark; + GetPeerWaterMark(deviceId, peerWaterMark); + waterMark = std::max(queryWaterMark.recvWaterMark, peerWaterMark); + return E_OK; +} + +int Metadata::SetRecvQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, const WaterMark &waterMark) +{ + return querySyncWaterMarkHelper_.SetRecvQueryWaterMark(queryIdentify, deviceId, waterMark); +} + +int Metadata::GetSendQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, WaterMark &waterMark, bool isAutoLift) +{ + QueryWaterMark queryWaterMark; + int errCode = querySyncWaterMarkHelper_.GetQueryWaterMark(queryIdentify, deviceId, queryWaterMark); + if (errCode != E_OK) { + return errCode; + } + if (isAutoLift) { + WaterMark localWaterMark; + GetLocalWaterMark(deviceId, localWaterMark); + waterMark = std::max(queryWaterMark.sendWaterMark, localWaterMark); + } else { + waterMark = queryWaterMark.sendWaterMark; + } + return E_OK; +} + +int Metadata::SetSendQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, const WaterMark &waterMark) +{ + return querySyncWaterMarkHelper_.SetSendQueryWaterMark(queryIdentify, deviceId, waterMark); +} + +int Metadata::GetLastQueryTime(const std::string &queryIdentify, const std::string &deviceId, Timestamp ×tamp) +{ + QueryWaterMark queryWaterMark; + int errCode = querySyncWaterMarkHelper_.GetQueryWaterMark(queryIdentify, deviceId, queryWaterMark); + if (errCode != E_OK) { + return errCode; + } + timestamp = queryWaterMark.lastQueryTime; + return E_OK; +} + +int Metadata::SetLastQueryTime(const std::string &queryIdentify, const std::string &deviceId, + const Timestamp ×tamp) +{ + return querySyncWaterMarkHelper_.SetLastQueryTime(queryIdentify, deviceId, timestamp); +} + +int Metadata::GetSendDeleteSyncWaterMark(const DeviceID &deviceId, WaterMark &waterMark, bool isAutoLift) +{ + DeleteWaterMark deleteWaterMark; + int errCode = querySyncWaterMarkHelper_.GetDeleteSyncWaterMark(deviceId, deleteWaterMark); + if (errCode != E_OK) { + return errCode; + } + if (isAutoLift) { + WaterMark localWaterMark; + GetLocalWaterMark(deviceId, localWaterMark); + waterMark = std::max(deleteWaterMark.sendWaterMark, localWaterMark); + } else { + waterMark = deleteWaterMark.sendWaterMark; + } + return E_OK; +} + +int Metadata::SetSendDeleteSyncWaterMark(const DeviceID &deviceId, const WaterMark &waterMark) +{ + return querySyncWaterMarkHelper_.SetSendDeleteSyncWaterMark(deviceId, waterMark); +} + +int Metadata::GetRecvDeleteSyncWaterMark(const DeviceID &deviceId, WaterMark &waterMark) +{ + DeleteWaterMark deleteWaterMark; + int errCode = querySyncWaterMarkHelper_.GetDeleteSyncWaterMark(deviceId, deleteWaterMark); + if (errCode != E_OK) { + return errCode; + } + WaterMark peerWaterMark; + GetPeerWaterMark(deviceId, peerWaterMark); + waterMark = std::max(deleteWaterMark.recvWaterMark, peerWaterMark); + return E_OK; +} + +int Metadata::SetRecvDeleteSyncWaterMark(const DeviceID &deviceId, const WaterMark &waterMark) +{ + return querySyncWaterMarkHelper_.SetRecvDeleteSyncWaterMark(deviceId, waterMark); +} + +int Metadata::ResetRecvQueryWaterMark(const DeviceID &deviceId, const std::string &tableName) +{ + return querySyncWaterMarkHelper_.ResetRecvQueryWaterMark(deviceId, tableName); +} + +void Metadata::GetDbCreateTime(const DeviceID &deviceId, uint64_t &outValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + DeviceID hashDeviceId; + GetHashDeviceId(deviceId, hashDeviceId, true); + if (metadataMap_.find(hashDeviceId) != metadataMap_.end()) { + metadata = metadataMap_[hashDeviceId]; + outValue = metadata.dbCreateTime; + return; + } + outValue = 0; + LOGI("Metadata::GetDbCreateTime, not found dev = %s dbCreateTime", STR_MASK(deviceId)); +} + +int Metadata::SetDbCreateTime(const DeviceID &deviceId, uint64_t inValue, bool isNeedHash) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + DeviceID hashDeviceId; + GetHashDeviceId(deviceId, hashDeviceId, isNeedHash); + if (metadataMap_.find(hashDeviceId) != metadataMap_.end()) { + metadata = metadataMap_[hashDeviceId]; + if (metadata.dbCreateTime != 0 && metadata.dbCreateTime != inValue) { + metadata.clearDeviceDataMark = REMOVE_DEVICE_DATA_MARK; + LOGI("Metadata::SetDbCreateTime,set cleardata mark,dev=%s,dbCreateTime=%" PRIu64, + STR_MASK(deviceId), inValue); + } + if (metadata.dbCreateTime == 0) { + LOGI("Metadata::SetDbCreateTime,update dev=%s,dbCreateTime=%" PRIu64, STR_MASK(deviceId), inValue); + } + } + metadata.dbCreateTime = inValue; + return SaveMetaDataValue(deviceId, metadata); +} + +int Metadata::ResetMetaDataAfterRemoveData(const DeviceID &deviceId) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + DeviceID hashDeviceId; + GetHashDeviceId(deviceId, hashDeviceId, true); + if (metadataMap_.find(hashDeviceId) != metadataMap_.end()) { + metadata = metadataMap_[hashDeviceId]; + metadata.clearDeviceDataMark = 0; + return SaveMetaDataValue(deviceId, metadata); + } + return -E_NOT_FOUND; +} + +void Metadata::GetRemoveDataMark(const DeviceID &deviceId, uint64_t &outValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + DeviceID hashDeviceId; + GetHashDeviceId(deviceId, hashDeviceId, true); + if (metadataMap_.find(hashDeviceId) != metadataMap_.end()) { + metadata = metadataMap_[hashDeviceId]; + outValue = metadata.clearDeviceDataMark; + return; + } + outValue = 0; +} + +int Metadata::DeleteMetaDataByPrefixKey(const Key &keyPrefix) const +{ + if (naturalStoragePtr_ == nullptr) { + return -E_INVALID_DB; + } + return naturalStoragePtr_->DeleteMetaDataByPrefixKey(keyPrefix); +} + +uint64_t Metadata::GetQueryLastTimestamp(const DeviceID &deviceId, const std::string &queryId) const +{ + std::vector key; + std::vector value; + std::string hashqueryId = DBConstant::SUBSCRIBE_QUERY_PREFIX + DBCommon::TransferHashString(queryId); + DBCommon::StringToVector(hashqueryId, key); + int errCode = GetMetadataFromDb(key, value); + std::lock_guard lockGuard(metadataLock_); + if (errCode == -E_NOT_FOUND) { + auto iter = queryIdMap_.find(deviceId); + if (iter != queryIdMap_.end()) { + if (iter->second.find(hashqueryId) == iter->second.end()) { + iter->second.insert(hashqueryId); + return INT64_MAX; + } + return 0; + } else { + queryIdMap_[deviceId] = { hashqueryId }; + return INT64_MAX; + } + } + auto iter = queryIdMap_.find(deviceId); + // while value is found in db, it can be found in db later when db is not closed + // so no need to record the hashqueryId in map + if (errCode == E_OK && iter != queryIdMap_.end()) { + iter->second.erase(hashqueryId); + } + return StringToLong(value); +} + +void Metadata::RemoveQueryFromRecordSet(const DeviceID &deviceId, const std::string &queryId) +{ + std::lock_guard lockGuard(metadataLock_); + std::string hashqueryId = DBConstant::SUBSCRIBE_QUERY_PREFIX + DBCommon::TransferHashString(queryId); + auto iter = queryIdMap_.find(deviceId); + if (iter != queryIdMap_.end() && iter->second.find(hashqueryId) != iter->second.end()) { + iter->second.erase(hashqueryId); + } +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/meta_data.h b/mock/distributeddb/syncer/src/meta_data.h new file mode 100644 index 0000000000000000000000000000000000000000..7b2a0051a8ddd2f5990ae96b31aeefc320078019 --- /dev/null +++ b/mock/distributeddb/syncer/src/meta_data.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef META_DATA_H +#define META_DATA_H + +#include +#include +#include +#include + +#include "db_types.h" +#include "ikvdb_sync_interface.h" +#include "query_sync_water_mark_helper.h" + +namespace DistributedDB { +struct MetaDataValue { + TimeOffset timeOffset = 0; + uint64_t lastUpdateTime = 0; + uint64_t localWaterMark = 0; + uint64_t peerWaterMark = 0; + Timestamp dbCreateTime = 0; + uint64_t clearDeviceDataMark = 0; // Default 0 for not remove device data. +}; + +class Metadata { +public: + Metadata(); + virtual ~Metadata(); + + int Initialize(ISyncInterface *storage); + + int SaveTimeOffset(const DeviceID &deviceId, TimeOffset inValue); + + void GetTimeOffset(const DeviceID &deviceId, TimeOffset &outValue); + + void GetLocalWaterMark(const DeviceID &deviceId, uint64_t &outValue); + + int SaveLocalWaterMark(const DeviceID &deviceId, uint64_t inValue); + + void GetPeerWaterMark(const DeviceID &deviceId, uint64_t &outValue); + + int SavePeerWaterMark(const DeviceID &deviceId, uint64_t inValue, bool isNeedHash); + + int SaveLocalTimeOffset(TimeOffset timeOffset); + + TimeOffset GetLocalTimeOffset() const; + + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash); + + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, const std::string &tableName); + + void SetLastLocalTime(Timestamp lastLocalTime); + + Timestamp GetLastLocalTime() const; + + int SetSendQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, const WaterMark &waterMark); + + // the querySync's sendWatermark will increase by the device watermark + // if the sendWatermark less than device watermark + int GetSendQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, WaterMark &waterMark, bool isAutoLift = true); + + int SetRecvQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, const WaterMark &waterMark); + + // the querySync's recvWatermark will increase by the device watermark + // if the watermark less than device watermark + int GetRecvQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, WaterMark &waterMark); + + virtual int SetLastQueryTime(const std::string &queryIdentify, const std::string &deviceId, + const Timestamp ×tamp); + + virtual int GetLastQueryTime(const std::string &queryIdentify, const std::string &deviceId, Timestamp ×tamp); + + int SetSendDeleteSyncWaterMark(const std::string &deviceId, const WaterMark &waterMark); + + // the deleteSync's sendWatermark will increase by the device watermark + // if the sendWatermark less than device watermark + int GetSendDeleteSyncWaterMark(const std::string &deviceId, WaterMark &waterMark, bool isAutoLift = true); + + int SetRecvDeleteSyncWaterMark(const std::string &deviceId, const WaterMark &waterMark); + + // the deleteSync's recvWatermark will increase by the device watermark + // if the recvWatermark less than device watermark + int GetRecvDeleteSyncWaterMark(const std::string &deviceId, WaterMark &waterMark); + + void GetDbCreateTime(const DeviceID &deviceId, uint64_t &outValue); + + int SetDbCreateTime(const DeviceID &deviceId, uint64_t inValue, bool isNeedHash); + + int ResetMetaDataAfterRemoveData(const DeviceID &deviceId); + + void GetRemoveDataMark(const DeviceID &deviceId, uint64_t &outValue); + + // always get value from db, value updated from storage trigger + uint64_t GetQueryLastTimestamp(const DeviceID &deviceId, const std::string &queryId) const; + + void RemoveQueryFromRecordSet(const DeviceID &deviceId, const std::string &queryId); +private: + + int SaveMetaDataValue(const DeviceID &deviceId, const MetaDataValue &inValue); + + // sync module need hash devices id + void GetMetaDataValue(const DeviceID &deviceId, MetaDataValue &outValue, bool isNeedHash); + + int SerializeMetaData(const MetaDataValue &inValue, std::vector &outValue); + + int DeSerializeMetaData(const std::vector &inValue, MetaDataValue &outValue) const; + + int GetMetadataFromDb(const std::vector &key, std::vector &outValue) const; + + int SetMetadataToDb(const std::vector &key, const std::vector &inValue); + + int DeleteMetaDataFromDB(const std::vector &keys) const; + int DeleteMetaDataByPrefixKey(const Key &keyPrefix) const; + + void PutMetadataToMap(const DeviceID &deviceId, const MetaDataValue &value); + + void GetMetadataFromMap(const DeviceID &deviceId, MetaDataValue &outValue); + + int64_t StringToLong(const std::vector &value) const; + + int GetAllMetadataKey(std::vector> &keys); + + int LoadAllMetadata(); + + uint64_t GetRandTimeOffset() const; + + void GetHashDeviceId(const DeviceID &deviceId, DeviceID &hashDeviceId, bool isNeedHash); + + // this function will read data from db by metaData's key + // and then serialize it and put to map + int LoadDeviceIdDataToMap(const Key &key); + + // reset the waterMark to zero + int ResetRecvQueryWaterMark(const DeviceID &deviceId, const std::string &tableName = ""); + + // store localTimeOffset in ram; if change, should add a lock first, change here and metadata, + // then release lock + std::atomic localTimeOffset_; + std::mutex localTimeOffsetLock_; + ISyncInterface *naturalStoragePtr_; + + // if changed, it should be locked from save-to-db to change-in-memory.save to db must be first, + // if save to db fail, it will not be changed in memory. + std::map metadataMap_; + mutable std::mutex metadataLock_; + std::map deviceIdToHashDeviceIdMap_; + + // store localTimeOffset in ram, used to make timestamp increase + mutable std::mutex lastLocalTimeLock_; + Timestamp lastLocalTime_; + + QuerySyncWaterMarkHelper querySyncWaterMarkHelper_; + + // set value: SUBSCRIBE_QUERY_PREFIX + DBCommon::TransferHashString(queryId) + // queryId is not in set while key is not found from db first time, and return lastTimestamp = INT64_MAX + // if query is in set return 0 while not found from db, means already sync before, don't trigger again + mutable std::map> queryIdMap_; +}; +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/syncer/src/multi_ver_data_sync.cpp b/mock/distributeddb/syncer/src/multi_ver_data_sync.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9bc8c84f7d577b5dd5ba893b3ddcf160662bd547 --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_data_sync.cpp @@ -0,0 +1,691 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_data_sync.h" + +#include "parcel.h" +#include "log_print.h" +#include "sync_types.h" +#include "message_transform.h" +#include "performance_analysis.h" +#include "db_constant.h" + +namespace DistributedDB { +// Class MultiVerRequestPacket +uint32_t MultiVerRequestPacket::CalculateLen() const +{ + uint64_t len = Parcel::GetIntLen(); + len = Parcel::GetEightByteAlign(len); + len += Parcel::GetMultiVerCommitLen(commit_); + if (len > INT32_MAX) { + return 0; + } + return len; +} + +void MultiVerRequestPacket::SetCommit(MultiVerCommitNode &commit) +{ + commit_ = std::move(commit); +} + +void MultiVerRequestPacket::GetCommit(MultiVerCommitNode &commit) const +{ + commit = commit_; +} + +void MultiVerRequestPacket::SetErrCode(int32_t errCode) +{ + errCode_ = errCode; +} + +int32_t MultiVerRequestPacket::GetErrCode() const +{ + return errCode_; +} + +// Class MultiVerAckPacket +uint32_t MultiVerAckPacket::CalculateLen() const +{ + uint64_t len = Parcel::GetIntLen(); + len = Parcel::GetEightByteAlign(len); + for (const auto &iter : entries_) { + len += Parcel::GetVectorCharLen(iter); + if (len > INT32_MAX) { + return 0; + } + } + return len; +} + +void MultiVerAckPacket::SetData(std::vector> &data) +{ + entries_ = std::move(data); +} + +void MultiVerAckPacket::GetData(std::vector> &data) const +{ + data = entries_; +} + +void MultiVerAckPacket::SetErrorCode(int32_t errCode) +{ + errorCode_ = errCode; +} + +void MultiVerAckPacket::GetErrorCode(int32_t &errCode) const +{ + errCode = errorCode_; +} + +// Class MultiVerDataSync +MultiVerDataSync::~MultiVerDataSync() +{ + storagePtr_ = nullptr; + communicateHandle_ = nullptr; +} + +int MultiVerDataSync::Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return -E_INVALID_ARGS; + } + + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return RequestPacketSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckPacketSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +int MultiVerDataSync::DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return -E_MESSAGE_ID_ERROR; + } + + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return RequestPacketDeSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckPacketDeSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +uint32_t MultiVerDataSync::CalculateLen(const Message *inMsg) +{ + if (!(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return 0; + } + + uint32_t len = 0; + int errCode = E_OK; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + errCode = RequestPacketCalculateLen(inMsg, len); + break; + case TYPE_RESPONSE: + errCode = AckPacketCalculateLen(inMsg, len); + break; + default: + return 0; + } + if (errCode != E_OK) { + return 0; + } + return len; +} + +int MultiVerDataSync::RegisterTransformFunc() +{ + TransformFunc func; + func.computeFunc = std::bind(&MultiVerDataSync::CalculateLen, std::placeholders::_1); + func.serializeFunc = std::bind(&MultiVerDataSync::Serialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + func.deserializeFunc = std::bind(&MultiVerDataSync::DeSerialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + return MessageTransform::RegTransformFunction(MULTI_VER_DATA_SYNC_MESSAGE, func); +} + +int MultiVerDataSync::Initialize(MultiVerKvDBSyncInterface *storagePtr, ICommunicator *communicateHandle) +{ + if ((storagePtr == nullptr) || (communicateHandle == nullptr)) { + return -E_INVALID_ARGS; + } + storagePtr_ = storagePtr; + communicateHandle_ = communicateHandle; + return E_OK; +} + +void MultiVerDataSync::TimeOutCallback(MultiVerSyncTaskContext *context, const Message *message) const +{ + return; +} + +int MultiVerDataSync::SyncStart(MultiVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + LOGD("MultiVerDataSync::SyncStart dst=%s{private}, begin", context->GetDeviceId().c_str()); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_DATA_GET_VALID_COMMIT); + } + MultiVerCommitNode commit; + int errCode = GetValidCommit(context, commit); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_DATA_GET_VALID_COMMIT); + } + if (errCode != E_OK) { + // sync don't need start + SendFinishedRequest(context); + return errCode; + } + + errCode = SendRequestPacket(context, commit); + LOGD("MultiVerDataSync::SyncStart dst=%s{private}, end", context->GetDeviceId().c_str()); + return errCode; +} + +int MultiVerDataSync::RequestRecvCallback(const MultiVerSyncTaskContext *context, const Message *message) +{ + if (message == nullptr || context == nullptr) { + return -E_INVALID_ARGS; + } + + if (!IsPacketValid(message, TYPE_REQUEST)) { + return -E_INVALID_ARGS; + } + + const MultiVerRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + if (packet->GetErrCode() == -E_LAST_SYNC_FRAME) { + return -E_LAST_SYNC_FRAME; + } + MultiVerCommitNode commit; + packet->GetCommit(commit); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_GET_COMMIT_DATA); + } + std::vector dataEntries; + int errCode = GetCommitData(commit, dataEntries); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_GET_COMMIT_DATA); + } + if (errCode != E_OK) { + LOGE("MultiVerDataSync::RequestRecvCallback : GetCommitData ERR, errno = %d", errCode); + } + + errCode = SendAckPacket(context, dataEntries, errCode, message); + for (auto &iter : dataEntries) { + ReleaseKvEntry(iter); + iter = nullptr; + } + LOGD("MultiVerDataSync::RequestRecvCallback : SendAckPacket, errno = %d, dst = %s{private}", + errCode, context->GetDeviceId().c_str()); + return errCode; +} + +int MultiVerDataSync::AckRecvCallback(MultiVerSyncTaskContext *context, const Message *message) +{ + if (message == nullptr) { + return -E_INVALID_ARGS; + } + if (!IsPacketValid(message, TYPE_RESPONSE) || (context == nullptr)) { + return -E_INVALID_ARGS; + } + + const MultiVerAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + int32_t errCode = E_OK; + packet->GetErrorCode(errCode); + if (errCode != E_OK) { + return errCode; + } + std::vector> dataEntries; + std::vector entries; + std::vector valueHashes; + MultiVerKvEntry *entry = nullptr; + + packet->GetData(dataEntries); + for (auto &iter : dataEntries) { + MultiVerKvEntry *item = CreateKvEntry(iter); + entries.push_back(item); + } + context->ReleaseEntries(); + context->SetEntries(entries); + context->SetEntriesIndex(0); + context->SetEntriesSize(static_cast(entries.size())); + LOGD("MultiVerDataSync::AckRecvCallback src=%s{private}, entries num = %zu", + context->GetDeviceId().c_str(), entries.size()); + + if (entries.size() > 0) { + entry = entries[0]; + errCode = entry->GetValueHash(valueHashes); + if (errCode != E_OK) { + return errCode; + } + } + context->SetValueSliceHashNodes(valueHashes); + context->SetValueSlicesIndex(0); + context->SetValueSlicesSize(valueHashes.size()); + LOGD("MultiVerDataSync::AckRecvCallback src=%s{private}, ValueSlicesSize num = %zu", + context->GetDeviceId().c_str(), valueHashes.size()); + return errCode; +} + +int MultiVerDataSync::PutCommitData(const MultiVerCommitNode &commit, const std::vector &entries, + const std::string &deviceName) +{ + return storagePtr_->PutCommitData(commit, entries, deviceName); +} + +int MultiVerDataSync::MergeSyncCommit(const MultiVerCommitNode &commit, const std::vector &commits) +{ + return storagePtr_->MergeSyncCommit(commit, commits); +} + +void MultiVerDataSync::ReleaseKvEntry(const MultiVerKvEntry *entry) +{ + return storagePtr_->ReleaseKvEntry(entry); +} + +void MultiVerDataSync::SendFinishedRequest(const MultiVerSyncTaskContext *context) +{ + if (context == nullptr) { + return; + } + MultiVerRequestPacket *packet = new (std::nothrow) MultiVerRequestPacket(); + if (packet == nullptr) { + LOGE("MultiVerRequestPacket::SendRequestPacket : new packet error"); + return; + } + packet->SetErrCode(-E_LAST_SYNC_FRAME); + Message *message = new (std::nothrow) Message(MULTI_VER_DATA_SYNC_MESSAGE); + if (message == nullptr) { + delete packet; + packet = nullptr; + LOGE("MultiVerDataSync::SendRequestPacket : new message error"); + return; + } + message->SetMessageType(TYPE_REQUEST); + message->SetTarget(context->GetDeviceId()); + int errCode = message->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete message; + message = nullptr; + LOGE("[MultiVerDataSync][SendFinishedRequest] : SetExternalObject failed errCode:%d", errCode); + return; + } + message->SetSessionId(context->GetRequestSessionId()); + message->SetSequenceId(context->GetSequenceId()); + + errCode = Send(message->GetTarget(), message); + if (errCode != E_OK) { + delete message; + message = nullptr; + LOGE("[MultiVerDataSync][SendFinishedRequest] SendFinishedRequest failed, err %d", errCode); + } + LOGI("[MultiVerDataSync][SendFinishedRequest] SendFinishedRequest dst=%s{private}", context->GetDeviceId().c_str()); +} + +int MultiVerDataSync::RequestPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + if ((inMsg == nullptr) || !IsPacketValid(inMsg, TYPE_REQUEST)) { + return -E_INVALID_ARGS; + } + const MultiVerRequestPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + len = packet->CalculateLen(); + return E_OK; +} + +int MultiVerDataSync::RequestPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || !IsPacketValid(inMsg, TYPE_REQUEST)) { + return -E_INVALID_ARGS; + } + const MultiVerRequestPacket *packet = inMsg->GetObject(); + if ((packet == nullptr) || (length != packet->CalculateLen())) { + return -E_INVALID_ARGS; + } + + MultiVerCommitNode commit; + packet->GetCommit(commit); + int32_t ackCode = packet->GetErrCode(); + + Parcel parcel(buffer, length); + int errCode = parcel.WriteInt(ackCode); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + parcel.EightByteAlign(); + + // commitMap Serialization + errCode = parcel.WriteMultiVerCommit(commit); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + + return errCode; +} + +int MultiVerDataSync::RequestPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || !IsPacketValid(inMsg, TYPE_REQUEST)) { + return -E_INVALID_ARGS; + } + + MultiVerCommitNode commit; + Parcel parcel(const_cast(buffer), length); + int32_t pktErrCode; + uint64_t packLen = parcel.ReadInt(pktErrCode); + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + parcel.EightByteAlign(); + packLen = Parcel::GetEightByteAlign(packLen); + // commit DeSerialization + packLen += parcel.ReadMultiVerCommit(commit); + if (packLen != length || parcel.IsError()) { + return -E_INVALID_ARGS; + } + MultiVerRequestPacket *packet = new (std::nothrow) MultiVerRequestPacket(); + if (packet == nullptr) { + LOGE("MultiVerDataSync::RequestPacketDeSerialization : new packet error"); + return -E_OUT_OF_MEMORY; + } + packet->SetCommit(commit); + packet->SetErrCode(pktErrCode); + int errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + } + return errCode; +} + +int MultiVerDataSync::AckPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + if (!IsPacketValid(inMsg, TYPE_RESPONSE)) { + return -E_INVALID_ARGS; + } + + const MultiVerAckPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + len = packet->CalculateLen(); + return E_OK; +} + +int MultiVerDataSync::AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || !IsPacketValid(inMsg, TYPE_RESPONSE)) { + return -E_INVALID_ARGS; + } + const MultiVerAckPacket *packet = inMsg->GetObject(); + if ((packet == nullptr) || (length != packet->CalculateLen())) { + return -E_INVALID_ARGS; + } + + Parcel parcel(buffer, length); + std::vector> entries; + + packet->GetData(entries); + int32_t errCode = E_OK; + packet->GetErrorCode(errCode); + // errCode Serialization + errCode = parcel.WriteInt(errCode); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + parcel.EightByteAlign(); + + // commits vector Serialization + for (const auto &iter : entries) { + errCode = parcel.WriteVectorChar(iter); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + } + + return errCode; +} + +int MultiVerDataSync::AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || !IsPacketValid(inMsg, TYPE_RESPONSE)) { + return -E_INVALID_ARGS; + } + + Parcel parcel(const_cast(buffer), length); + int32_t pktErrCode; + + // errCode DeSerialization + uint32_t packLen = parcel.ReadInt(pktErrCode); + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + parcel.EightByteAlign(); + packLen = Parcel::GetEightByteAlign(packLen); + + // commits vector DeSerialization + std::vector> entries; + while (packLen < length) { + std::vector data; + packLen += parcel.ReadVectorChar(data); + // A valid dataItem got, Save to storage + entries.push_back(data); + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + } + MultiVerAckPacket *packet = new (std::nothrow) MultiVerAckPacket(); + if (packet == nullptr) { + LOGE("MultiVerDataSync::AckPacketDeSerialization : new packet error"); + return -E_OUT_OF_MEMORY; + } + packet->SetData(entries); + packet->SetErrorCode(pktErrCode); + int errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + } + return errCode; +} + +bool MultiVerDataSync::IsPacketValid(const Message *inMsg, uint16_t messageType) +{ + if ((inMsg == nullptr) || (inMsg->GetMessageId() != MULTI_VER_DATA_SYNC_MESSAGE)) { + return false; + } + if (messageType != inMsg->GetMessageType()) { + return false; + } + return true; +} + +int MultiVerDataSync::GetValidCommit(MultiVerSyncTaskContext *context, MultiVerCommitNode &commit) +{ + int commitsSize = context->GetCommitsSize(); + if (commitsSize > DBConstant::MAX_COMMIT_SIZE) { + LOGE("MultiVerDataSync::GetValidCommit failed, to large!"); + return -E_LENGTH_ERROR; + } + int index = context->GetCommitIndex(); + if (context->GetRetryStatus() == SyncTaskContext::NEED_RETRY) { + context->SetRetryStatus(SyncTaskContext::NO_NEED_RETRY); + index--; + } + index = (index < 0) ? 0 : index; + LOGD("MultiVerDataSync::GetValidCommit begin, dst=%s{private}, index = %d", context->GetDeviceId().c_str(), index); + while (index < commitsSize) { + MultiVerCommitNode commitItem; + context->GetCommit(index, commitItem); + LOGD("MultiVerDataSync::GetValidCommit , dst=%s{private}, index = %d, commitsSize = %d", + context->GetDeviceId().c_str(), index, commitsSize); + + index++; + context->SetCommitIndex(index); + if (IsCommitExisted(commitItem)) { + continue; + } + commit = commitItem; + LOGD("MultiVerDataSync::GetValidCommit ok, dst=%s{private}, commit index = %d", + context->GetDeviceId().c_str(), index); + return E_OK; + } + LOGD("MultiVerDataSync::GetValidCommit not found, dst=%s{private}", context->GetDeviceId().c_str()); + return -E_NOT_FOUND; +} + +bool MultiVerDataSync::IsCommitExisted(const MultiVerCommitNode &commit) +{ + return storagePtr_->IsCommitExisted(commit); +} + +int MultiVerDataSync::Send(const DeviceID &deviceId, const Message *inMsg) +{ + SendConfig conf = {false, false, SEND_TIME_OUT, {}}; + int errCode = communicateHandle_->SendMessage(deviceId, inMsg, conf); + if (errCode != E_OK) { + LOGE("MultiVerDataSync::Send ERR! ERR = %d", errCode); + } + return errCode; +} + +int MultiVerDataSync::SendRequestPacket(const MultiVerSyncTaskContext *context, MultiVerCommitNode &commit) +{ + MultiVerRequestPacket *packet = new (std::nothrow) MultiVerRequestPacket(); + if (packet == nullptr) { + LOGE("MultiVerRequestPacket::SendRequestPacket : new packet error"); + return -E_OUT_OF_MEMORY; + } + packet->SetCommit(commit); + Message *message = new (std::nothrow) Message(MULTI_VER_DATA_SYNC_MESSAGE); + if (message == nullptr) { + delete packet; + packet = nullptr; + LOGE("MultiVerDataSync::SendRequestPacket : new message error"); + return -E_OUT_OF_MEMORY; + } + message->SetMessageType(TYPE_REQUEST); + message->SetTarget(context->GetDeviceId()); + int errCode = message->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete message; + message = nullptr; + LOGE("[MultiVerDataSync][SendRequestPacket] : SetExternalObject failed errCode:%d", errCode); + return errCode; + } + message->SetSessionId(context->GetRequestSessionId()); + message->SetSequenceId(context->GetSequenceId()); + + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_DATA_ENTRY_SEND_REQUEST_TO_ACK_RECV); + } + errCode = Send(message->GetTarget(), message); + if (errCode != E_OK) { + delete message; + message = nullptr; + } + LOGD("MultiVerDataSync::SendRequestPacket end"); + return errCode; +} + +int MultiVerDataSync::SendAckPacket(const MultiVerSyncTaskContext *context, + const std::vector &dataItems, int retCode, const Message *message) +{ + if (message == nullptr) { + LOGE("MultiVerDataSync::SendAckPacket : message is nullptr"); + return -E_INVALID_ARGS; + } + + MultiVerAckPacket *packet = new (std::nothrow) MultiVerAckPacket(); + if (packet == nullptr) { + LOGE("MultiVerDataSync::SendAckPack et : packet is nullptr"); + return -E_OUT_OF_MEMORY; + } + Message *ackMessage = new (std::nothrow) Message(MULTI_VER_DATA_SYNC_MESSAGE); + if (ackMessage == nullptr) { + delete packet; + packet = nullptr; + LOGE("MultiVerDataSync::SendAckPacket : new message error"); + return -E_OUT_OF_MEMORY; + } + + std::vector> entries; + for (const auto &iter : dataItems) { + std::vector item; + iter->GetSerialData(item); + entries.push_back(item); + } + packet->SetData(entries); + packet->SetErrorCode(static_cast(retCode)); + + ackMessage->SetMessageType(TYPE_RESPONSE); + ackMessage->SetTarget(context->GetDeviceId()); + int errCode = ackMessage->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete ackMessage; + ackMessage = nullptr; + LOGE("[MultiVerDataSync][SendAckPacket] : SetExternalObject failed errCode:%d", errCode); + return errCode; + } + ackMessage->SetSequenceId(message->GetSequenceId()); + ackMessage->SetSessionId(message->GetSessionId()); + errCode = Send(ackMessage->GetTarget(), ackMessage); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + } + LOGD("MultiVerDataSync::SendAckPacket end, dst=%s{private}, errCode = %d", context->GetDeviceId().c_str(), errCode); + return errCode; +} + +int MultiVerDataSync::GetCommitData(const MultiVerCommitNode &commit, std::vector &entries) +{ + return storagePtr_->GetCommitData(commit, entries); +} + +MultiVerKvEntry *MultiVerDataSync::CreateKvEntry(const std::vector &entry) +{ + return storagePtr_->CreateKvEntry(entry); +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/multi_ver_data_sync.h b/mock/distributeddb/syncer/src/multi_ver_data_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..889be32c854c823f25b99fdb4dfb74d1db4d48f4 --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_data_sync.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_DATA_SYNC_H +#define MULTI_VER_DATA_SYNC_H + +#ifndef OMIT_MULTI_VER +#include + +#include "icommunicator.h" +#include "multi_ver_kvdb_sync_interface.h" +#include "multi_ver_sync_task_context.h" +#include "sync_task_context.h" + +namespace DistributedDB { +class MultiVerRequestPacket { +public: + MultiVerRequestPacket() : errCode_(E_OK) {}; + ~MultiVerRequestPacket() {}; + + uint32_t CalculateLen() const; + + void SetCommit(MultiVerCommitNode &commit); + + void GetCommit(MultiVerCommitNode &commit) const; + + void SetErrCode(int32_t errCode); + + int32_t GetErrCode() const; +private: + MultiVerCommitNode commit_; + int32_t errCode_ = E_OK; +}; + +class MultiVerAckPacket { +public: + MultiVerAckPacket() : errorCode_(0) {}; + ~MultiVerAckPacket() {}; + + uint32_t CalculateLen() const; + + void SetData(std::vector> &data); + + void GetData(std::vector> &data) const; + + void SetErrorCode(int32_t errCode); + + void GetErrorCode(int32_t &errCode) const; +private: + std::vector> entries_; + int32_t errorCode_; +}; + +class MultiVerDataSync { +public: + MultiVerDataSync() : storagePtr_(nullptr), communicateHandle_(nullptr) {}; + ~MultiVerDataSync(); + DISABLE_COPY_ASSIGN_MOVE(MultiVerDataSync); + + static int RegisterTransformFunc(); + + int Initialize(MultiVerKvDBSyncInterface *storagePtr, ICommunicator *communicateHandle); + + static int Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static uint32_t CalculateLen(const Message *inMsg); + + void TimeOutCallback(MultiVerSyncTaskContext *context, const Message *message) const; + + int SyncStart(MultiVerSyncTaskContext *context); + + int RequestRecvCallback(const MultiVerSyncTaskContext *context, const Message *message); + + int AckRecvCallback(MultiVerSyncTaskContext *context, const Message *message); + + int PutCommitData(const MultiVerCommitNode &commit, const std::vector &entries, + const std::string &deviceName); + + int MergeSyncCommit(const MultiVerCommitNode &commit, const std::vector &commits); + + void ReleaseKvEntry(const MultiVerKvEntry *entry); + + void SendFinishedRequest(const MultiVerSyncTaskContext *context); + +private: + static int RequestPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int RequestPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int RequestPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static int AckPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static bool IsPacketValid(const Message *inMsg, uint16_t messageType); + + int GetValidCommit(MultiVerSyncTaskContext *context, MultiVerCommitNode &commit); + + bool IsCommitExisted(const MultiVerCommitNode &); + + int Send(const DeviceID &deviceId, const Message *inMsg); + + int SendRequestPacket(const MultiVerSyncTaskContext *context, MultiVerCommitNode &commit); + + int SendAckPacket(const MultiVerSyncTaskContext *context, const std::vector &dataItems, + int retCode, const Message *message); + + int GetCommitData(const MultiVerCommitNode &commit, std::vector &entries); + + MultiVerKvEntry *CreateKvEntry(const std::vector &entry); + + MultiVerKvDBSyncInterface *storagePtr_; + ICommunicator *communicateHandle_; +}; +} + +#endif +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/multi_ver_sync_engine.cpp b/mock/distributeddb/syncer/src/multi_ver_sync_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..54bbe224edf7bf9a93f7676bc008b55e52ccc72d --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_sync_engine.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_sync_engine.h" +#include "multi_ver_sync_task_context.h" + +namespace DistributedDB { +ISyncTaskContext *MultiVerSyncEngine::CreateSyncTaskContext() +{ + return new (std::nothrow) MultiVerSyncTaskContext; +} + +DEFINE_OBJECT_TAG_FACILITIES(MultiVerSyncEngine); +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/multi_ver_sync_engine.h b/mock/distributeddb/syncer/src/multi_ver_sync_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..0c23ae95c44ae1eb316c50a8f4e1c2fadbfcefa7 --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_sync_engine.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_SYNC_ENGINE_H +#define MULTI_VER_SYNC_ENGINE_H + +#ifndef OMIT_MULTI_VER +#include "sync_engine.h" + +namespace DistributedDB { +class MultiVerSyncEngine final : public SyncEngine { +public: + MultiVerSyncEngine() {}; + + DISABLE_COPY_ASSIGN_MOVE(MultiVerSyncEngine); + +protected: + ~MultiVerSyncEngine() override {}; + + // Create a context + ISyncTaskContext *CreateSyncTaskContext() override; + +private: + DECLARE_OBJECT_TAG(MultiVerSyncEngine); +}; +} // namespace DistributedDB + +#endif // MULTI_VER_SYNC_ENGINE_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/multi_ver_sync_state_machine.cpp b/mock/distributeddb/syncer/src/multi_ver_sync_state_machine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c63868a7602e9a321f49f842c233887a3f8217af --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_sync_state_machine.cpp @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_sync_state_machine.h" + +#include +#include +#include + +#include "message_transform.h" +#include "log_print.h" +#include "sync_types.h" +#include "db_common.h" +#include "ref_object.h" +#include "performance_analysis.h" + +namespace DistributedDB { +namespace { +void ChangeEntriesTimestamp(std::vector &entries, TimeOffset outOffset, TimeOffset timefixOffset) +{ + for (MultiVerKvEntry *entry : entries) { + if (entry == nullptr) { + continue; + } + Timestamp timestamp; + entry->GetTimestamp(timestamp); + timestamp = timestamp - static_cast(outOffset + timefixOffset); + entry->SetTimestamp(timestamp); + } +} +} +std::vector MultiVerSyncStateMachine::stateSwitchTables_; +MultiVerSyncStateMachine::MultiVerSyncStateMachine() + : context_(nullptr), + multiVerStorage_(nullptr), + timeSync_(nullptr), + commitHistorySync_(nullptr), + multiVerDataSync_(nullptr), + valueSliceSync_(nullptr) +{ +} + +MultiVerSyncStateMachine::~MultiVerSyncStateMachine() +{ + Clear(); +} + +int MultiVerSyncStateMachine::Initialize(ISyncTaskContext *context, ISyncInterface *syncInterface, + std::shared_ptr &metadata, ICommunicator *communicator) +{ + if (context == nullptr || syncInterface == nullptr || metadata == nullptr || communicator == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = SyncStateMachine::Initialize(context, syncInterface, metadata, communicator); + if (errCode != E_OK) { + return errCode; + } + + timeSync_ = std::make_unique(); + commitHistorySync_ = std::make_unique(); + multiVerDataSync_ = std::make_unique(); + valueSliceSync_ = std::make_unique(); + + errCode = timeSync_->Initialize(communicator, metadata, syncInterface, context->GetDeviceId()); + if (errCode != E_OK) { + LOGE("timeSync_->Initialize failed err %d", errCode); + goto ERROR_OUT; + } + LOGD("timeSync_->Initialize OK"); + + // init functions below will never fail + multiVerStorage_ = static_cast(syncInterface); + commitHistorySync_->Initialize(multiVerStorage_, communicator); + multiVerDataSync_->Initialize(multiVerStorage_, communicator); + valueSliceSync_->Initialize(multiVerStorage_, communicator); + + context_ = static_cast(context); + currentState_ = IDLE; + (void)timeSync_->SyncStart(); + return E_OK; + +ERROR_OUT: + Clear(); + return errCode; +} + +void MultiVerSyncStateMachine::SyncStep() +{ + RefObject::IncObjRef(context_); + RefObject::IncObjRef(communicator_); + int errCode = RuntimeContext::GetInstance()->ScheduleTask( + std::bind(&MultiVerSyncStateMachine::SyncStepInnerLocked, this)); + if (errCode != E_OK) { + LOGE("[MultiVerSyncStateMachine] Schedule SyncStep failed"); + RefObject::DecObjRef(communicator_); + RefObject::DecObjRef(context_); + } +} + +void MultiVerSyncStateMachine::StepToIdle() +{ + currentState_ = IDLE; + StopWatchDog(); + context_->Clear(); + PerformanceAnalysis::GetInstance()->TimeRecordEnd(); + LOGD("[MultiVerSyncStateMachine][%s] step to idle", STR_MASK(context_->GetDeviceId())); +} + +int MultiVerSyncStateMachine::MessageCallbackCheck(const Message *inMsg) +{ + RefObject::AutoLock lock(context_); + if (context_->IsKilled()) { + return -E_OBJ_IS_KILLED; + } + if (!IsPacketValid(inMsg)) { + return -E_INVALID_ARGS; + } + if ((inMsg->GetMessageType() == TYPE_RESPONSE) && (inMsg->GetMessageId() != TIME_SYNC_MESSAGE)) { + context_->IncSequenceId(); + int errCode = ResetWatchDog(); + if (errCode != E_OK) { + LOGW("[MultiVerSyncStateMachine][MessageCallback] ResetWatchDog failed , err %d", errCode); + } + } + return E_OK; +} + +int MultiVerSyncStateMachine::ReceiveMessageCallback(Message *inMsg) +{ + if (inMsg == nullptr) { + return -E_INVALID_ARGS; + } + if (inMsg->IsFeedbackError()) { + LOGE("[MultiVerSyncStateMachine] Feedback Message with errorNo=%u.", inMsg->GetErrorNo()); + return -static_cast(inMsg->GetErrorNo()); + } + if (inMsg->GetMessageId() == TIME_SYNC_MESSAGE) { + return TimeSyncPacketRecvCallback(context_, inMsg); + } + std::lock_guard lock(stateMachineLock_); + int errCode = MessageCallbackCheck(inMsg); + if (errCode != E_OK) { + return errCode; + } + switch (inMsg->GetMessageId()) { + case COMMIT_HISTORY_SYNC_MESSAGE: + errCode = CommitHistorySyncPktRecvCallback(context_, inMsg); + if ((errCode != -E_NOT_FOUND) && (inMsg->GetMessageType() == TYPE_REQUEST) && (errCode != -E_NOT_PERMIT)) { + SyncResponseBegin(inMsg->GetSessionId()); + } + break; + case MULTI_VER_DATA_SYNC_MESSAGE: + errCode = MultiVerDataPktRecvCallback(context_, inMsg); + break; + case VALUE_SLICE_SYNC_MESSAGE: + errCode = ValueSlicePktRecvCallback(context_, inMsg); + break; + default: + errCode = -E_NOT_SUPPORT; + break; + } + if (errCode == -E_LAST_SYNC_FRAME) { + SyncResponseEnd(inMsg->GetSessionId()); + return errCode; + } + if (errCode != E_OK && inMsg->GetMessageType() == TYPE_RESPONSE) { + Abort(); + } + return errCode; +} + +void MultiVerSyncStateMachine::StepToTimeout(TimerId timerId) +{ + { + std::lock_guard lock(stateMachineLock_); + TimerId timer = syncContext_->GetTimerId(); + if (timer != timerId) { + return; + } + currentState_ = SYNC_TIME_OUT; + } + Abort(); +} + +int MultiVerSyncStateMachine::CommitHistorySyncStepInner(void) +{ + int errCode = commitHistorySync_->SyncStart(context_); + if (errCode != E_OK) { + LOGE("[MultiVerSyncStateMachine][CommitHistorySyncStep] failed, errCode %d", errCode); + } + return errCode; +} + +int MultiVerSyncStateMachine::MultiVerDataSyncStepInner(void) +{ + return multiVerDataSync_->SyncStart(context_); +} + +int MultiVerSyncStateMachine::ValueSliceSyncStepInner(void) +{ + return valueSliceSync_->SyncStart(context_); +} + +void MultiVerSyncStateMachine::SyncStepInnerLocked() +{ + if (context_->IncUsedCount() != E_OK) { + goto SYNC_STEP_OUT; + } + + LOGD("[MultiVerSyncStateMachine] SyncStep dst=%s, state = %d", STR_MASK(context_->GetDeviceId()), currentState_); + int errCode; + { + std::lock_guard lock(stateMachineLock_); + switch (currentState_) { + case COMMIT_HISTORY_SYNC: + errCode = CommitHistorySyncStepInner(); + if (errCode != E_OK) { + Abort(); + } + break; + case MULTI_VER_DATA_ENTRY_SYNC: + errCode = MultiVerDataSyncStepInner(); + if (errCode == -E_NOT_FOUND) { + Finish(); + goto SYNC_STEP_SAFE_OUT; + } + break; + case MULTI_VER_VALUE_SLICE_SYNC: + errCode = ValueSliceSyncStepInner(); + if (errCode == -E_NOT_FOUND) { + int err = OneCommitSyncFinish(); + if (err != E_OK) { + valueSliceSync_->SendFinishedRequest(context_); + Abort(); + goto SYNC_STEP_SAFE_OUT; + } + currentState_ = MULTI_VER_DATA_ENTRY_SYNC; + SyncStep(); + goto SYNC_STEP_SAFE_OUT; + } + break; + default: + break; + } + } + +SYNC_STEP_SAFE_OUT: + context_->SafeExit(); + +SYNC_STEP_OUT: + RefObject::DecObjRef(communicator_); + RefObject::DecObjRef(context_); +} + +void MultiVerSyncStateMachine::SyncStepInner() +{ +} + +int MultiVerSyncStateMachine::StartSyncInner() +{ + LOGI("[MultiVerSyncStateMachine] StartSync"); + currentState_ = COMMIT_HISTORY_SYNC; + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->TimeRecordStart(); + } + int errCode = StartWatchDog(); + if (errCode != E_OK) { + LOGE("[MultiVerSyncStateMachine][StartSync] WatchDog start failed! err:%d", errCode); + return errCode; + } + SyncStep(); + return E_OK; +} + +void MultiVerSyncStateMachine::AbortInner() +{ + context_->Clear(); + StepToIdle(); + ExecNextTask(); +} + +const std::vector &MultiVerSyncStateMachine::GetStateSwitchTables() const +{ + return stateSwitchTables_; +} + +int MultiVerSyncStateMachine::PrepareNextSyncTask() +{ + return StartSyncInner(); +} + +void MultiVerSyncStateMachine::SendSaveDataNotifyPacket(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) +{ + (void)sessionId; + (void)sequenceId; + (void)inMsgId; +} + +void MultiVerSyncStateMachine::CommErrAbort() +{ + std::lock_guard lock(stateMachineLock_); + Abort(); + RefObject::DecObjRef(context_); +} + +int MultiVerSyncStateMachine::TimeSyncPacketRecvCallback(const MultiVerSyncTaskContext *context, const Message *inMsg) +{ + int errCode; + if ((context == nullptr) || (inMsg == nullptr) || (inMsg->GetMessageId() != TIME_SYNC_MESSAGE)) { + return -E_INVALID_ARGS; + } + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + errCode = timeSync_->RequestRecv(inMsg); + return errCode; + case TYPE_RESPONSE: + errCode = timeSync_->AckRecv(inMsg); + if (errCode != E_OK) { + LOGE("[MultiVerSyncStateMachine] TimeSyncPacketRecvCallback AckRecv failed err %d", errCode); + } + return errCode; + default: + return -E_INVALID_ARGS; + } +} + +int MultiVerSyncStateMachine::CommitHistorySyncPktRecvCallback(MultiVerSyncTaskContext *context, const Message *inMsg) +{ + if ((context == nullptr) || (inMsg == nullptr) || (inMsg->GetMessageId() != COMMIT_HISTORY_SYNC_MESSAGE)) { + return -E_INVALID_ARGS; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + int errCode; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_SEND_LOCAL_DATA_CHANGED_TO_COMMIT_REQUEST_RECV); + } + return commitHistorySync_->RequestRecvCallback(context, inMsg); + case TYPE_RESPONSE: + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_COMMIT_SEND_REQUEST_TO_ACK_RECV); + } + errCode = commitHistorySync_->AckRecvCallback(context, inMsg); + if (errCode != E_OK) { + return errCode; + } + currentState_ = MULTI_VER_DATA_ENTRY_SYNC; + SyncStep(); + return errCode; + default: + return -E_INVALID_ARGS; + } +} + +int MultiVerSyncStateMachine::MultiVerDataPktRecvCallback(MultiVerSyncTaskContext *context, const Message *inMsg) +{ + if ((context == nullptr) || (inMsg == nullptr) || (inMsg->GetMessageId() != MULTI_VER_DATA_SYNC_MESSAGE)) { + return -E_INVALID_ARGS; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + int errCode; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return multiVerDataSync_->RequestRecvCallback(context, inMsg); + case TYPE_RESPONSE: + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_DATA_ENTRY_SEND_REQUEST_TO_ACK_RECV); + } + errCode = multiVerDataSync_->AckRecvCallback(context, inMsg); + if (errCode != E_OK) { + multiVerDataSync_->SendFinishedRequest(context); + return errCode; + } + currentState_ = MULTI_VER_VALUE_SLICE_SYNC; + SyncStep(); + return errCode; + default: + return -E_INVALID_ARGS; + } +} + +int MultiVerSyncStateMachine::ValueSlicePktRecvCallback(MultiVerSyncTaskContext *context, const Message *inMsg) +{ + if ((context == nullptr) || (inMsg == nullptr) || (inMsg->GetMessageId() != VALUE_SLICE_SYNC_MESSAGE)) { + return -E_INVALID_ARGS; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + int errCode; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return valueSliceSync_->RequestRecvCallback(context, inMsg); + case TYPE_RESPONSE: + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_VALUE_SLICE_SEND_REQUEST_TO_ACK_RECV); + } + errCode = valueSliceSync_->AckRecvCallback(context, inMsg); + if (errCode != E_OK) { + valueSliceSync_->SendFinishedRequest(context); + return errCode; + } + currentState_ = MULTI_VER_VALUE_SLICE_SYNC; + SyncStep(); + return errCode; + default: + return -E_INVALID_ARGS; + } +} + +void MultiVerSyncStateMachine::Finish() +{ + MultiVerCommitNode commit; + std::vector commits; + int commitsSize = context_->GetCommitsSize(); + if (commitsSize > 0) { + context_->GetCommit(commitsSize - 1, commit); + context_->GetCommits(commits); + LOGD("MultiVerSyncStateMachine::Finish merge src=%s", STR_MASK(context_->GetDeviceId())); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_MERGE); + } + int errCode = multiVerDataSync_->MergeSyncCommit(commit, commits); + LOGD("MultiVerSyncStateMachine::Finish merge src=%s, MergeSyncCommit errCode:%d", + STR_MASK(context_->GetDeviceId()), errCode); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_MERGE); + } + } + RefObject::AutoLock lock(context_); + context_->SetOperationStatus(SyncOperation::OP_FINISHED_ALL); + StepToIdle(); + ExecNextTask(); +} + +int MultiVerSyncStateMachine::OneCommitSyncFinish() +{ + MultiVerCommitNode commit; + std::vector entries; + std::string deviceName; + TimeOffset outOffset = 0; + int errCode = E_OK; + int commitIndex = context_->GetCommitIndex(); + + LOGD("MultiVerSyncStateMachine::OneCommitSyncFinish src=%s, commitIndex = %d,", STR_MASK(context_->GetDeviceId()), + commitIndex); + if (commitIndex > 0) { + context_->GetCommit(commitIndex - 1, commit); + deviceName = context_->GetDeviceId(); + context_->GetEntries(entries); + LOGD("MultiVerSyncStateMachine::OneCommitSyncFinish src=%s, entries size = %lu", + STR_MASK(context_->GetDeviceId()), entries.size()); + errCode = timeSync_->GetTimeOffset(outOffset, TIME_SYNC_WAIT_TIME); + if (errCode != E_OK) { + LOGI("MultiVerSyncStateMachine::OneCommitSyncFinish GetTimeOffset fail errCode:%d", errCode); + return errCode; + } + Timestamp currentLocalTime = context_->GetCurrentLocalTime(); + commit.timestamp -= static_cast(outOffset); + + // Due to time sync error, commit timestamp may bigger than currentLocalTime, we need to fix the timestamp + TimeOffset timefixOffset = (commit.timestamp < currentLocalTime) ? 0 : (commit.timestamp - + static_cast(currentLocalTime)); + LOGD("MultiVerSyncStateMachine::OneCommitSyncFinish src=%s, timefixOffset = %" PRId64, + STR_MASK(context_->GetDeviceId()), timefixOffset); + commit.timestamp -= static_cast(timefixOffset); + ChangeEntriesTimestamp(entries, outOffset, timefixOffset); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_PUT_COMMIT_DATA); + } + errCode = multiVerDataSync_->PutCommitData(commit, entries, deviceName); + LOGD("MultiVerSyncStateMachine::OneCommitSyncFinish PutCommitData src=%s, errCode = %d", + STR_MASK(context_->GetDeviceId()), errCode); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_PUT_COMMIT_DATA); + } + if (errCode == E_OK) { + context_->ReleaseEntries(); + } + } + DBCommon::PrintHexVector(commit.commitId, __LINE__); + return errCode; +} + +bool MultiVerSyncStateMachine::IsPacketValid(const Message *inMsg) const +{ + if (inMsg == nullptr) { + return false; + } + + if ((inMsg->GetMessageId() < TIME_SYNC_MESSAGE) || (inMsg->GetMessageId() > VALUE_SLICE_SYNC_MESSAGE) || + (inMsg->GetMessageId() == DATA_SYNC_MESSAGE)) { + LOGE("[MultiVerSyncStateMachine] Message is invalid, id = %d", inMsg->GetMessageId()); + return false; + } + if (inMsg->GetMessageId() == TIME_SYNC_MESSAGE) { + return true; + } + if (inMsg->GetMessageType() == TYPE_RESPONSE) { + if ((inMsg->GetSequenceId() != context_->GetSequenceId()) || + (inMsg->GetSessionId() != context_->GetRequestSessionId())) { + LOGE("[MultiVerSyncStateMachine] Message is invalid, inMsg SequenceId = %d, context seq = %d," + "msg session id = %d, context session = %d", inMsg->GetSequenceId(), context_->GetSequenceId(), + inMsg->GetSessionId(), context_->GetRequestSessionId()); + return false; + } + } + return true; +} + +void MultiVerSyncStateMachine::Clear() +{ + commitHistorySync_ = nullptr; + multiVerDataSync_ = nullptr; + timeSync_ = nullptr; + valueSliceSync_ = nullptr; + multiVerStorage_ = nullptr; + context_ = nullptr; +} + +void MultiVerSyncStateMachine::SyncResponseBegin(uint32_t sessionId) +{ + { + std::lock_guard lock(responseInfosLock_); + auto iter = std::find_if(responseInfos_.begin(), responseInfos_.end(), [sessionId](const ResponseInfo &info) { + return info.sessionId == sessionId; + }); + if (iter != responseInfos_.end()) { + LOGE("[MultiVerSyncStateMachine][SyncResponseEnd] sessionId existed! exit."); + return; + } + TimerAction timeOutCallback = + std::bind(&MultiVerSyncStateMachine::SyncResponseTimeout, this, std::placeholders::_1); + // To make sure context_ alive in timeout callback, we should IncObjRef for the context_. + RefObject::IncObjRef(context_); + TimerId timerId = 0; + int errCode = RuntimeContext::GetInstance()->SetTimer( + RESPONSE_TIME_OUT, timeOutCallback, + [this]() { + int ret = RuntimeContext::GetInstance()->ScheduleTask([this](){ RefObject::DecObjRef(context_); }); + if (ret != E_OK) { + LOGE("[MultiVerSyncStateMachine][SyncResponseEnd] timer finalizer ScheduleTask, errCode %d", ret); + } + }, + timerId); + if (errCode != E_OK) { + LOGE("[MultiVerSyncStateMachine][ResponseSessionBegin] SetTimer failed err %d", errCode); + RefObject::DecObjRef(context_); + return; + } + ResponseInfo info{sessionId, timerId}; + responseInfos_.push_back(info); + LOGI("[MultiVerSyncStateMachine][SyncResponseBegin] begin"); + } + multiVerStorage_->NotifyStartSyncOperation(); +} + +void MultiVerSyncStateMachine::SyncResponseEnd(uint32_t sessionId) +{ + { + std::lock_guard lock(responseInfosLock_); + auto iter = std::find_if(responseInfos_.begin(), responseInfos_.end(), [sessionId](const ResponseInfo &info) { + return info.sessionId == sessionId; + }); + if (iter == responseInfos_.end()) { + LOGW("[MultiVerSyncStateMachine][SyncResponseEnd] Can't find sync response %d", sessionId); + return; + } + RuntimeContext::GetInstance()->RemoveTimer(iter->timerId); + responseInfos_.erase(iter); + LOGI("[MultiVerSyncStateMachine][SyncResponseBegin] end response"); + } + multiVerStorage_->NotifyFinishSyncOperation(); +} + +int MultiVerSyncStateMachine::SyncResponseTimeout(TimerId timerId) +{ + uint32_t sessionId; + { + std::lock_guard lock(responseInfosLock_); + auto iter = std::find_if(responseInfos_.begin(), responseInfos_.end(), [timerId](const ResponseInfo &info) { + return info.timerId == timerId; + }); + if (iter == responseInfos_.end()) { + LOGW("[MultiVerSyncStateMachine][SyncResponseTimeout] Can't find sync response timerId %" PRIu64, timerId); + return E_OK; + } + sessionId = iter->sessionId; + } + SyncResponseEnd(sessionId); + return E_OK; +} + +bool MultiVerSyncStateMachine::IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query) +{ + (void) inMsg; + (void) query; + return false; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/multi_ver_sync_state_machine.h b/mock/distributeddb/syncer/src/multi_ver_sync_state_machine.h new file mode 100644 index 0000000000000000000000000000000000000000..ea02f5d4e551848543d9788827c4992b5860d6ff --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_sync_state_machine.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_SYNC_STATE_MACHINE_H +#define MULTI_VER_SYNC_STATE_MACHINE_H + +#ifndef OMIT_MULTI_VER +#include + +#include "commit_history_sync.h" +#include "db_types.h" +#include "meta_data.h" +#include "multi_ver_data_sync.h" +#include "multi_ver_sync_task_context.h" +#include "sync_state_machine.h" +#include "time_sync.h" +#include "value_slice_sync.h" + +namespace DistributedDB { +class MultiVerSyncStateMachine final : public SyncStateMachine { +public: + struct ResponseInfo { + uint32_t sessionId = 0; + TimerId timerId = 0; + }; + + MultiVerSyncStateMachine(); + ~MultiVerSyncStateMachine() override; + + // Init the MultiVerSyncStateMachine + int Initialize(ISyncTaskContext *context, ISyncInterface *syncInterface, std::shared_ptr &metadata, + ICommunicator *communicator) override; + + // send Message to the StateMachine + int ReceiveMessageCallback(Message *inMsg) override; + + // Called by CommErrHandler, used to abort sync when handle err + void CommErrAbort() override; + + DISABLE_COPY_ASSIGN_MOVE(MultiVerSyncStateMachine); + +protected: + // Step the MultiVerSyncStateMachine + void SyncStep() override; + + // SyncOperation is timeout, step to timeout state + void StepToTimeout(TimerId timerId) override; + + void SyncStepInnerLocked() override; + + void SyncStepInner() override; + + void AbortInner() override; + + int StartSyncInner() override; + + const std::vector &GetStateSwitchTables() const override; + + // Do some init for run a next sync task + int PrepareNextSyncTask() override; + + // Called by StartSaveDataNotifyTimer, used to send a save data notify packet + void SendSaveDataNotifyPacket(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) override; + + bool IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query) override; + +private: + enum State { + IDLE, + TIME_SYNC, + COMMIT_HISTORY_SYNC, + MULTI_VER_DATA_ENTRY_SYNC, + MULTI_VER_VALUE_SLICE_SYNC, + SYNC_TIME_OUT, + INNER_ERR + }; + + void StepToIdle(); + + int MessageCallbackCheck(const Message *inMsg); + + int CommitHistorySyncStepInner(void); + + int MultiVerDataSyncStepInner(void); + + int ValueSliceSyncStepInner(void); + + int TimeSyncPacketRecvCallback(const MultiVerSyncTaskContext *context, const Message *inMsg); + + int CommitHistorySyncPktRecvCallback(MultiVerSyncTaskContext *context, const Message *inMsg); + + int MultiVerDataPktRecvCallback(MultiVerSyncTaskContext *context, const Message *inMsg); + + int ValueSlicePktRecvCallback(MultiVerSyncTaskContext *context, const Message *inMsg); + + void Finish(); + + int OneCommitSyncFinish(); + + bool IsPacketValid(const Message *inMsg) const; + + void Clear(); + + // Mark sync response is begin now, we should disable real delete + void SyncResponseBegin(uint32_t sessionId); + + // Mark sync response is finished, we should enable real delete + void SyncResponseEnd(uint32_t sessionId); + + // Mark sync response may has an err, has not received finish ack, we should enable real delete + int SyncResponseTimeout(TimerId timerId); + + static const int RESPONSE_TIME_OUT = 30 * 1000; // 30s + + static std::vector stateSwitchTables_; + MultiVerSyncTaskContext *context_; + MultiVerKvDBSyncInterface *multiVerStorage_; + std::mutex responseInfosLock_; + std::list responseInfos_; + std::unique_ptr timeSync_; + std::unique_ptr commitHistorySync_; + std::unique_ptr multiVerDataSync_; + std::unique_ptr valueSliceSync_; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_SYNC_STATE_MACHINE_H +#endif diff --git a/mock/distributeddb/syncer/src/multi_ver_sync_target.h b/mock/distributeddb/syncer/src/multi_ver_sync_target.h new file mode 100644 index 0000000000000000000000000000000000000000..aea804816f5edc2f154bcc8ffc7c3dcaefb3cb68 --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_sync_target.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_SYNC_TASK_INFO_H +#define MULTI_VER_SYNC_TASK_INFO_H + +#include "sync_target.h" + +namespace DistributedDB { +class MultiVerSyncTarget final : public SyncTarget { +}; +} // namespace DistributedDB + +#endif // MULTI_VER_SYNC_TASK_INFO_H diff --git a/mock/distributeddb/syncer/src/multi_ver_sync_task_context.cpp b/mock/distributeddb/syncer/src/multi_ver_sync_task_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..77e522b1452ea3ed7e807704f7e5612cf3431b4a --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_sync_task_context.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "db_common.h" +#include "db_dfx_adapter.h" +#include "log_print.h" +#include "multi_ver_sync_state_machine.h" +#include "multi_ver_sync_target.h" +#include "multi_ver_sync_task_context.h" + +namespace DistributedDB { +DEFINE_OBJECT_TAG_FACILITIES(MultiVerSyncTaskContext) + +MultiVerSyncTaskContext::~MultiVerSyncTaskContext() +{ +} + +int MultiVerSyncTaskContext::Initialize(const std::string &deviceId, ISyncInterface *syncInterface, + std::shared_ptr &metadata, ICommunicator *communicator) +{ + if (deviceId.empty() || (syncInterface == nullptr) || (communicator == nullptr)) { + return -E_INVALID_ARGS; + } + syncInterface_ = syncInterface; + communicator_ = communicator; + deviceId_ = deviceId; + taskExecStatus_ = INIT; + isAutoSync_ = true; + timeHelper_ = std::make_unique(); + int errCode = timeHelper_->Initialize(syncInterface, metadata); + if (errCode != E_OK) { + LOGE("[MultiVerSyncTaskContext] timeHelper Initialize failed, err %d.", errCode); + return errCode; + } + + stateMachine_ = new (std::nothrow) MultiVerSyncStateMachine; + if (stateMachine_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + + errCode = stateMachine_->Initialize(this, syncInterface, metadata, communicator); + TimerAction timeOutCallback = std::bind(&SyncStateMachine::TimeoutCallback, + static_cast(stateMachine_), + std::placeholders::_1); + SetTimeoutCallback(timeOutCallback); + OnKill([this]() { this->KillWait(); }); + { + std::lock_guard lock(synTaskContextSetLock_); + synTaskContextSet_.insert(this); + } + std::vector label = syncInterface_->GetIdentifier(); + label.resize(3); // only show 3 bytes + syncActionName_ = DBDfxAdapter::SYNC_ACTION + "_" + + DBCommon::VectorToHexString(label) + "_" + deviceId_.c_str(); + return errCode; +} + +int MultiVerSyncTaskContext::AddSyncOperation(SyncOperation *operation) +{ + if (operation == nullptr) { + return -E_INVALID_ARGS; + } + + if (operation->IsAutoSync() && !IsTargetQueueEmpty()) { + LOGI("[MultiVerSyncTaskContext] Exist operation in queue, skip it!"); + operation->SetStatus(deviceId_, SyncOperation::OP_FINISHED_ALL); + return E_OK; + } + + MultiVerSyncTarget *target = new (std::nothrow) MultiVerSyncTarget; + if (target == nullptr) { + return -E_OUT_OF_MEMORY; + } + target->SetSyncOperation(operation); + target->SetTaskType(ISyncTarget::REQUEST); + AddSyncTarget(target); + return E_OK; +} + +int MultiVerSyncTaskContext::GetCommitIndex() const +{ + return commitsIndex_; +} + +void MultiVerSyncTaskContext::SetCommitIndex(int index) +{ + commitsIndex_ = index; +} + +int MultiVerSyncTaskContext::GetEntriesIndex() const +{ + return entriesIndex_; +} + +void MultiVerSyncTaskContext::SetEntriesIndex(int index) +{ + entriesIndex_ = index; +} + +int MultiVerSyncTaskContext::GetValueSlicesIndex() const +{ + return valueSlicesIndex_; +} + +void MultiVerSyncTaskContext::SetValueSlicesIndex(int index) +{ + valueSlicesIndex_ = index; +} + +void MultiVerSyncTaskContext::GetCommits(std::vector &commits) +{ + commits = commits_; +} + +void MultiVerSyncTaskContext::SetCommits(const std::vector &commits) +{ + commits_ = commits; +} + +void MultiVerSyncTaskContext::GetCommit(int index, MultiVerCommitNode &commit) const +{ + commit = commits_[index]; +} + +void MultiVerSyncTaskContext::SetCommit(int index, const MultiVerCommitNode &commit) +{ + commits_[index] = commit; +} + +void MultiVerSyncTaskContext::SetEntries(const std::vector &entries) +{ + entries_ = entries; +} + +void MultiVerSyncTaskContext::ReleaseEntries(void) +{ + for (auto &item : entries_) { + if (syncInterface_ != nullptr) { + static_cast(syncInterface_)->ReleaseKvEntry(item); + } + item = nullptr; + } + entries_.clear(); + entries_.shrink_to_fit(); +} + +void MultiVerSyncTaskContext::GetEntries(std::vector &entries) const +{ + entries = entries_; +} + +void MultiVerSyncTaskContext::GetEntry(int index, MultiVerKvEntry *&entry) +{ + entry = entries_[index]; +} + +void MultiVerSyncTaskContext::SetCommitsSize(int commitsSize) +{ + commitsSize_ = commitsSize; +} + +int MultiVerSyncTaskContext::GetCommitsSize() const +{ + return commitsSize_; +} + +void MultiVerSyncTaskContext::SetEntriesSize(int entriesSize) +{ + entriesSize_ = entriesSize; +} + +int MultiVerSyncTaskContext::GetEntriesSize() const +{ + return entriesSize_; +} + +void MultiVerSyncTaskContext::SetValueSlicesSize(int valueSlicesSize) +{ + valueSlicesSize_ = valueSlicesSize; +} + +int MultiVerSyncTaskContext::GetValueSlicesSize() const +{ + return valueSlicesSize_; +} + +void MultiVerSyncTaskContext::GetValueSliceHashNode(int index, ValueSliceHash &hashNode) const +{ + hashNode = valueSliceHashNodes_[index]; +} + +void MultiVerSyncTaskContext::SetValueSliceHashNodes(const std::vector &valueSliceHashNodes) +{ + valueSliceHashNodes_ = valueSliceHashNodes; +} + +void MultiVerSyncTaskContext::GetValueSliceHashNodes(std::vector &valueSliceHashNodes) const +{ + valueSliceHashNodes = valueSliceHashNodes_; +} + +void MultiVerSyncTaskContext::Clear() +{ + commits_.clear(); + commits_.shrink_to_fit(); + ReleaseEntries(); + valueSliceHashNodes_.clear(); + valueSliceHashNodes_.shrink_to_fit(); + commitsIndex_ = 0; + commitsSize_ = 0; + entriesIndex_ = 0; + entriesSize_ = 0; + valueSlicesIndex_ = 0; + valueSlicesSize_ = 0; + retryTime_ = 0; + isNeedRetry_ = NO_NEED_RETRY; + StopTimer(); + sequenceId_ = 1; // minimum valid ID : 1 + syncId_ = 0; +} + +void MultiVerSyncTaskContext::CopyTargetData(const ISyncTarget *target, const TaskParam &taskParam) +{ + SyncTaskContext::CopyTargetData(target, taskParam); +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/multi_ver_sync_task_context.h b/mock/distributeddb/syncer/src/multi_ver_sync_task_context.h new file mode 100644 index 0000000000000000000000000000000000000000..054e9759db00065de931d5588a31e3d3b14b8308 --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_sync_task_context.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_SYNC_TASK_CONTEXT_H +#define MULTI_VER_SYNC_TASK_CONTEXT_H + +#ifndef OMIT_MULTI_VER +#include "multi_ver_kvdb_sync_interface.h" +#include "sync_task_context.h" + +namespace DistributedDB { +class MultiVerSyncTaskContext final : public SyncTaskContext { +public: + MultiVerSyncTaskContext() {}; + + DISABLE_COPY_ASSIGN_MOVE(MultiVerSyncTaskContext); + + // Init the MultiVerSyncTaskContext + int Initialize(const std::string &deviceId, ISyncInterface *syncInterface, std::shared_ptr &metadata, + ICommunicator *communicator) override; + + // Add a sync task target with the operation to the queue + int AddSyncOperation(SyncOperation *operation) override; + + int GetCommitIndex() const; + + void SetCommitIndex(int index); + + int GetEntriesIndex() const; + + void SetEntriesIndex(int index); + + int GetValueSlicesIndex() const; + + void SetValueSlicesIndex(int index); + + void GetCommits(std::vector &commits); + + void SetCommits(const std::vector &commits); + + void GetCommit(int index, MultiVerCommitNode &commit) const; + + void SetCommit(int index, const MultiVerCommitNode &commit); + + void SetEntries(const std::vector &entries); + + void ReleaseEntries(void); + + void GetEntries(std::vector &entries) const; + + void GetEntry(int index, MultiVerKvEntry *&entry); + + void SetCommitsSize(int commitsSize); + + int GetCommitsSize() const; + + void SetEntriesSize(int entriesSize); + + int GetEntriesSize() const; + + void SetValueSlicesSize(int valueSlicesSize); + + int GetValueSlicesSize() const; + + void GetValueSliceHashNode(int index, ValueSliceHash &hashNode) const; + + void SetValueSliceHashNodes(const std::vector &valueSliceHashNodes); + + void GetValueSliceHashNodes(std::vector &valueSliceHashNodes) const; + + void Clear() override; + +protected: + ~MultiVerSyncTaskContext() override; + + void CopyTargetData(const ISyncTarget *target, const TaskParam &taskParam) override; + +private: + DECLARE_OBJECT_TAG(MultiVerSyncTaskContext); + + std::vector commits_; + std::vector entries_; + std::vector valueSliceHashNodes_; + int commitsIndex_ = 0; + int commitsSize_ = 0; + int entriesIndex_ = 0; + int entriesSize_ = 0; + int valueSlicesIndex_ = 0; + int valueSlicesSize_ = 0; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_SYNC_TASK_CONTEXT_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/multi_ver_syncer.cpp b/mock/distributeddb/syncer/src/multi_ver_syncer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..96018b9c686d37dc414083f68986ad11d5fed755 --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_syncer.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "multi_ver_syncer.h" + +#include +#include +#include + +#include "multi_ver_sync_engine.h" +#include "multi_ver_kvdb_sync_interface.h" +#include "log_print.h" + +namespace DistributedDB { +MultiVerSyncer::MultiVerSyncer() + : autoSyncEnable_(true) +{ +} + +MultiVerSyncer::~MultiVerSyncer() +{ +} + +void MultiVerSyncer::EnableAutoSync(bool enable) +{ + LOGD("[Syncer] EnableAutoSync enable = %d", enable); + if (autoSyncEnable_ == enable) { + return; + } + + autoSyncEnable_ = enable; + if (!enable || (syncEngine_ == nullptr)) { + return; + } + + std::vector devices; + GetOnlineDevices(devices); + if (devices.empty()) { + return; + } + + int errCode = Sync(devices, SyncModeType::AUTO_PULL, nullptr, nullptr, false); + if (errCode != E_OK) { + LOGE("[Syncer] sync start by EnableAutoSync failed err %d", errCode); + } +} + +int MultiVerSyncer::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) +{ + return EraseDeviceWaterMark(deviceId, isNeedHash, ""); +} + +int MultiVerSyncer::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, + const std::string &tableName) +{ + return -E_NOT_SUPPORT; +} + +void MultiVerSyncer::LocalDataChanged(int notifyEvent) +{ + if (!initialized_) { + LOGE("[Syncer] Syncer has not Init"); + return; + } + + if (!autoSyncEnable_) { + return; + } + + syncEngine_->BroadCastDataChanged(); +} + +void MultiVerSyncer::RemoteDataChanged(const std::string &device) +{ + LOGD("[MultiVerSyncer] Remote data changed or device online dev %s{private}", device.c_str()); + if (autoSyncEnable_) { + std::vector devices {device}; + int errCode = Sync(devices, SyncModeType::AUTO_PULL, nullptr, nullptr, false); + if (errCode != E_OK) { + LOGE("[MultiVerSyncer] sync start by RemoteDataChanged failed err %d", errCode); + } + } +} + +void MultiVerSyncer::RemoteDeviceOffline(const std::string &device) +{ + (void) device; +} + +ISyncEngine *MultiVerSyncer::CreateSyncEngine() +{ + return new (std::nothrow) MultiVerSyncEngine(); +} + +void MultiVerSyncer::AddSyncOperation(SyncOperation *operation) +{ + MultiVerKvDBSyncInterface *syncInterface = static_cast(syncInterface_); + syncInterface->NotifyStartSyncOperation(); + GenericSyncer::AddSyncOperation(operation); +} + +void MultiVerSyncer::SyncOperationKillCallbackInner(int syncId) +{ + if (syncInterface_ != nullptr) { + LOGI("[MultiVerSyncer] Operation on kill id = %d", syncId); + MultiVerKvDBSyncInterface *syncInterface = static_cast(syncInterface_); + syncInterface->NotifyFinishSyncOperation(); + } + GenericSyncer::SyncOperationKillCallbackInner(syncId); +} + +int MultiVerSyncer::SetStaleDataWipePolicy(WipePolicy policy) +{ + (void) policy; + return -E_NOT_SUPPORT; +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/multi_ver_syncer.h b/mock/distributeddb/syncer/src/multi_ver_syncer.h new file mode 100644 index 0000000000000000000000000000000000000000..37b431281d7b6c948c6a7c398765f12ba47f8fca --- /dev/null +++ b/mock/distributeddb/syncer/src/multi_ver_syncer.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_SYNCER_H +#define MULTI_VER_SYNCER_H + +#ifndef OMIT_MULTI_VER +#include "generic_syncer.h" +#include "macro_utils.h" + +namespace DistributedDB { +class MultiVerSyncer final : public GenericSyncer { +public: + MultiVerSyncer(); + ~MultiVerSyncer() override; + + // Enable auto sync function + void EnableAutoSync(bool enable) override; + + // delete specified device's watermark + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) override; + + // delete specified device's and table's watermark + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, + const std::string &tableName) override; + + // Local data changed callback + void LocalDataChanged(int notifyEvent) override; + + // Remote data changed callback + void RemoteDataChanged(const std::string &device) override; + + void RemoteDeviceOffline(const std::string &device) override; + + // Set stale data wipe policy + int SetStaleDataWipePolicy(WipePolicy policy) override; + +protected: + // Create a sync engine, if has memory error, will return nullptr. + ISyncEngine *CreateSyncEngine() override; + + // Add a Sync Operation, after call this function, the operation will be start + void AddSyncOperation(SyncOperation *operation) override; + + // Used to set to the SyncOperation Onkill + void SyncOperationKillCallbackInner(int syncId) override; + +private: + bool autoSyncEnable_; +}; +} // namespace DistributedDB + +#endif // MULTI_VER_SYNCER_H +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/query_sync_water_mark_helper.cpp b/mock/distributeddb/syncer/src/query_sync_water_mark_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4fbeb8a0dbe9b8046bf48bc3f1e41ffefdb2e4fd --- /dev/null +++ b/mock/distributeddb/syncer/src/query_sync_water_mark_helper.cpp @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "query_sync_water_mark_helper.h" + +#include +#include +#include "platform_specific.h" +#include "parcel.h" +#include "db_errno.h" +#include "db_common.h" +#include "log_print.h" + +namespace DistributedDB { +namespace { + const int MAX_CACHE_ITEMS = 200; + const uint32_t MAX_STORE_ITEMS = 100000; + // WaterMark Version + constexpr uint32_t QUERY_WATERMARK_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_6_0; + constexpr uint32_t DELETE_WATERMARK_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_3_0; + // Prefix Key in db + const std::string QUERY_SYNC_PREFIX_KEY = "querySync"; + const std::string DELETE_SYNC_PREFIX_KEY = "deleteSync"; +} + +QuerySyncWaterMarkHelper::QuerySyncWaterMarkHelper() + : storage_(nullptr) +{} + +QuerySyncWaterMarkHelper::~QuerySyncWaterMarkHelper() +{ + storage_ = nullptr; + deviceIdToHashQuerySyncIdMap_.clear(); + deleteSyncCache_.clear(); + deviceIdToHashDeleteSyncIdMap_.clear(); +} + +int LruMap::Put(const std::string &key, const QueryWaterMark &inValue) +{ + std::lock_guard autoLock(lruLock_); + cache_[key] = inValue; + return Elimination(key, inValue); +} + +int LruMap::Get(const std::string &key, QueryWaterMark &outValue) +{ + std::lock_guard autoLock(lruLock_); + if (cache_.find(key) == cache_.end()) { + return -E_NOT_FOUND; + } + outValue = cache_[key]; + return Elimination(key, outValue); +} + +void LruMap::RemoveWithPrefixKey(const std::string &prefixKey) +{ + std::lock_guard autoLock(lruLock_); + auto iterator = eliminationChain_.begin(); + while (iterator != eliminationChain_.end()) { + const std::string &key = (*iterator).first; + if (key.find(prefixKey) == 0) { + (void)cache_.erase(key); + iterator = eliminationChain_.erase(iterator); + } else { + iterator++; + } + } +} + +// move the node to last and remove the first node until the size less than limit +int LruMap::Elimination(const std::string &key, const QueryWaterMark &inQueryWaterMark) +{ + auto iterator = eliminationChain_.begin(); + while (iterator != eliminationChain_.end()) { + if ((*iterator).first == key) { + eliminationChain_.erase(iterator); + break; + } + iterator++; + } + std::pair entry = {key, inQueryWaterMark}; + eliminationChain_.push_back(entry); + while (eliminationChain_.size() > MAX_CACHE_ITEMS) { + std::pair &pair = eliminationChain_.front(); + cache_.erase(pair.first); + eliminationChain_.pop_front(); + } + return E_OK; +} + +int QuerySyncWaterMarkHelper::GetMetadataFromDb(const std::vector &key, std::vector &outValue) +{ + if (storage_ == nullptr) { + return -E_INVALID_DB; + } + return storage_->GetMetaData(key, outValue); +} + +int QuerySyncWaterMarkHelper::SetMetadataToDb(const std::vector &key, const std::vector &inValue) +{ + if (storage_ == nullptr) { + return -E_INVALID_DB; + } + return storage_->PutMetaData(key, inValue); +} + +int QuerySyncWaterMarkHelper::DeleteMetaDataFromDB(const std::vector &keys) const +{ + if (storage_ == nullptr) { + return -E_INVALID_DB; + } + return storage_->DeleteMetaData(keys); +} + +int QuerySyncWaterMarkHelper::Initialize(ISyncInterface *storage) +{ + storage_ = storage; + return E_OK; +} + +int QuerySyncWaterMarkHelper::LoadDeleteSyncDataToCache(const Key &deleteWaterMarkKey) +{ + std::vector value; + int errCode = GetMetadataFromDb(deleteWaterMarkKey, value); + if (errCode != E_OK) { + return errCode; + } + DeleteWaterMark deleteWaterMark; + std::string dbKey(deleteWaterMarkKey.begin(), deleteWaterMarkKey.end()); + errCode = DeSerializeDeleteWaterMark(value, deleteWaterMark); + if (errCode != E_OK) { + return errCode; + } + std::lock_guard autoLock(deleteSyncLock_); + deleteSyncCache_[dbKey] = deleteWaterMark; + return errCode; +} + +int QuerySyncWaterMarkHelper::GetQueryWaterMarkInCacheAndDb(const std::string &cacheKey, + QueryWaterMark &queryWaterMark) +{ + // first get from cache_ + int errCode = querySyncCache_.Get(cacheKey, queryWaterMark); + bool addToCache = false; + if (errCode == -E_NOT_FOUND) { + // second get from db + errCode = GetQueryWaterMarkFromDB(cacheKey, queryWaterMark); + addToCache = true; + } + if (errCode == -E_NOT_FOUND) { + // third generate one and save to db + errCode = PutQueryWaterMarkToDB(cacheKey, queryWaterMark); + } + // something error return + if (errCode != E_OK) { + LOGE("[Meta]GetQueryWaterMark Fail code = %d", errCode); + return errCode; + } + // remember add to cache_ + if (addToCache) { + querySyncCache_.Put(cacheKey, queryWaterMark); + } + return errCode; +} + +int QuerySyncWaterMarkHelper::GetQueryWaterMark(const std::string &queryIdentify, const std::string &deviceId, + QueryWaterMark &queryWaterMark) +{ + std::string cacheKey; + GetHashQuerySyncDeviceId(deviceId, queryIdentify, cacheKey); + std::lock_guard autoLock(queryWaterMarkLock_); + return GetQueryWaterMarkInCacheAndDb(cacheKey, queryWaterMark); +} + +int QuerySyncWaterMarkHelper::SetRecvQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, const WaterMark &waterMark) +{ + std::string cacheKey; + GetHashQuerySyncDeviceId(deviceId, queryIdentify, cacheKey); + std::lock_guard autoLock(queryWaterMarkLock_); + return SetRecvQueryWaterMarkWithoutLock(cacheKey, waterMark); +} + +int QuerySyncWaterMarkHelper::SetLastQueryTime(const std::string &queryIdentify, + const std::string &deviceId, const Timestamp ×tamp) +{ + std::string cacheKey; + GetHashQuerySyncDeviceId(deviceId, queryIdentify, cacheKey); + std::lock_guard autoLock(queryWaterMarkLock_); + QueryWaterMark queryWaterMark; + int errCode = GetQueryWaterMarkInCacheAndDb(cacheKey, queryWaterMark); + if (errCode != E_OK) { + return errCode; + } + queryWaterMark.lastQueryTime = timestamp; + return UpdateCacheAndSave(cacheKey, queryWaterMark); +} + +int QuerySyncWaterMarkHelper::SetRecvQueryWaterMarkWithoutLock(const std::string &cacheKey, + const WaterMark &waterMark) +{ + QueryWaterMark queryWaterMark; + int errCode = GetQueryWaterMarkInCacheAndDb(cacheKey, queryWaterMark); + if (errCode != E_OK) { + return errCode; + } + queryWaterMark.recvWaterMark = waterMark; + return UpdateCacheAndSave(cacheKey, queryWaterMark); +} + +int QuerySyncWaterMarkHelper::SetSendQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, const WaterMark &waterMark) +{ + std::string cacheKey; + GetHashQuerySyncDeviceId(deviceId, queryIdentify, cacheKey); + QueryWaterMark queryWaterMark; + std::lock_guard autoLock(queryWaterMarkLock_); + int errCode = GetQueryWaterMarkInCacheAndDb(cacheKey, queryWaterMark); + if (errCode != E_OK) { + return errCode; + } + queryWaterMark.sendWaterMark = waterMark; + return UpdateCacheAndSave(cacheKey, queryWaterMark); +} + +int QuerySyncWaterMarkHelper::UpdateCacheAndSave(const std::string &cacheKey, + QueryWaterMark &queryWaterMark) +{ + // update lastUsedTime + int errCode = OS::GetCurrentSysTimeInMicrosecond(queryWaterMark.lastUsedTime); + if (errCode != E_OK) { + return errCode; + } + // save db first + errCode = SaveQueryWaterMarkToDB(cacheKey, queryWaterMark); + if (errCode != E_OK) { + return errCode; + } + querySyncCache_.Put(cacheKey, queryWaterMark); + return errCode; +} + +int QuerySyncWaterMarkHelper::PutQueryWaterMarkToDB(const DeviceID &dbKeyString, QueryWaterMark &queryWaterMark) +{ + int errCode = OS::GetCurrentSysTimeInMicrosecond(queryWaterMark.lastUsedTime); + if (errCode != E_OK) { + return errCode; + } + queryWaterMark.version = QUERY_WATERMARK_VERSION_CURRENT; + return SaveQueryWaterMarkToDB(dbKeyString, queryWaterMark); +} + +int QuerySyncWaterMarkHelper::SaveQueryWaterMarkToDB(const DeviceID &dbKeyString, const QueryWaterMark &queryWaterMark) +{ + // serialize value + Value dbValue; + int errCode = SerializeQueryWaterMark(queryWaterMark, dbValue); + if (errCode != E_OK) { + return errCode; + } + // serialize key + Key dbKey; + DBCommon::StringToVector(dbKeyString, dbKey); + // save + errCode = SetMetadataToDb(dbKey, dbValue); + if (errCode != E_OK) { + LOGE("QuerySyncWaterMarkHelper::SaveQueryWaterMarkToDB failed errCode:%d", errCode); + } + return errCode; +} + +int QuerySyncWaterMarkHelper::GetQueryWaterMarkFromDB(const DeviceID &dbKeyString, QueryWaterMark &queryWaterMark) +{ + // serialize key + Key dbKey; + DBCommon::StringToVector(dbKeyString, dbKey); + // search in db + Value dbValue; + int errCode = GetMetadataFromDb(dbKey, dbValue); + if (errCode != E_OK) { + return errCode; + } + return DeSerializeQueryWaterMark(dbValue, queryWaterMark); +} + +int QuerySyncWaterMarkHelper::SerializeQueryWaterMark(const QueryWaterMark &queryWaterMark, Value &outValue) +{ + uint64_t length = CalculateQueryWaterMarkSize(queryWaterMark); + outValue.resize(length); + Parcel parcel(outValue.data(), outValue.size()); + parcel.WriteUInt32(queryWaterMark.version); + parcel.EightByteAlign(); + parcel.WriteUInt64(queryWaterMark.sendWaterMark); + parcel.WriteUInt64(queryWaterMark.recvWaterMark); + parcel.WriteUInt64(queryWaterMark.lastUsedTime); + parcel.WriteString(queryWaterMark.sql); + parcel.WriteUInt64(queryWaterMark.lastQueryTime); + if (parcel.IsError()) { + LOGE("[Meta] Parcel error when serialize queryWaterMark"); + return -E_PARSE_FAIL; + } + return E_OK; +} + +int QuerySyncWaterMarkHelper::DeSerializeQueryWaterMark(const Value &dbQueryWaterMark, QueryWaterMark &queryWaterMark) +{ + Parcel parcel(const_cast(dbQueryWaterMark.data()), dbQueryWaterMark.size()); + parcel.ReadUInt32(queryWaterMark.version); + parcel.EightByteAlign(); + parcel.ReadUInt64(queryWaterMark.sendWaterMark); + parcel.ReadUInt64(queryWaterMark.recvWaterMark); + parcel.ReadUInt64(queryWaterMark.lastUsedTime); + parcel.ReadString(queryWaterMark.sql); + if (queryWaterMark.version >= SOFTWARE_VERSION_RELEASE_6_0) { + parcel.ReadUInt64(queryWaterMark.lastQueryTime); + } + if (parcel.IsError()) { + LOGE("[Meta] Parcel error when deserialize queryWaterMark"); + return -E_PARSE_FAIL; + } + return E_OK; +} + +uint64_t QuerySyncWaterMarkHelper::CalculateQueryWaterMarkSize(const QueryWaterMark &queryWaterMark) +{ + uint64_t length = Parcel::GetUInt32Len(); // version + length = Parcel::GetEightByteAlign(length); + length += Parcel::GetUInt64Len(); // sendWaterMark + length += Parcel::GetUInt64Len(); // recvWaterMark + length += Parcel::GetUInt64Len(); // lastUsedTime + length += Parcel::GetStringLen(queryWaterMark.sql); + length += Parcel::GetUInt64Len(); // lastQueryTime + return length; +} + +void QuerySyncWaterMarkHelper::GetHashQuerySyncDeviceId(const DeviceID &deviceId, + const DeviceID &queryId, DeviceID &hashQuerySyncId) +{ + std::lock_guard autoLock(queryWaterMarkLock_); + if (deviceIdToHashQuerySyncIdMap_[deviceId].count(queryId) == 0) { + // do not modify this + hashQuerySyncId = QUERY_SYNC_PREFIX_KEY + DBCommon::TransferHashString(deviceId) + queryId; + deviceIdToHashQuerySyncIdMap_[deviceId][queryId] = hashQuerySyncId; + } else { + hashQuerySyncId = deviceIdToHashQuerySyncIdMap_[deviceId][queryId]; + } +} + +int QuerySyncWaterMarkHelper::GetDeleteSyncWaterMark(const std::string &deviceId, DeleteWaterMark &deleteWaterMark) +{ + std::string hashId; + GetHashDeleteSyncDeviceId(deviceId, hashId); + return GetDeleteWaterMarkFromCache(hashId, deleteWaterMark); +} + +int QuerySyncWaterMarkHelper::SetSendDeleteSyncWaterMark(const DeviceID &deviceId, const WaterMark &waterMark) +{ + std::string hashId; + GetHashDeleteSyncDeviceId(deviceId, hashId); + DeleteWaterMark deleteWaterMark; + GetDeleteWaterMarkFromCache(hashId, deleteWaterMark); + deleteWaterMark.sendWaterMark = waterMark; + std::lock_guard autoLock(deleteSyncLock_); + return UpdateDeleteSyncCacheAndSave(hashId, deleteWaterMark); +} + +int QuerySyncWaterMarkHelper::SetRecvDeleteSyncWaterMark(const DeviceID &deviceId, const WaterMark &waterMark) +{ + std::string hashId; + GetHashDeleteSyncDeviceId(deviceId, hashId); + DeleteWaterMark deleteWaterMark; + GetDeleteWaterMarkFromCache(hashId, deleteWaterMark); + deleteWaterMark.recvWaterMark = waterMark; + std::lock_guard autoLock(deleteSyncLock_); + return UpdateDeleteSyncCacheAndSave(hashId, deleteWaterMark); +} + +int QuerySyncWaterMarkHelper::UpdateDeleteSyncCacheAndSave(const std::string &dbKey, + const DeleteWaterMark &deleteWaterMark) +{ + // save db first + int errCode = SaveDeleteWaterMarkToDB(dbKey, deleteWaterMark); + if (errCode != E_OK) { + return errCode; + } + // modify cache + deleteSyncCache_[dbKey] = deleteWaterMark; + return errCode; +} + +int QuerySyncWaterMarkHelper::GetDeleteWaterMarkFromCache(const DeviceID &hashDeviceId, + DeleteWaterMark &deleteWaterMark) +{ + // lock prevent different thread visit deleteSyncCache_ + std::lock_guard autoLock(deleteSyncLock_); + // if not found + if (deleteSyncCache_.find(hashDeviceId) == deleteSyncCache_.end()) { + DeleteWaterMark waterMark; + waterMark.version = DELETE_WATERMARK_VERSION_CURRENT; + int errCode = GetDeleteWaterMarkFromDB(hashDeviceId, waterMark); + if (errCode == -E_NOT_FOUND) { + deleteWaterMark.sendWaterMark = 0; + deleteWaterMark.recvWaterMark = 0; + errCode = E_OK; + } + if (errCode != E_OK) { + LOGE("[Meta]GetDeleteWaterMark Fail code = %d", errCode); + return errCode; + } + deleteSyncCache_.insert(std::pair(hashDeviceId, waterMark)); + } + deleteWaterMark = deleteSyncCache_[hashDeviceId]; + return E_OK; +} + +int QuerySyncWaterMarkHelper::GetDeleteWaterMarkFromDB(const DeviceID &hashDeviceId, + DeleteWaterMark &deleteWaterMark) +{ + Key dbKey; + DBCommon::StringToVector(hashDeviceId, dbKey); + // search in db + Value dbValue; + int errCode = GetMetadataFromDb(dbKey, dbValue); + if (errCode != E_OK) { + return errCode; + } + // serialize value + return DeSerializeDeleteWaterMark(dbValue, deleteWaterMark); +} + +int QuerySyncWaterMarkHelper::SaveDeleteWaterMarkToDB(const DeviceID &hashDeviceId, + const DeleteWaterMark &deleteWaterMark) +{ + // serialize value + Value dbValue; + int errCode = SerializeDeleteWaterMark(deleteWaterMark, dbValue); + if (errCode != E_OK) { + return errCode; + } + Key dbKey; + DBCommon::StringToVector(hashDeviceId, dbKey); + // save + errCode = SetMetadataToDb(dbKey, dbValue); + if (errCode != E_OK) { + LOGE("QuerySyncWaterMarkHelper::SaveDeleteWaterMarkToDB failed errCode:%d", errCode); + } + return errCode; +} + +void QuerySyncWaterMarkHelper::GetHashDeleteSyncDeviceId(const DeviceID &deviceId, DeviceID &hashDeleteSyncId) +{ + std::lock_guard autoLock(deleteSyncLock_); + if (deviceIdToHashDeleteSyncIdMap_.count(deviceId) == 0) { + hashDeleteSyncId = DELETE_SYNC_PREFIX_KEY + DBCommon::TransferHashString(deviceId); + deviceIdToHashDeleteSyncIdMap_.insert(std::pair(deviceId, hashDeleteSyncId)); + } else { + hashDeleteSyncId = deviceIdToHashDeleteSyncIdMap_[deviceId]; + } +} + +int QuerySyncWaterMarkHelper::SerializeDeleteWaterMark(const DeleteWaterMark &deleteWaterMark, + std::vector &outValue) +{ + uint64_t length = CalculateDeleteWaterMarkSize(); + outValue.resize(length); + Parcel parcel(outValue.data(), outValue.size()); + parcel.WriteUInt32(deleteWaterMark.version); + parcel.EightByteAlign(); + parcel.WriteUInt64(deleteWaterMark.sendWaterMark); + parcel.WriteUInt64(deleteWaterMark.recvWaterMark); + if (parcel.IsError()) { + LOGE("[Meta] Parcel error when serialize deleteWaterMark."); + return -E_PARSE_FAIL; + } + return E_OK; +} + +int QuerySyncWaterMarkHelper::DeSerializeDeleteWaterMark(const std::vector &inValue, + DeleteWaterMark &deleteWaterMark) +{ + Parcel parcel(const_cast(inValue.data()), inValue.size()); + parcel.ReadUInt32(deleteWaterMark.version); + parcel.EightByteAlign(); + parcel.ReadUInt64(deleteWaterMark.sendWaterMark); + parcel.ReadUInt64(deleteWaterMark.recvWaterMark); + if (parcel.IsError()) { + LOGE("[Meta] Parcel error when deserialize deleteWaterMark."); + return -E_PARSE_FAIL; + } + return E_OK; +} + +uint64_t QuerySyncWaterMarkHelper::CalculateDeleteWaterMarkSize() +{ + uint64_t length = Parcel::GetUInt32Len(); // version + length = Parcel::GetEightByteAlign(length); + length += Parcel::GetUInt64Len(); // sendWaterMark + length += Parcel::GetUInt64Len(); // recvWaterMark + return length; +} + +std::string QuerySyncWaterMarkHelper::GetQuerySyncPrefixKey() +{ + return QUERY_SYNC_PREFIX_KEY; +} + +std::string QuerySyncWaterMarkHelper::GetDeleteSyncPrefixKey() +{ + return DELETE_SYNC_PREFIX_KEY; +} + +int QuerySyncWaterMarkHelper::RemoveLeastUsedQuerySyncItems(const std::vector &querySyncIds) +{ + if (querySyncIds.size() < MAX_STORE_ITEMS) { + return E_OK; + } + std::vector> allItems; + std::map> idMap; + std::vector> waitToRemove; + for (const auto &id : querySyncIds) { + Value value; + int errCode = GetMetadataFromDb(id, value); + if (errCode != E_OK) { + waitToRemove.push_back(id); + continue; // may be this failure cause by wrong data + } + QueryWaterMark queryWaterMark; + std::string queryKey(id.begin(), id.end()); + errCode = DeSerializeQueryWaterMark(value, queryWaterMark); + if (errCode != E_OK) { + waitToRemove.push_back(id); + continue; // may be this failure cause by wrong data + } + idMap.insert({queryKey, id}); + allItems.emplace_back(queryKey, queryWaterMark.lastUsedTime); + } + // we only remove broken data below + // 1. common data size less then 10w + // 2. allItems.size() - MAX_STORE_ITEMS - waitToRemove.size() < 0 + // so we only let allItems.size() < MAX_STORE_ITEMS + waitToRemove.size() + if (allItems.size() < MAX_STORE_ITEMS + waitToRemove.size()) { + // remove in db + return DeleteMetaDataFromDB(waitToRemove); + } + uint32_t removeCount = allItems.size() - MAX_STORE_ITEMS - waitToRemove.size(); + // quick select the k_th least used + std::nth_element(allItems.begin(), allItems.begin() + removeCount, allItems.end(), + [](std::pair &w1, std::pair &w2) { + return w1.second < w2.second; + }); + for (uint32_t i = 0; i < removeCount; ++i) { + waitToRemove.push_back(idMap[allItems[i].first]); + } + // remove in db + return DeleteMetaDataFromDB(waitToRemove); +} + +int QuerySyncWaterMarkHelper::ResetRecvQueryWaterMark(const DeviceID &deviceId, const std::string &tableName) +{ + // lock prevent other thread modify queryWaterMark at this moment + { + std::lock_guard autoLock(queryWaterMarkLock_); + std::string prefixKeyStr = QUERY_SYNC_PREFIX_KEY + DBCommon::TransferHashString(deviceId); + if (!tableName.empty()) { + std::string hashTableName = DBCommon::TransferHashString(tableName); + std::string hexTableName = DBCommon::TransferStringToHex(hashTableName); + prefixKeyStr += hexTableName; + } + + // remove in db + Key prefixKey; + DBCommon::StringToVector(prefixKeyStr, prefixKey); + int errCode = storage_->DeleteMetaDataByPrefixKey(prefixKey); + if (errCode != E_OK) { + LOGE("[META]ResetRecvQueryWaterMark fail errCode:%d", errCode); + return errCode; + } + // clean cache + querySyncCache_.RemoveWithPrefixKey(prefixKeyStr); + } + return E_OK; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/query_sync_water_mark_helper.h b/mock/distributeddb/syncer/src/query_sync_water_mark_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..f0e6a8b4a10f786d2d54e80af68a414e89da5aec --- /dev/null +++ b/mock/distributeddb/syncer/src/query_sync_water_mark_helper.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef QUERY_SYNC_WATER_MARK_HELPER_H +#define QUERY_SYNC_WATER_MARK_HELPER_H + +#include +#include +#include +#include +#include +#include "db_types.h" +#include "ikvdb_sync_interface.h" + +namespace DistributedDB { +struct QueryWaterMark { + uint32_t version = 0; // start with 103 + WaterMark sendWaterMark = 0; + WaterMark recvWaterMark = 0; + Timestamp lastUsedTime = 0; // use for delete data + std::string sql; // for analyze sql from logs + Timestamp lastQueryTime = 0; // use for miss query scene add in 106 +}; + +struct DeleteWaterMark { + uint32_t version = 0; + WaterMark sendWaterMark = 0; + WaterMark recvWaterMark = 0; +}; + +// LRU map +class LruMap final { +public: + LruMap() = default; + ~LruMap() = default; + + DISABLE_COPY_ASSIGN_MOVE(LruMap); + + int Get(const std::string &key, QueryWaterMark &outValue); + int Put(const std::string &key, const QueryWaterMark &inValue); + void RemoveWithPrefixKey(const std::string &prefixKey); +private: + int Elimination(const std::string &key, const QueryWaterMark &inQueryWaterMark); + + std::mutex lruLock_; + std::map cache_; + std::deque> eliminationChain_; +}; + +class QuerySyncWaterMarkHelper { +public: + QuerySyncWaterMarkHelper(); + ~QuerySyncWaterMarkHelper(); + + DISABLE_COPY_ASSIGN_MOVE(QuerySyncWaterMarkHelper); + + int Initialize(ISyncInterface *storage); + + int GetQueryWaterMark(const std::string &queryIdentify, const std::string &deviceId, + QueryWaterMark &queryWaterMark); + + int SetSendQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, const WaterMark &waterMark); + + int SetRecvQueryWaterMark(const std::string &queryIdentify, + const std::string &deviceId, const WaterMark &waterMark); + + int SetLastQueryTime(const std::string &queryIdentify, + const std::string &deviceId, const Timestamp ×tamp); + + int GetDeleteSyncWaterMark(const std::string &deviceId, DeleteWaterMark &deleteWaterMark); + + int SetSendDeleteSyncWaterMark(const std::string &deviceId, const WaterMark &waterMark); + + int SetRecvDeleteSyncWaterMark(const std::string &deviceId, const WaterMark &waterMark); + + // this function will read deleteWaterMark from db by it's deleteWaterMarkKey + // and then serialize it and put to cache + int LoadDeleteSyncDataToCache(const Key &deleteWaterMarkKey); + + // this function will remove data in db + int RemoveLeastUsedQuerySyncItems(const std::vector &querySyncIds); + + // reset the waterMark to zero + int ResetRecvQueryWaterMark(const DeviceID &deviceId, const std::string &tableName); + + static std::string GetQuerySyncPrefixKey(); + + static std::string GetDeleteSyncPrefixKey(); + +private: + + int GetMetadataFromDb(const std::vector &key, std::vector &outValue); + + int SetMetadataToDb(const std::vector &key, const std::vector &inValue); + + int DeleteMetaDataFromDB(const std::vector &keys) const; + + int SaveQueryWaterMarkToDB(const DeviceID &dbKeyString, const QueryWaterMark &queryWaterMark); + + int GetQueryWaterMarkFromDB(const DeviceID &dbKeyString, QueryWaterMark &queryWaterMark); + + int SetRecvQueryWaterMarkWithoutLock(const std::string &cacheKey, + const WaterMark &waterMark); + + // search the queryWaterMark from db or cache_ + // and ensure it exit in cache_ + int GetQueryWaterMarkInCacheAndDb(const std::string &cacheKey, QueryWaterMark &queryWaterMark); + + // only first create queryWaterMark will call this function + // it will create a queryWaterMark and save to db + int PutQueryWaterMarkToDB(const DeviceID &dbKeyString, QueryWaterMark &queryWaterMark); + + // get the querySync hashId in cache_ or generate one and then put it in to cache_ + // the hashId is made up of "QUERY_SYNC_PREFIX_KEY" + hash(deviceId) + queryId + void GetHashQuerySyncDeviceId(const DeviceID &deviceId, + const DeviceID &queryId, DeviceID &hashQuerySyncId); + + // put queryWaterMark to lru cache_ and then save to db + int UpdateCacheAndSave(const std::string &cacheKey, QueryWaterMark &queryWaterMark); + + // search the deleteWaterMark from db or cache_ + // and ensure it exit in cache_ + int GetDeleteWaterMarkFromCache(const DeviceID &hashDeviceId, DeleteWaterMark &deleteWaterMark); + + // get the deleteSync hashId in cache_ or generate one and then put it in to cache_ + // the hashId is made up of "DELETE_SYNC_PREFIX_KEY" + hash(deviceId) + void GetHashDeleteSyncDeviceId(const DeviceID &deviceId, DeviceID &hashDeleteSyncId); + + int SaveDeleteWaterMarkToDB(const DeviceID &hashDeviceId, const DeleteWaterMark &deleteWaterMark); + + int GetDeleteWaterMarkFromDB(const DeviceID &hashDeviceId, DeleteWaterMark &deleteWaterMark); + + // put queryWaterMark to lru cache_ and then save to db + int UpdateDeleteSyncCacheAndSave(const std::string &dbKey, const DeleteWaterMark &deleteWaterMark); + + static int SerializeQueryWaterMark(const QueryWaterMark &queryWaterMark, std::vector &outValue); + + static int DeSerializeQueryWaterMark(const std::vector &dbQueryWaterMark, QueryWaterMark &queryWaterMark); + + static uint64_t CalculateQueryWaterMarkSize(const QueryWaterMark &queryWaterMark); + + static int SerializeDeleteWaterMark(const DeleteWaterMark &deleteWaterMark, std::vector &outValue); + + static int DeSerializeDeleteWaterMark(const std::vector &inValue, DeleteWaterMark &deleteWaterMark); + + static uint64_t CalculateDeleteWaterMarkSize(); + + // store or visit queryWaterMark should add a lock + // because it will change the eliminationChain + // and the queryWaterMark use a LRU Map to store in ram + std::mutex queryWaterMarkLock_; + LruMap querySyncCache_; + std::map> deviceIdToHashQuerySyncIdMap_; + + // also store deleteKeyWaterMark should add a lock + std::mutex deleteSyncLock_; + std::map deleteSyncCache_; + std::map deviceIdToHashDeleteSyncIdMap_; + + ISyncInterface *storage_; +}; +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/syncer/src/single_ver_data_message_schedule.cpp b/mock/distributeddb/syncer/src/single_ver_data_message_schedule.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b958ee8f3c60c2af3ab00a74412a1166d274d7f0 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_data_message_schedule.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "single_ver_data_message_schedule.h" + +#include "db_common.h" +#include "log_print.h" +#include "version.h" +#include "single_ver_data_sync.h" + +namespace DistributedDB { +SingleVerDataMessageSchedule::~SingleVerDataMessageSchedule() +{ + LOGD("~SingleVerDataMessageSchedule"); + ClearMsg(); +} + +void SingleVerDataMessageSchedule::Initialize(const std::string &label, const std::string &deviceId) +{ + label_ = label; + deviceId_ = deviceId; +} + +void SingleVerDataMessageSchedule::PutMsg(Message *inMsg) +{ + if (inMsg == nullptr) { + return; + } + std::lock_guard lock(queueLock_); + msgQueue_.push(inMsg); + isNeedReload_ = true; +} + +bool SingleVerDataMessageSchedule::IsNeedReloadQueue() +{ + std::lock_guard lock(queueLock_); + return isNeedReload_; +} + +Message *SingleVerDataMessageSchedule::MoveNextMsg(SingleVerSyncTaskContext *context, bool &isNeedHandle, + bool &isNeedContinue) +{ + uint32_t remoteVersion = context->GetRemoteSoftwareVersion(); + if (remoteVersion < SOFTWARE_VERSION_RELEASE_3_0) { + // just get last msg when remote version is < 103 or >=103 but just open db now + return GetLastMsgFromQueue(); + } + { + std::lock_guard lock(workingLock_); + if (isWorking_) { + isNeedContinue = false; + return nullptr; + } + isWorking_ = true; + } + ResetTimer(context); + UpdateMsgMap(); + Message *msg = GetMsgFromMap(isNeedHandle); + isNeedContinue = true; + if (msg == nullptr) { + StopTimer(); + std::lock_guard lock(workingLock_); + isWorking_ = false; + return nullptr; + } + return msg; +} + +void SingleVerDataMessageSchedule::ScheduleInfoHandle(bool isNeedHandleStatus, bool isNeedClearMap, + const Message *inMsg) +{ + if (isNeedHandleStatus) { + const DataRequestPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + LOGE("[DataMsgSchedule] packet is nullptr"); + return; + } + uint64_t curPacketId = packet->GetPacketId(); + { + std::lock_guard lock(lock_); + finishedPacketId_ = curPacketId; + if (isNeedClearMap) { + ClearMsgMapWithNoLock(); + expectedSequenceId_ = 1; + } else { + LOGI("[DataMsgSchedule] DealMsg seqId=%" PRIu32 " finishedPacketId=%" PRIu64 " ok,label=%s,dev=%s", + expectedSequenceId_, finishedPacketId_, label_.c_str(), STR_MASK(deviceId_)); + expectedSequenceId_++; + } + } + } + std::lock_guard lock(workingLock_); + isWorking_ = false; +} + +void SingleVerDataMessageSchedule::ClearMsg() +{ + StopTimer(); + ClearMsgQueue(); + ClearMsgMap(); +} + +void SingleVerDataMessageSchedule::UpdateMsgMap() +{ + std::queue msgTmpQueue; + { + std::lock_guard lock(queueLock_); + while (!msgQueue_.empty()) { + msgTmpQueue.push(msgQueue_.front()); + msgQueue_.pop(); + } + isNeedReload_ = false; + } + UpdateMsgMapInner(msgTmpQueue); +} + +void SingleVerDataMessageSchedule::UpdateMsgMapInner(std::queue &msgTmpQueue) +{ + // update msg map + std::lock_guard lock(lock_); + while (!msgTmpQueue.empty()) { + Message *msg = msgTmpQueue.front(); + msgTmpQueue.pop(); + // insert new msg into map and delete old msg + int errCode = UpdateMsgMapIfNeed(msg); + if (errCode != E_OK) { + delete msg; + } + } +} + +Message *SingleVerDataMessageSchedule::GetMsgFromMap(bool &isNeedHandle) +{ + isNeedHandle = true; + std::lock_guard lock(lock_); + while (!messageMap_.empty()) { + auto iter = messageMap_.begin(); + Message *msg = iter->second; + messageMap_.erase(iter); + const DataRequestPacket *packet = msg->GetObject(); + if (packet == nullptr) { + LOGE("[DataMsgSchedule] expected error"); + delete msg; + continue; + } + uint32_t sequenceId = msg->GetSequenceId(); + uint64_t packetId = packet->GetPacketId(); + if (sequenceId < expectedSequenceId_) { + uint64_t revisePacketId = finishedPacketId_ - (expectedSequenceId_ - 1 - sequenceId); + LOGI("[DataMsgSchedule] drop msg because seqId less than exSeqId"); + if (packetId < revisePacketId) { + delete msg; + continue; + } + // means already handle the msg, and just send E_OK ack in dataSync + isNeedHandle = false; + return msg; + } + if (sequenceId == expectedSequenceId_) { + if (packetId < finishedPacketId_) { + LOGI("[DataMsgSchedule] drop msg because packetId less than finishedPacketId"); + delete msg; + continue; + } + // if packetId == finishedPacketId_ need handle + // it will happened while watermark/need_abilitySync when last ack is missing + return msg; + } + // sequenceId > expectedSequenceId_, not need handle, put into map again + messageMap_[sequenceId] = msg; + return nullptr; + } + return nullptr; +} + +Message *SingleVerDataMessageSchedule::GetLastMsgFromQueue() +{ + std::lock_guard lock(queueLock_); + isNeedReload_ = false; + while (!msgQueue_.empty()) { + Message *msg = msgQueue_.front(); + msgQueue_.pop(); + if (msgQueue_.empty()) { // means last msg + return msg; + } + delete msg; + } + return nullptr; +} + +void SingleVerDataMessageSchedule::ClearMsgMap() +{ + std::lock_guard lock(lock_); + ClearMsgMapWithNoLock(); +} + +void SingleVerDataMessageSchedule::ClearMsgMapWithNoLock() +{ + LOGD("[DataMsgSchedule] begin to ClearMsgMapWithNoLock"); + for (auto &iter : messageMap_) { + delete iter.second; + iter.second = nullptr; + } + messageMap_.clear(); +} + +void SingleVerDataMessageSchedule::ClearMsgQueue() +{ + std::lock_guard lock(queueLock_); + while (!msgQueue_.empty()) { + Message *msg = msgQueue_.front(); + msgQueue_.pop(); + delete msg; + } +} + +void SingleVerDataMessageSchedule::StartTimer(SingleVerSyncTaskContext *context) +{ + std::lock_guard lock(lock_); + TimerId timerId = 0; + RefObject::IncObjRef(context); + TimerAction timeOutCallback = std::bind(&SingleVerDataMessageSchedule::TimeOut, this, std::placeholders::_1); + int errCode = RuntimeContext::GetInstance()->SetTimer(IDLE_TIME_OUT, timeOutCallback, + [context]() { + int errCode = RuntimeContext::GetInstance()->ScheduleTask([context]() { + RefObject::DecObjRef(context); + }); + if (errCode != E_OK) { + LOGE("[DataMsgSchedule] timer finalizer ScheduleTask,errCode=%d", errCode); + } + }, timerId); + if (errCode != E_OK) { + RefObject::DecObjRef(context); + LOGE("[DataMsgSchedule] timer ScheduleTask, errCode=%d", errCode); + return; + } + timerId_ = timerId; + LOGD("[DataMsgSchedule] StartTimer,TimerId=%" PRIu64, timerId_); +} + +void SingleVerDataMessageSchedule::StopTimer() +{ + TimerId timerId; + { + std::lock_guard lock(lock_); + LOGD("[DataMsgSchedule] StopTimer,remove TimerId=%" PRIu64, timerId_); + if (timerId_ == 0) { + return; + } + timerId = timerId_; + timerId_ = 0; + } + RuntimeContext::GetInstance()->RemoveTimer(timerId); +} + +void SingleVerDataMessageSchedule::ResetTimer(SingleVerSyncTaskContext *context) +{ + StopTimer(); + StartTimer(context); +} + +int SingleVerDataMessageSchedule::TimeOut(TimerId timerId) +{ + if (IsNeedReloadQueue()) { + LOGI("[DataMsgSchedule] new msg exists, no need to timeout handle"); + return E_OK; + } + { + std::lock_guard lock(workingLock_); + if (isWorking_) { + LOGI("[DataMsgSchedule] other thread is handle msg, no need to timeout handle"); + return E_OK; + } + } + { + std::lock_guard lock(lock_); + LOGI("[DataMsgSchedule] timeout handling, stop timerId_[%" PRIu64 "]", timerId); + if (timerId == timerId_) { + ClearMsgMapWithNoLock(); + timerId_ = 0; + } + } + RuntimeContext::GetInstance()->RemoveTimer(timerId); + return E_OK; +} + +int SingleVerDataMessageSchedule::UpdateMsgMapIfNeed(Message *msg) +{ + if (msg == nullptr) { + return -E_INVALID_ARGS; + } + const DataRequestPacket *packet = msg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + uint32_t sessionId = msg->GetSessionId(); + uint32_t sequenceId = msg->GetSequenceId(); + uint64_t packetId = packet->GetPacketId(); + if (prevSessionId_ != 0 && sessionId == prevSessionId_) { + LOGD("[DataMsgSchedule] recv prev sessionId msg, drop msg, label=%s, dev=%s", label_.c_str(), + STR_MASK(deviceId_)); + return -E_INVALID_ARGS; + } + if (sessionId != currentSessionId_) { + // make sure all msg sessionId is same in msgMap + ClearMsgMapWithNoLock(); + prevSessionId_ = currentSessionId_; + currentSessionId_ = sessionId; + finishedPacketId_ = 0; + expectedSequenceId_ = 1; + } + if (messageMap_.count(sequenceId) > 0) { + const auto *cachePacket = messageMap_[sequenceId]->GetObject(); + if (cachePacket != nullptr) { + if (packetId != 0 && packetId < cachePacket->GetPacketId()) { + LOGD("[DataMsgSchedule] drop msg packetId=%" PRIu64 ", cachePacketId=%" PRIu64 ", label=%s, dev=%s", + packetId, cachePacket->GetPacketId(), label_.c_str(), STR_MASK(deviceId_)); + return -E_INVALID_ARGS; + } + } + delete messageMap_[sequenceId]; + messageMap_[sequenceId] = nullptr; + } + messageMap_[sequenceId] = msg; + LOGD("[DataMsgSchedule] put into msgMap seqId=%" PRIu32 ", packetId=%" PRIu64 ", label=%s, dev=%s", sequenceId, + packetId, label_.c_str(), STR_MASK(deviceId_)); + return E_OK; +} +} \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_data_message_schedule.h b/mock/distributeddb/syncer/src/single_ver_data_message_schedule.h new file mode 100644 index 0000000000000000000000000000000000000000..bc9a0937e6bababaf3a06835e3459d5ae47d99bb --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_data_message_schedule.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_DATA_MESSAGE_SCHEDULE_H +#define SINGLE_VER_DATA_MESSAGE_SCHEDULE_H +#include +#include +#include +#include + +#include "message.h" +#include "runtime_context.h" +#include "single_ver_sync_task_context.h" + +namespace DistributedDB { +class SingleVerDataMessageSchedule { +public: + SingleVerDataMessageSchedule() = default; + ~SingleVerDataMessageSchedule(); + void Initialize(const std::string &label, const std::string &deviceId); + void PutMsg(Message *inMsg); + bool IsNeedReloadQueue(); + Message *MoveNextMsg(SingleVerSyncTaskContext *context, bool &isNeedHandle, bool &isNeedContinue); + void ScheduleInfoHandle(bool isNeedHandleStatus, bool isNeedClearMap, const Message *inMsg); + void ClearMsg(); +private: + void UpdateMsgMap(); + void UpdateMsgMapInner(std::queue &msgTmpQueue); + int UpdateMsgMapIfNeed(Message *msg); + Message *GetMsgFromMap(bool &isNeedHandle); + Message *GetLastMsgFromQueue(); + void ClearMsgMap(); + void ClearMsgMapWithNoLock(); + void ClearMsgQueue(); + void StartTimer(SingleVerSyncTaskContext *context); + void StopTimer(); + void ResetTimer(SingleVerSyncTaskContext *context); + // when timeout queue size is 0 because thread can move queue msg to map if isNeedReload which is + // activated when queue has new msg is true + // so only need clear map msg + int TimeOut(TimerId timerId); + + static constexpr int IDLE_TIME_OUT = 5 * 60 * 1000; // 5min + std::mutex queueLock_; + std::queue msgQueue_; + bool isNeedReload_ = false; + // only one thread is deal msg + std::mutex workingLock_; + bool isWorking_ = false; + // first:sequenceId second:Message*, deal msg from low sequenceId to high sequenceId + std::mutex lock_; + std::map messageMap_; + uint32_t prevSessionId_ = 0; // drop the msg if msg sessionId is prev sessionId. + uint32_t currentSessionId_ = 0; + uint64_t finishedPacketId_ = 0; // next msg packetId should larger than it + uint32_t expectedSequenceId_ = 0; // handle next msg which sequenceId is equal to it + TimerId timerId_ = 0; + + std::string label_; + std::string deviceId_; +}; +} +#endif // SINGLE_VER_DATA_MESSAGE_SCHEDULE_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_data_packet.cpp b/mock/distributeddb/syncer/src/single_ver_data_packet.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea0733888e7602d43c9479b29abd0eb6efb80457 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_data_packet.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_data_packet.h" +#include "icommunicator.h" +#include "single_ver_kvdb_sync_interface.h" +#include "query_sync_object.h" +#include "generic_single_ver_kv_entry.h" +#include "sync_types.h" +#include "version.h" +#include "parcel.h" + + +namespace DistributedDB { +DataRequestPacket::~DataRequestPacket() +{ + for (auto &entry : data_) { + delete entry; + entry = nullptr; + } +} + +void DataRequestPacket::SetData(std::vector &data) +{ + data_ = std::move(data); +} + +const std::vector &DataRequestPacket::GetData() const +{ + return data_; +} + +void DataRequestPacket::SetCompressData(std::vector &compressData) +{ + compressData_ = std::move(compressData); +} + +const std::vector &DataRequestPacket::GetCompressData() const +{ + return compressData_; +} + +void DataRequestPacket::SetEndWaterMark(WaterMark waterMark) +{ + endWaterMark_ = waterMark; +} + +WaterMark DataRequestPacket::GetEndWaterMark() const +{ + return endWaterMark_; +} + +void DataRequestPacket::SetLocalWaterMark(WaterMark waterMark) +{ + localWaterMark_ = waterMark; +} + +WaterMark DataRequestPacket::GetLocalWaterMark() const +{ + return localWaterMark_; +} + +void DataRequestPacket::SetPeerWaterMark(WaterMark waterMark) +{ + peerWaterMark_ = waterMark; +} + +WaterMark DataRequestPacket::GetPeerWaterMark() const +{ + return peerWaterMark_; +} + +void DataRequestPacket::SetSendCode(int32_t errCode) +{ + sendCode_ = errCode; +} + +int32_t DataRequestPacket::GetSendCode() const +{ + return sendCode_; +} + +void DataRequestPacket::SetMode(int32_t mode) +{ + mode_ = mode; +} + +int32_t DataRequestPacket::GetMode() const +{ + return mode_; +} + +void DataRequestPacket::SetSessionId(uint32_t sessionId) +{ + sessionId_ = sessionId; +} + +uint32_t DataRequestPacket::GetSessionId() const +{ + return sessionId_; +} + +void DataRequestPacket::SetVersion(uint32_t version) +{ + version_ = version; +} + +uint32_t DataRequestPacket::GetVersion() const +{ + return version_; +} + +void DataRequestPacket::SetReserved(std::vector &reserved) +{ + reserved_ = std::move(reserved); +} + +void DataRequestPacket::SetReserved(std::vector &&reserved) +{ + reserved_ = reserved; +} + +std::vector DataRequestPacket::GetReserved() const +{ + return reserved_; +} + +uint64_t DataRequestPacket::GetPacketId() const +{ + uint64_t packetId = 0; + std::vector DataRequestReserve = GetReserved(); + if (DataRequestReserve.size() > REQUEST_PACKET_RESERVED_INDEX_PACKETID) { + return DataRequestReserve[REQUEST_PACKET_RESERVED_INDEX_PACKETID]; + } else { + return packetId; + } +} + +uint32_t DataRequestPacket::CalculateLen(uint32_t messageId) const +{ + uint64_t totalLen = GenericSingleVerKvEntry::CalculateLens( + IsCompressData() ? std::vector {} : data_, version_); // for data + totalLen += Parcel::GetUInt64Len(); // endWaterMark + totalLen += Parcel::GetUInt64Len(); // localWaterMark + totalLen += Parcel::GetUInt64Len(); // peerWaterMark + totalLen += Parcel::GetIntLen(); // sendCode + totalLen += Parcel::GetIntLen(); // mode + totalLen += Parcel::GetUInt32Len(); // sessionId + totalLen += Parcel::GetUInt32Len(); // version + totalLen += Parcel::GetVectorLen(reserved_); // reserved + + if (version_ > SOFTWARE_VERSION_RELEASE_2_0) { + totalLen += Parcel::GetUInt32Len(); // flag bit0 used for isLastSequence + } + totalLen = Parcel::GetEightByteAlign(totalLen); // 8-byte align + if (totalLen > INT32_MAX) { + return 0; + } + if (messageId == QUERY_SYNC_MESSAGE) { + // deleted watermark + totalLen += Parcel::GetUInt64Len(); + // query id + totalLen += Parcel::GetStringLen(queryId_); + // add for queryObject + totalLen += query_.CalculateParcelLen(SOFTWARE_VERSION_CURRENT); + } + if (IsCompressData()) { + totalLen += GenericSingleVerKvEntry::CalculateCompressedLens(compressData_); // add forcompressData_ + } + if (totalLen > INT32_MAX) { + return 0; + } + return totalLen; +} + +void DataRequestPacket::SetFlag(uint32_t flag) +{ + flag_ = flag; +} + +uint32_t DataRequestPacket::GetFlag() const +{ + return flag_; +} + +bool DataRequestPacket::IsLastSequence() const +{ + return ((flag_ & IS_LAST_SEQUENCE) == IS_LAST_SEQUENCE); +} + +void DataRequestPacket::SetLastSequence() +{ + flag_ = flag_ | IS_LAST_SEQUENCE; +} + +bool DataRequestPacket::IsNeedUpdateWaterMark() const +{ + return !((flag_ & IS_UPDATE_WATER) == IS_UPDATE_WATER); +} + +void DataRequestPacket::SetUpdateWaterMark() +{ + flag_ = flag_ | IS_UPDATE_WATER; +} + +void DataRequestPacket::SetCompressDataMark() +{ + flag_ = flag_ | IS_COMPRESS_DATA; +} + +bool DataRequestPacket::IsCompressData() const +{ + return ((flag_ & IS_COMPRESS_DATA) == IS_COMPRESS_DATA); +} + +void DataRequestPacket::SetCompressAlgo(CompressAlgorithm algo) +{ + algo_ = algo; +} + +CompressAlgorithm DataRequestPacket::GetCompressAlgo() const +{ + return algo_; +} + +void DataRequestPacket::SetBasicInfo(int sendCode, uint32_t version, int32_t mode) +{ + SetSendCode(sendCode); + SetVersion(version); + SetMode(mode); +} + +void DataRequestPacket::SetWaterMark(WaterMark localMark, WaterMark peerMark, WaterMark deletedWatermark) +{ + localWaterMark_ = localMark; + peerWaterMark_ = peerMark; + deletedWatermark_ = deletedWatermark; +} + +void DataRequestPacket::SetQuery(const QuerySyncObject &query) +{ + query_ = query; +} + +QuerySyncObject DataRequestPacket::GetQuery() const +{ + return query_; +} + +void DataRequestPacket::SetQueryId(const std::string &queryId) +{ + queryId_ = queryId; +} + +std::string DataRequestPacket::GetQueryId() const +{ + return queryId_; +} + +void DataRequestPacket::SetDeletedWaterMark(WaterMark watermark) +{ + deletedWatermark_ = watermark; +} + +WaterMark DataRequestPacket::GetDeletedWaterMark() const +{ + return deletedWatermark_; +} + +void DataAckPacket::SetData(uint64_t data) +{ + data_ = data; +} + +uint64_t DataAckPacket::GetData() const +{ + return data_; +} + +void DataAckPacket::SetRecvCode(int32_t errorCode) +{ + recvCode_ = errorCode; +} + +int32_t DataAckPacket::GetRecvCode() const +{ + return recvCode_; +} + +void DataAckPacket::SetVersion(uint32_t version) +{ + version_ = version; +} + +uint32_t DataAckPacket::GetVersion() const +{ + return version_; +} + +void DataAckPacket::SetReserved(std::vector &reserved) +{ + reserved_ = std::move(reserved); +} + +std::vector DataAckPacket::GetReserved() const +{ + return reserved_; +} + +uint64_t DataAckPacket::GetPacketId() const +{ + uint64_t packetId = 0; + std::vector DataAckReserve = GetReserved(); + if (DataAckReserve.size() > ACK_PACKET_RESERVED_INDEX_PACKETID) { + packetId = DataAckReserve[ACK_PACKET_RESERVED_INDEX_PACKETID]; + } + // while remote db is close and open again, it may not carry packetId + // so the second index is deletewatermark if it is the query Sync, should drop the deletewatermark here + if (packetId > MAX_PACKETID) { + return 0; + } + return packetId; +} + +bool DataAckPacket::IsPacketIdValid(uint64_t packetId) +{ + return (packetId > 0); +} + +uint32_t DataAckPacket::CalculateLen() const +{ + uint64_t len = Parcel::GetUInt64Len(); // ackWaterMark + len += Parcel::GetIntLen(); // recvCode + len += Parcel::GetUInt32Len(); // version + len += Parcel::GetVectorLen(reserved_); // reserved + + len = Parcel::GetEightByteAlign(len); + if (len > INT32_MAX) { + return 0; + } + return len; +} + +void ControlRequestPacket::SetPacketHead(int sendCode, uint32_t version, int32_t controlCmd, uint32_t flag) +{ + sendCode_ = sendCode; + version_ = version; + controlCmdType_ = static_cast(controlCmd); + flag_ = flag; +} + +int32_t ControlRequestPacket::GetSendCode() const +{ + return sendCode_; +} + +uint32_t ControlRequestPacket::GetVersion() const +{ + return version_; +} + +uint32_t ControlRequestPacket::GetcontrolCmdType() const +{ + return controlCmdType_; +} + +uint32_t ControlRequestPacket::GetFlag() const +{ + return flag_; +} + +void ControlRequestPacket::SetQuery(const QuerySyncObject &query) +{ + (void)query; +} + +uint32_t ControlRequestPacket::CalculateLen() const +{ + uint64_t len = Parcel::GetUInt32Len(); // version_ + len += Parcel::GetIntLen(); // sendCode_ + len += Parcel::GetUInt32Len(); // controlCmdType_ + len += Parcel::GetUInt32Len(); // flag + + len = Parcel::GetEightByteAlign(len); + if (len > INT32_MAX) { + return 0; + } + return len; +} + +void SubscribeRequest::SetQuery(const QuerySyncObject &query) +{ + query_ = query; +} + +QuerySyncObject SubscribeRequest::GetQuery() const +{ + return query_; +} + +uint32_t SubscribeRequest::CalculateLen() const +{ + uint64_t totalLen = ControlRequestPacket::CalculateLen(); + if (totalLen == 0) { + LOGE("[SubscribeRequest] cal packet len failed"); + return 0; + } + // add for queryObject + totalLen += query_.CalculateParcelLen(SOFTWARE_VERSION_CURRENT); + if (totalLen > INT32_MAX) { + return 0; + } + return totalLen; +} + +bool SubscribeRequest::IsAutoSubscribe() const +{ + return ((GetFlag() & IS_AUTO_SUBSCRIBE) == IS_AUTO_SUBSCRIBE); +} + +void ControlAckPacket::SetPacketHead(int recvCode, uint32_t version, int32_t controlCmd, uint32_t flag) +{ + recvCode_ = recvCode; + version_ = version; + controlCmdType_ = static_cast(controlCmd); + flag_ = flag; +} + +int32_t ControlAckPacket::GetRecvCode() const +{ + return recvCode_; +} + +uint32_t ControlAckPacket::GetVersion() const +{ + return version_; +} + +uint32_t ControlAckPacket::GetcontrolCmdType() const +{ + return controlCmdType_; +} + +uint32_t ControlAckPacket::GetFlag() const +{ + return flag_; +} + +uint32_t ControlAckPacket::CalculateLen() const +{ + uint64_t len = Parcel::GetUInt32Len(); // version_ + len += Parcel::GetIntLen(); // recvCode_ + len += Parcel::GetUInt32Len(); // controlCmdType_ + len += Parcel::GetUInt32Len(); // flag + len = Parcel::GetEightByteAlign(len); + if (len > INT32_MAX) { + return 0; + } + return len; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/single_ver_data_packet.h b/mock/distributeddb/syncer/src/single_ver_data_packet.h new file mode 100644 index 0000000000000000000000000000000000000000..7731a71a45597371eaebd3596aa53485390f14c4 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_data_packet.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_DATA_PACKET_NEW_H +#define SINGLE_VER_DATA_PACKET_NEW_H + +#include "icommunicator.h" +#include "parcel.h" +#include "query_sync_object.h" +#include "single_ver_kvdb_sync_interface.h" +#include "sync_types.h" +#include "version.h" + +namespace DistributedDB { +using SendDataItem = SingleVerKvEntry *; + +class DataRequestPacket { +public: + DataRequestPacket() {}; + virtual ~DataRequestPacket(); + + void SetData(std::vector &data); + + const std::vector &GetData() const; + const std::vector &GetCompressedData() const; + + void SetCompressData(std::vector &compressData); + + const std::vector &GetCompressData() const; + + void SetEndWaterMark(WaterMark waterMark); + + WaterMark GetEndWaterMark() const; + + void SetLocalWaterMark(WaterMark waterMark); + + WaterMark GetLocalWaterMark() const; + + void SetPeerWaterMark(WaterMark waterMark); + + WaterMark GetPeerWaterMark() const; + + void SetSendCode(int32_t errCode); + + int32_t GetSendCode() const; + + void SetMode(int32_t mode); + + int32_t GetMode() const; + + void SetSessionId(uint32_t sessionId); + + uint32_t GetSessionId() const; + + void SetVersion(uint32_t version); + + uint32_t GetVersion() const; + + uint32_t CalculateLen(uint32_t messageId) const; + + void SetReserved(std::vector &reserved); + void SetReserved(std::vector &&reserved); + + std::vector GetReserved() const; + + uint64_t GetPacketId() const; + + void SetFlag(uint32_t flag); + + uint32_t GetFlag() const; + + bool IsLastSequence() const; + + void SetLastSequence(); + + bool IsNeedUpdateWaterMark() const; + + void SetUpdateWaterMark(); + + void SetBasicInfo(int sendCode, uint32_t version, int32_t mode); + + void SetWaterMark(WaterMark localMark, WaterMark peerMark, WaterMark deletedWatermark); + + void SetQuery(const QuerySyncObject &query); + QuerySyncObject GetQuery() const; + + void SetQueryId(const std::string &queryId); + std::string GetQueryId() const; + + void SetDeletedWaterMark(WaterMark watermark); + WaterMark GetDeletedWaterMark() const; + + void SetCompressDataMark(); + bool IsCompressData() const; + + void SetCompressAlgo(CompressAlgorithm algo); + CompressAlgorithm GetCompressAlgo() const; + +protected: + std::vector data_; + WaterMark endWaterMark_ = 0; + WaterMark localWaterMark_ = 0; + WaterMark peerWaterMark_ = 0; + int32_t sendCode_ = 0; + int32_t mode_ = SyncModeType::INVALID_MODE; + uint32_t sessionId_ = 0; + uint32_t version_ = SOFTWARE_VERSION_CURRENT; + std::vector reserved_; + uint32_t flag_ = 0; // bit 0 used for isLastSequence + // add for query sync mode + QuerySyncObject query_; + std::string queryId_; + WaterMark deletedWatermark_ = 0; + std::vector compressData_; // if compressData size is above 0, means use compressData and ignore data_ + CompressAlgorithm algo_ = CompressAlgorithm::NONE; // used for param while serialize compress data + static const uint32_t IS_LAST_SEQUENCE = 0x1; // bit 0 used for isLastSequence, 1: is last, 0: not last + static const uint32_t IS_UPDATE_WATER = 0x2; // bit 1 used for update watermark, 0: update, 1: not update + static const uint32_t IS_COMPRESS_DATA = 0x4; // bit 3 used for compress data, 0: raw data, 1: compress data +}; + +class DataAckPacket { +public: + DataAckPacket() {}; + virtual ~DataAckPacket() {}; + + void SetData(uint64_t data); + + uint64_t GetData() const; + + void SetRecvCode(int32_t errorCode); + + int32_t GetRecvCode() const; + + void SetVersion(uint32_t version); + + uint32_t GetVersion() const; + + void SetReserved(std::vector &reserved); + + std::vector GetReserved() const; + + uint64_t GetPacketId() const; + + static bool IsPacketIdValid(uint64_t packetId); + + uint32_t CalculateLen() const; + +private: + /* + * data_ is waterMark when revCode_ == LOCAL_WATER_MARK_NOT_INIT || revCode_ == E_OK; + * data_ is timer in milliSeconds when revCode_ == -E_SAVE_DATA_NOTIFY && data_ != 0. + */ + uint64_t data_ = 0; + int32_t recvCode_ = 0; + uint32_t version_ = SOFTWARE_VERSION_CURRENT; + std::vector reserved_; +}; + +class ControlRequestPacket { +public: + ControlRequestPacket() {}; + virtual ~ControlRequestPacket() {}; + void SetPacketHead(int sendCode, uint32_t version, int32_t controlCmd, uint32_t flag); + + int32_t GetSendCode() const; + uint32_t GetVersion() const; + uint32_t GetcontrolCmdType() const; + uint32_t GetFlag() const; + virtual void SetQuery(const QuerySyncObject &query); + virtual uint32_t CalculateLen() const; +private: + uint32_t version_ = SOFTWARE_VERSION_CURRENT; + int32_t sendCode_ = 0; + uint32_t controlCmdType_ = 0; + uint32_t flag_ = 0; +}; + +class SubscribeRequest : public ControlRequestPacket { +public: + SubscribeRequest() {}; + ~SubscribeRequest() override {}; + QuerySyncObject GetQuery() const; + bool IsAutoSubscribe() const; + void SetQuery(const QuerySyncObject &query) override; + uint32_t CalculateLen() const override; + static const uint32_t IS_AUTO_SUBSCRIBE = 0x1; +private: + QuerySyncObject query_; +}; + +class ControlAckPacket { +public: + ControlAckPacket() {}; + virtual ~ControlAckPacket() {}; + void SetPacketHead(int recvCode, uint32_t version, int32_t controlCmd, uint32_t flag); + int32_t GetRecvCode() const; + uint32_t GetVersion() const; + uint32_t GetcontrolCmdType() const; + uint32_t GetFlag() const; + uint32_t CalculateLen() const; + +private: + uint32_t version_ = SOFTWARE_VERSION_CURRENT; + int32_t recvCode_ = 0; + uint32_t controlCmdType_ = 0; + uint32_t flag_ = 0; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_DATA_SYNC_NEW_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_data_sync.cpp b/mock/distributeddb/syncer/src/single_ver_data_sync.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8796f514405d9a7d4bc5d1838709c9618ffc6d1a --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_data_sync.cpp @@ -0,0 +1,2031 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_data_sync.h" + +#include "db_common.h" +#include "db_types.h" +#include "generic_single_ver_kv_entry.h" +#include "intercepted_data_impl.h" +#include "log_print.h" +#include "message_transform.h" +#include "performance_analysis.h" +#include "single_ver_data_sync_utils.h" +#include "single_ver_sync_state_machine.h" +#include "subscribe_manager.h" +#ifdef RELATIONAL_STORE +#include "relational_db_sync_interface.h" +#endif + +namespace DistributedDB { +SingleVerDataSync::SingleVerDataSync() + : mtuSize_(0), + storage_(nullptr), + communicateHandle_(nullptr), + metadata_(nullptr) +{ +} + +SingleVerDataSync::~SingleVerDataSync() +{ + storage_ = nullptr; + communicateHandle_ = nullptr; + metadata_ = nullptr; +} + +int SingleVerDataSync::Initialize(ISyncInterface *inStorage, ICommunicator *inCommunicateHandle, + std::shared_ptr &inMetadata, const std::string &deviceId) +{ + if ((inStorage == nullptr) || (inCommunicateHandle == nullptr) || (inMetadata == nullptr)) { + return -E_INVALID_ARGS; + } + storage_ = static_cast(inStorage); + communicateHandle_ = inCommunicateHandle; + metadata_ = inMetadata; + mtuSize_ = DBConstant::MIN_MTU_SIZE; // default size is 1K, it will update when need sync data. + std::vector label = inStorage->GetIdentifier(); + label.resize(3); // only show 3 Bytes enough + label_ = DBCommon::VectorToHexString(label); + deviceId_ = deviceId; + msgSchedule_.Initialize(label_, deviceId_); + return E_OK; +} + +int SingleVerDataSync::SyncStart(int mode, SingleVerSyncTaskContext *context) +{ + std::lock_guard lock(lock_); + if (sessionId_ != 0) { // auto sync timeout resend + return ReSendData(context); + } + ResetSyncStatus(mode, context); + LOGI("[DataSync] SendStart,mode=%d,label=%s,device=%s", mode_, label_.c_str(), STR_MASK(deviceId_)); + int tmpMode = SyncOperation::TransferSyncMode(mode); + int errCode = E_OK; + if (tmpMode == SyncModeType::PUSH) { + errCode = PushStart(context); + } else if (tmpMode == SyncModeType::PUSH_AND_PULL) { + errCode = PushPullStart(context); + } else if (tmpMode == SyncModeType::PULL) { + errCode = PullRequestStart(context); + } else { + errCode = PullResponseStart(context); + } + if (context->IsSkipTimeoutError(errCode)) { + // if E_TIMEOUT occurred, means send message pressure is high, put into resend map and wait for resend. + // just return to avoid higher pressure for send. + return E_OK; + } + if (errCode != E_OK) { + LOGE("[DataSync] SendStart errCode=%d", errCode); + return errCode; + } + if (tmpMode == SyncModeType::PUSH_AND_PULL && context->GetTaskErrCode() == -E_EKEYREVOKED) { + LOGE("wait for recv finished for push and pull mode"); + return -E_EKEYREVOKED; + } + return InnerSyncStart(context); +} + +int SingleVerDataSync::InnerSyncStart(SingleVerSyncTaskContext *context) +{ + while (true) { + if (windowSize_ <= 0 || isAllDataHasSent_) { + LOGD("[DataSync] InnerDataSync winSize=%d,isAllSent=%d,label=%s,device=%s", windowSize_, isAllDataHasSent_, + label_.c_str(), STR_MASK(deviceId_)); + return E_OK; + } + int mode = SyncOperation::TransferSyncMode(mode_); + if (mode == SyncModeType::PULL) { + LOGE("[DataSync] unexpected error"); + return -E_INVALID_ARGS; + } + int errCode; + context->IncSequenceId(); + if (mode == SyncModeType::PUSH || mode == SyncModeType::PUSH_AND_PULL) { + errCode = PushStart(context); + } else { + errCode = PullResponseStart(context); + } + if ((mode == SyncModeType::PUSH_AND_PULL) && errCode == -E_EKEYREVOKED) { + LOGE("[DataSync] wait for recv finished,label=%s,device=%s", label_.c_str(), STR_MASK(deviceId_)); + isAllDataHasSent_ = true; + return -E_EKEYREVOKED; + } + if (context->IsSkipTimeoutError(errCode)) { + // if E_TIMEOUT occurred, means send message pressure is high, put into resend map and wait for resend. + // just return to avoid higher pressure for send. + return E_OK; + } + if (errCode != E_OK) { + LOGE("[DataSync] InnerSend errCode=%d", errCode); + return errCode; + } + } + return E_OK; +} + +void SingleVerDataSync::InnerClearSyncStatus() +{ + sessionId_ = 0; + reSendMap_.clear(); + windowSize_ = 0; + maxSequenceIdHasSent_ = 0; + isAllDataHasSent_ = false; +} + +int SingleVerDataSync::TryContinueSync(SingleVerSyncTaskContext *context, const Message *message) +{ + if (message == nullptr) { + LOGE("[DataSync] AckRecv message nullptr"); + return -E_INVALID_ARGS; + } + const DataAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + uint64_t packetId = packet->GetPacketId(); // above 102 version data request reserve[0] store packetId value + uint32_t sessionId = message->GetSessionId(); + uint32_t sequenceId = message->GetSequenceId(); + + std::lock_guard lock(lock_); + LOGI("[DataSync] recv ack seqId=%" PRIu32 ",packetId=%" PRIu64 ",winSize=%d,label=%s,dev=%s", sequenceId, packetId, + windowSize_, label_.c_str(), STR_MASK(deviceId_)); + if (sessionId != sessionId_) { + LOGI("[DataSync] ignore ack,sessionId is different"); + return E_OK; + } + Timestamp lastQueryTime = 0; + if (reSendMap_.count(sequenceId) != 0) { + lastQueryTime = reSendMap_[sequenceId].end; + reSendMap_.erase(sequenceId); + windowSize_++; + } else { + LOGI("[DataSync] ack seqId not in map"); + return E_OK; + } + if (context->IsQuerySync() && storage_->GetInterfaceType() == ISyncInterface::SYNC_RELATION) { + Timestamp dbLastQueryTime = 0; + int errCode = metadata_->GetLastQueryTime(context->GetQuerySyncId(), context->GetDeviceId(), dbLastQueryTime); + if (errCode == E_OK && dbLastQueryTime < lastQueryTime) { + errCode = metadata_->SetLastQueryTime(context->GetQuerySyncId(), context->GetDeviceId(), lastQueryTime); + } + if (errCode != E_OK) { + return errCode; + } + } + if (!isAllDataHasSent_) { + return InnerSyncStart(context); + } else if (reSendMap_.size() == 0) { + context->SetOperationStatus(SyncOperation::OP_SEND_FINISHED); + InnerClearSyncStatus(); + return -E_FINISHED; + } + return E_OK; +} + +void SingleVerDataSync::ClearSyncStatus() +{ + std::lock_guard lock(lock_); + InnerClearSyncStatus(); +} + +int SingleVerDataSync::ReSendData(SingleVerSyncTaskContext *context) +{ + if (reSendMap_.empty()) { + LOGI("[DataSync] ReSend map empty"); + return -E_INTERNAL_ERROR; + } + uint32_t sequenceId = reSendMap_.begin()->first; + ReSendInfo reSendInfo = reSendMap_.begin()->second; + LOGI("[DataSync] ReSend mode=%d,start=%" PRIu64 ",end=%" PRIu64 ",delStart=%" PRIu64 ",delEnd=%" PRIu64 "," + "seqId=%" PRIu32 ",packetId=%" PRIu64 ",windowsize=%d,label=%s,deviceId=%s", mode_, reSendInfo.start, + reSendInfo.end, reSendInfo.deleteBeginTime, reSendInfo.deleteEndTime, sequenceId, reSendInfo.packetId, + windowSize_, label_.c_str(), STR_MASK(deviceId_)); + DataSyncReSendInfo dataReSendInfo = {sessionId_, sequenceId, reSendInfo.start, reSendInfo.end, + reSendInfo.deleteBeginTime, reSendInfo.deleteEndTime, reSendInfo.packetId}; + return ReSend(context, dataReSendInfo); +} + +std::string SingleVerDataSync::GetLocalDeviceName() +{ + std::string deviceInfo; + if (communicateHandle_ != nullptr) { + communicateHandle_->GetLocalIdentity(deviceInfo); + } + return deviceInfo; +} + +int SingleVerDataSync::Send(SingleVerSyncTaskContext *context, const Message *message, const CommErrHandler &handler, + uint32_t packetLen) +{ + bool startFeedDogRet = false; + if (packetLen > mtuSize_ && mtuSize_ > NOTIFY_MIN_MTU_SIZE) { + uint32_t time = static_cast(static_cast(packetLen) * + static_cast(context->GetTimeoutTime()) / mtuSize_); // no overflow + startFeedDogRet = context->StartFeedDogForSync(time, SyncDirectionFlag::SEND); + } + SendConfig sendConfig; + SetSendConfigParam(storage_->GetDbProperties(), context->GetDeviceId(), false, SEND_TIME_OUT, sendConfig); + int errCode = communicateHandle_->SendMessage(context->GetDeviceId(), message, sendConfig, handler); + if (errCode != E_OK) { + LOGE("[DataSync][Send] send message failed, errCode=%d", errCode); + if (startFeedDogRet) { + context->StopFeedDogForSync(SyncDirectionFlag::SEND); + } + } + return errCode; +} + +int SingleVerDataSync::GetData(SingleVerSyncTaskContext *context, std::vector &outData, size_t packetSize) +{ + int errCode; + UpdateMtuSize(); + if (context->GetRetryStatus() == SyncTaskContext::NEED_RETRY) { + context->SetRetryStatus(SyncTaskContext::NO_NEED_RETRY); + LOGI("[DataSync][GetData] resend data"); + errCode = GetUnsyncData(context, outData, packetSize); + } else { + ContinueToken token; + context->GetContinueToken(token); + if (token == nullptr) { + errCode = GetUnsyncData(context, outData, packetSize); + } else { + LOGD("[DataSync][GetData] get data from token"); + // if there is data to send, read out data, and update local watermark, send data + errCode = GetNextUnsyncData(context, outData, packetSize); + } + } + if (errCode == -E_UNFINISHED) { + LOGD("[DataSync][GetData] not finished."); + } + if (SingleVerDataSyncUtils::IsGetDataSuccessfully(errCode)) { + std::string localHashName = DBCommon::TransferHashString(GetLocalDeviceName()); + SingleVerDataSyncUtils::TransDbDataItemToSendDataItem(localHashName, outData); + } + return errCode; +} + +int SingleVerDataSync::GetDataWithPerformanceRecord(SingleVerSyncTaskContext *context, SyncEntry &syncOutData) +{ + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + size_t packetSize = (version > SOFTWARE_VERSION_RELEASE_2_0) ? + DBConstant::MAX_HPMODE_PACK_ITEM_SIZE : DBConstant::MAX_NORMAL_PACK_ITEM_SIZE; + bool needCompressOnSync = false; + uint8_t compressionRate = DBConstant::DEFAULT_COMPTRESS_RATE; + (void)storage_->GetCompressionOption(needCompressOnSync, compressionRate); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_READ_DATA); + } + int errCode = GetData(context, syncOutData.entries, packetSize); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_READ_DATA); + } + if (!SingleVerDataSyncUtils::IsGetDataSuccessfully(errCode)) { + context->SetTaskErrCode(errCode); + return errCode; + } + + int innerCode = InterceptData(syncOutData); + if (innerCode != E_OK) { + context->SetTaskErrCode(innerCode); + return innerCode; + } + + CompressAlgorithm remoteAlgo = context->ChooseCompressAlgo(); + if (needCompressOnSync && remoteAlgo != CompressAlgorithm::NONE) { + int compressCode = GenericSingleVerKvEntry::Compress(syncOutData.entries, syncOutData.compressedEntries, + { remoteAlgo, version }); + if (compressCode != E_OK) { + return compressCode; + } + } + return errCode; +} + +int SingleVerDataSync::GetUnsyncData(SingleVerSyncTaskContext *context, std::vector &outData, + size_t packetSize) +{ + int errCode; + WaterMark startMark = 0; + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + GetLocalWaterMark(curType, context->GetQuerySyncId(), context, startMark); + WaterMark endMark = MAX_TIMESTAMP; + if ((endMark == 0) || (startMark > endMark)) { + return E_OK; + } + ContinueToken token = nullptr; + context->GetContinueToken(token); + if (token != nullptr) { + storage_->ReleaseContinueToken(token); + } + DataSizeSpecInfo syncDataSizeInfo = GetDataSizeSpecInfo(packetSize); + if (curType != SyncType::QUERY_SYNC_TYPE) { + errCode = storage_->GetSyncData(startMark, endMark, outData, token, syncDataSizeInfo); + } else { + WaterMark deletedStartMark = 0; + GetLocalDeleteSyncWaterMark(context, deletedStartMark); + Timestamp lastQueryTimestamp = 0; + errCode = metadata_->GetLastQueryTime(context->GetQuerySyncId(), + context->GetDeviceId(), lastQueryTimestamp); + if (errCode == E_OK) { + QuerySyncObject queryObj = context->GetQuery(); + errCode = storage_->GetSyncData(queryObj, + SyncTimeRange{ startMark, deletedStartMark, endMark, endMark, lastQueryTimestamp}, + syncDataSizeInfo, token, outData); + } + } + context->SetContinueToken(token); + if (!SingleVerDataSyncUtils::IsGetDataSuccessfully(errCode)) { + LOGE("[DataSync][GetUnsyncData] get unsync data failed,errCode=%d", errCode); + } + return errCode; +} + +int SingleVerDataSync::GetNextUnsyncData(SingleVerSyncTaskContext *context, std::vector &outData, + size_t packetSize) +{ + ContinueToken token; + context->GetContinueToken(token); + DataSizeSpecInfo syncDataSizeInfo = GetDataSizeSpecInfo(packetSize); + int errCode = storage_->GetSyncDataNext(outData, token, syncDataSizeInfo); + context->SetContinueToken(token); + if (!SingleVerDataSyncUtils::IsGetDataSuccessfully(errCode)) { + LOGE("[DataSync][GetNextUnsyncData] get next unsync data failed, errCode=%d", errCode); + } + return errCode; +} + +int SingleVerDataSync::SaveData(const SingleVerSyncTaskContext *context, const std::vector &inData, + SyncType curType, const QuerySyncObject &query) +{ + if (inData.empty()) { + return E_OK; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_SAVE_DATA); + } + + const std::string localHashName = DBCommon::TransferHashString(GetLocalDeviceName()); + SingleVerDataSyncUtils::TransSendDataItemToLocal(context, localHashName, inData); + int errCode = E_OK; + // query only support prefix key and don't have query in packet in 104 version + errCode = storage_->PutSyncDataWithQuery(query, inData, context->GetDeviceId()); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_SAVE_DATA); + } + if (errCode != E_OK) { + LOGE("[DataSync][SaveData] save sync data failed,errCode=%d", errCode); + } + return errCode; +} + +void SingleVerDataSync::ResetSyncStatus(int inMode, SingleVerSyncTaskContext *context) +{ + mode_ = inMode; + maxSequenceIdHasSent_ = 0; + isAllDataHasSent_ = false; + context->ReSetSequenceId(); + reSendMap_.clear(); + if (context->GetRemoteSoftwareVersion() < SOFTWARE_VERSION_RELEASE_3_0) { + windowSize_ = LOW_VERSION_WINDOW_SIZE; + } else { + windowSize_ = HIGH_VERSION_WINDOW_SIZE; + } + int mode = SyncOperation::TransferSyncMode(inMode); + if (mode == SyncModeType::PUSH || mode == SyncModeType::PUSH_AND_PULL || mode == SyncModeType::PULL) { + sessionId_ = context->GetRequestSessionId(); + } else { + sessionId_ = context->GetResponseSessionId(); + } +} + +SyncTimeRange SingleVerDataSync::GetSyncDataTimeRange(SyncType syncType, SingleVerSyncTaskContext *context, + const std::vector &inData, UpdateWaterMark &isUpdate) +{ + WaterMark localMark = 0; + WaterMark deleteMark = 0; + GetLocalWaterMark(syncType, context->GetQuerySyncId(), context, localMark); + GetLocalDeleteSyncWaterMark(context, deleteMark); + return SingleVerDataSyncUtils::GetSyncDataTimeRange(syncType, localMark, deleteMark, inData, isUpdate); +} + +int SingleVerDataSync::SaveLocalWaterMark(SyncType syncType, const SingleVerSyncTaskContext *context, + SyncTimeRange dataTimeRange, bool isCheckBeforUpdate) const +{ + WaterMark localMark = 0; + int errCode = E_OK; + const std::string &deviceId = context->GetDeviceId(); + std::string queryId = context->GetQuerySyncId(); + if (syncType != SyncType::QUERY_SYNC_TYPE) { + if (isCheckBeforUpdate) { + GetLocalWaterMark(syncType, queryId, context, localMark); + if (localMark >= dataTimeRange.endTime) { + return E_OK; + } + } + errCode = metadata_->SaveLocalWaterMark(deviceId, dataTimeRange.endTime); + } else { + bool isNeedUpdateMark = true; + bool isNeedUpdateDeleteMark = true; + if (isCheckBeforUpdate) { + WaterMark deleteDataWaterMark = 0; + GetLocalWaterMark(syncType, queryId, context, localMark); + GetLocalDeleteSyncWaterMark(context, deleteDataWaterMark); + if (localMark >= dataTimeRange.endTime) { + isNeedUpdateMark = false; + } + if (deleteDataWaterMark >= dataTimeRange.deleteEndTime) { + isNeedUpdateDeleteMark = false; + } + } + if (isNeedUpdateMark) { + LOGD("label=%s,dev=%s,endTime=%" PRIu64, label_.c_str(), STR_MASK(GetDeviceId()), dataTimeRange.endTime); + errCode = metadata_->SetSendQueryWaterMark(queryId, deviceId, dataTimeRange.endTime); + if (errCode != E_OK) { + LOGE("[DataSync][SaveLocalWaterMark] save query metadata watermark failed,errCode=%d", errCode); + return errCode; + } + } + if (isNeedUpdateDeleteMark) { + LOGD("label=%s,dev=%s,deleteEndTime=%" PRIu64, label_.c_str(), STR_MASK(GetDeviceId()), + dataTimeRange.deleteEndTime); + errCode = metadata_->SetSendDeleteSyncWaterMark(context->GetDeleteSyncId(), dataTimeRange.deleteEndTime); + } + } + if (errCode != E_OK) { + LOGE("[DataSync][SaveLocalWaterMark] save metadata local watermark failed,errCode=%d", errCode); + } + return errCode; +} + +void SingleVerDataSync::GetPeerWaterMark(SyncType syncType, const std::string &queryIdentify, + const DeviceID &deviceId, WaterMark &waterMark) const +{ + if (syncType != SyncType::QUERY_SYNC_TYPE) { + metadata_->GetPeerWaterMark(deviceId, waterMark); + return; + } + metadata_->GetRecvQueryWaterMark(queryIdentify, deviceId, waterMark); +} + +void SingleVerDataSync::GetPeerDeleteSyncWaterMark(const DeviceID &deviceId, WaterMark &waterMark) +{ + metadata_->GetRecvDeleteSyncWaterMark(deviceId, waterMark); +} + +void SingleVerDataSync::GetLocalDeleteSyncWaterMark(const SingleVerSyncTaskContext *context, + WaterMark &waterMark) const +{ + metadata_->GetSendDeleteSyncWaterMark(context->GetDeleteSyncId(), waterMark, context->IsAutoLiftWaterMark()); +} + +void SingleVerDataSync::GetLocalWaterMark(SyncType syncType, const std::string &queryIdentify, + const SingleVerSyncTaskContext *context, WaterMark &waterMark) const +{ + if (syncType != SyncType::QUERY_SYNC_TYPE) { + metadata_->GetLocalWaterMark(context->GetDeviceId(), waterMark); + return; + } + metadata_->GetSendQueryWaterMark(queryIdentify, context->GetDeviceId(), + waterMark, context->IsAutoLiftWaterMark()); +} + +int SingleVerDataSync::RemoveDeviceDataHandle(SingleVerSyncTaskContext *context, const Message *message, + WaterMark maxSendDataTime) +{ + bool isNeedClearRemoteData = false; + std::lock_guard autoLock(removeDeviceDataLock_); + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_3_0) { + uint64_t clearDeviceDataMark = 0; + metadata_->GetRemoveDataMark(context->GetDeviceId(), clearDeviceDataMark); + isNeedClearRemoteData = (clearDeviceDataMark == REMOVE_DEVICE_DATA_MARK); + } else { + const DataRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + LOGE("[RemoveDeviceDataHandle] get packet object failed"); + return -E_INVALID_ARGS; + } + SyncType curType = SyncOperation::GetSyncType(packet->GetMode()); + WaterMark packetLocalMark = packet->GetLocalWaterMark(); + WaterMark peerMark = 0; + GetPeerWaterMark(curType, context->GetQuerySyncId(), context->GetDeviceId(), peerMark); + isNeedClearRemoteData = ((packetLocalMark == 0) && (peerMark != 0)); + } + if (!isNeedClearRemoteData) { + return E_OK; + } + int errCode = E_OK; + if (context->IsNeedClearRemoteStaleData()) { + // need to clear remote device history data + errCode = storage_->RemoveDeviceData(context->GetDeviceId(), true); + if (errCode != E_OK) { + (void)SendDataAck(context, message, errCode, maxSendDataTime); + return errCode; + } + if (context->GetRemoteSoftwareVersion() == SOFTWARE_VERSION_EARLIEST) { + // avoid repeat clear in ack + metadata_->SaveLocalWaterMark(context->GetDeviceId(), 0); + } + } + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_3_0) { + errCode = metadata_->ResetMetaDataAfterRemoveData(context->GetDeviceId()); + if (errCode != E_OK) { + (void)SendDataAck(context, message, errCode, maxSendDataTime); + return errCode; + } + } + return E_OK; +} + +int SingleVerDataSync::DealRemoveDeviceDataByAck(SingleVerSyncTaskContext *context, WaterMark ackWaterMark, + const std::vector &reserved) +{ + bool isNeedClearRemoteData = false; + std::lock_guard autoLock(removeDeviceDataLock_); + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_3_0) { + uint64_t clearDeviceDataMark = 0; + metadata_->GetRemoveDataMark(context->GetDeviceId(), clearDeviceDataMark); + isNeedClearRemoteData = (clearDeviceDataMark != 0); + } else if (reserved.empty()) { + WaterMark localMark = 0; + GetLocalWaterMark(curType, context->GetQuery().GetIdentify(), context, localMark); + isNeedClearRemoteData = ((localMark != 0) && (ackWaterMark == 0)); + } else { + WaterMark peerMark = 0; + GetPeerWaterMark(curType, context->GetQuerySyncId(), + context->GetDeviceId(), peerMark); + isNeedClearRemoteData = ((reserved[ACK_PACKET_RESERVED_INDEX_LOCAL_WATER_MARK] == 0) && (peerMark != 0)); + } + if (!isNeedClearRemoteData) { + return E_OK; + } + // need to clear remote historydata + LOGI("[DataSync][WaterMarkException] AckRecv reserved not empty,rebuilted,clear historydata,label=%s,dev=%s", + label_.c_str(), STR_MASK(GetDeviceId())); + int errCode = storage_->RemoveDeviceData(context->GetDeviceId(), true); + if (errCode != E_OK) { + return errCode; + } + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_3_0) { + errCode = metadata_->ResetMetaDataAfterRemoveData(context->GetDeviceId()); + } + return errCode; +} + +void SingleVerDataSync::SetSessionEndTimestamp(Timestamp end) +{ + sessionEndTimestamp_ = end; +} + +Timestamp SingleVerDataSync::GetSessionEndTimestamp() const +{ + return sessionEndTimestamp_; +} + +void SingleVerDataSync::UpdateSendInfo(SyncTimeRange dataTimeRange, SingleVerSyncTaskContext *context) +{ + ReSendInfo reSendInfo; + reSendInfo.start = dataTimeRange.beginTime; + reSendInfo.end = dataTimeRange.endTime; + reSendInfo.deleteBeginTime = dataTimeRange.deleteBeginTime; + reSendInfo.deleteEndTime = dataTimeRange.deleteEndTime; + reSendInfo.packetId = context->GetPacketId(); + maxSequenceIdHasSent_++; + reSendMap_[maxSequenceIdHasSent_] = reSendInfo; + windowSize_--; + ContinueToken token; + context->GetContinueToken(token); + if (token == nullptr) { + isAllDataHasSent_ = true; + } + LOGI("[DataSync] mode=%d,start=%" PRIu64 ",end=%" PRIu64 ",deleteStart=%" PRIu64 ",deleteEnd=%" PRIu64 "," + "seqId=%" PRIu32 ",packetId=%" PRIu64 ",window_size=%d,isAllSend=%d,label=%s,device=%s", mode_, + reSendInfo.start, reSendInfo.end, reSendInfo.deleteBeginTime, reSendInfo.deleteEndTime, maxSequenceIdHasSent_, + reSendInfo.packetId, windowSize_, isAllDataHasSent_, label_.c_str(), STR_MASK(deviceId_)); +} + +void SingleVerDataSync::FillDataRequestPacket(DataRequestPacket *packet, SingleVerSyncTaskContext *context, + SyncEntry &syncData, int sendCode, int mode) +{ + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + WaterMark localMark = 0; + WaterMark peerMark = 0; + WaterMark deleteMark = 0; + bool needCompressOnSync = false; + uint8_t compressionRate = DBConstant::DEFAULT_COMPTRESS_RATE; + (void)storage_->GetCompressionOption(needCompressOnSync, compressionRate); + std::string id = context->GetQuerySyncId(); + GetLocalWaterMark(curType, id, context, localMark); + GetPeerWaterMark(curType, id, context->GetDeviceId(), peerMark); + GetLocalDeleteSyncWaterMark(context, deleteMark); + if (((mode != SyncModeType::RESPONSE_PULL && sendCode == E_OK)) || + (mode == SyncModeType::RESPONSE_PULL && sendCode == SEND_FINISHED)) { + packet->SetLastSequence(); + } + int tmpMode = mode; + if (mode == SyncModeType::RESPONSE_PULL) { + tmpMode = (curType == SyncType::QUERY_SYNC_TYPE) ? SyncModeType::QUERY_PUSH : SyncModeType::PUSH; + } + packet->SetData(syncData.entries); + packet->SetCompressData(syncData.compressedEntries); + packet->SetBasicInfo(sendCode, version, tmpMode); + packet->SetWaterMark(localMark, peerMark, deleteMark); + if (SyncOperation::TransferSyncMode(mode) == SyncModeType::PUSH_AND_PULL) { + packet->SetEndWaterMark(context->GetEndMark()); + packet->SetSessionId(context->GetRequestSessionId()); + } + packet->SetQuery(context->GetQuery()); + packet->SetQueryId(context->GetQuerySyncId()); + CompressAlgorithm curAlgo = context->ChooseCompressAlgo(); + if (needCompressOnSync && curAlgo != CompressAlgorithm::NONE) { + packet->SetCompressDataMark(); + packet->SetCompressAlgo(curAlgo); + } + SingleVerDataSyncUtils::SetPacketId(packet, context, version); + if (curType == SyncType::QUERY_SYNC_TYPE && (context->GetQuery().HasLimit() || + context->GetQuery().HasOrderBy())) { + packet->SetUpdateWaterMark(); + } + LOGD("[DataSync] curType=%d,local=%" PRIu64 ",del=%" PRIu64 ",end=%" PRIu64 ",label=%s,dev=%s,queryId=%s," + "isCompress=%d", static_cast(curType), localMark, deleteMark, context->GetEndMark(), label_.c_str(), + STR_MASK(GetDeviceId()), STR_MASK(context->GetQuery().GetIdentify()), packet->IsCompressData()); +} + +int SingleVerDataSync::RequestStart(SingleVerSyncTaskContext *context, int mode) +{ + if (!SingleVerDataSyncUtils::QuerySyncCheck(context)) { + context->SetTaskErrCode(-E_NOT_SUPPORT); + return -E_NOT_SUPPORT; + } + int errCode = RemoveDeviceDataIfNeed(context); + if (errCode != E_OK) { + context->SetTaskErrCode(errCode); + return errCode; + } + SyncEntry syncData; + // get data + errCode = GetDataWithPerformanceRecord(context, syncData); + SingleVerDataSyncUtils::TranslateErrCodeIfNeed(mode, context->GetRemoteSoftwareVersion(), errCode); + + if (!SingleVerDataSyncUtils::IsGetDataSuccessfully(errCode)) { + LOGE("[DataSync][PushStart] get data failed, errCode=%d", errCode); + return errCode; + } + + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + if (packet == nullptr) { + LOGE("[DataSync][PushStart] new DataRequestPacket error"); + return -E_OUT_OF_MEMORY; + } + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + UpdateWaterMark isUpdateWaterMark; + SyncTimeRange dataTime = GetSyncDataTimeRange(curType, context, syncData.entries, isUpdateWaterMark); + if (errCode == E_OK) { + SetSessionEndTimestamp(std::max(dataTime.endTime, dataTime.deleteEndTime)); + } + FillDataRequestPacket(packet, context, syncData, errCode, mode); + errCode = SendDataPacket(curType, packet, context); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_MACHINE_START_TO_PUSH_SEND); + } + if (errCode == E_OK || errCode == -E_TIMEOUT) { + UpdateSendInfo(dataTime, context); + } + if (errCode == E_OK) { + if (curType == SyncType::QUERY_SYNC_TYPE && (context->GetQuery().HasLimit() || + context->GetQuery().HasOrderBy())) { + LOGI("[DataSync][RequestStart] query contain limit/offset/orderby, no need to update watermark."); + return E_OK; + } + SyncTimeRange tmpDataTime = SingleVerDataSyncUtils::ReviseLocalMark(curType, dataTime, isUpdateWaterMark); + SaveLocalWaterMark(curType, context, tmpDataTime); + } + return errCode; +} + +int SingleVerDataSync::PushStart(SingleVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + return RequestStart(context, + (curType == SyncType::QUERY_SYNC_TYPE) ? SyncModeType::QUERY_PUSH : SyncModeType::PUSH); +} + +int SingleVerDataSync::PushPullStart(SingleVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + return RequestStart(context, context->GetMode()); +} + +int SingleVerDataSync::PullRequestStart(SingleVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + if (!SingleVerDataSyncUtils::QuerySyncCheck(context)) { + context->SetTaskErrCode(-E_NOT_SUPPORT); + return -E_NOT_SUPPORT; + } + int errCode = RemoveDeviceDataIfNeed(context); + if (errCode != E_OK) { + context->SetTaskErrCode(errCode); + return errCode; + } + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + if (packet == nullptr) { + LOGE("[DataSync][PullRequest]new DataRequestPacket error"); + return -E_OUT_OF_MEMORY; + } + SyncType syncType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + WaterMark peerMark = 0; + WaterMark localMark = 0; + WaterMark deleteMark = 0; + GetPeerWaterMark(syncType, context->GetQuerySyncId(), + context->GetDeviceId(), peerMark); + GetLocalWaterMark(syncType, context->GetQuerySyncId(), context, localMark); + GetLocalDeleteSyncWaterMark(context, deleteMark); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + WaterMark endMark = context->GetEndMark(); + SyncTimeRange dataTime = {localMark, deleteMark, localMark, deleteMark}; + + packet->SetBasicInfo(E_OK, version, context->GetMode()); + packet->SetWaterMark(localMark, peerMark, deleteMark); + packet->SetEndWaterMark(endMark); + packet->SetSessionId(context->GetRequestSessionId()); + packet->SetQuery(context->GetQuery()); + packet->SetQueryId(context->GetQuerySyncId()); + packet->SetLastSequence(); + SingleVerDataSyncUtils::SetPacketId(packet, context, version); + + LOGD("[DataSync][Pull] curType=%d,local=%" PRIu64 ",del=%" PRIu64 ",end=%" PRIu64 ",peer=%" PRIu64 ",label=%s," + "dev=%s", static_cast(syncType), localMark, deleteMark, endMark, peerMark, label_.c_str(), + STR_MASK(GetDeviceId())); + UpdateSendInfo(dataTime, context); + return SendDataPacket(syncType, packet, context); +} + +int SingleVerDataSync::PullResponseStart(SingleVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + SyncEntry syncData; + // get data + int errCode = GetDataWithPerformanceRecord(context, syncData); + if (!SingleVerDataSyncUtils::IsGetDataSuccessfully(errCode)) { + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0) { + SendPullResponseDataPkt(errCode, syncData, context); + } + return errCode; + } + // if send finished + int ackCode = E_OK; + ContinueToken token = nullptr; + context->GetContinueToken(token); + if (errCode == E_OK && token == nullptr) { + LOGD("[DataSync][PullResponse] send last frame end"); + ackCode = SEND_FINISHED; + } + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + UpdateWaterMark isUpdateWaterMark; + SyncTimeRange dataTime = GetSyncDataTimeRange(curType, context, syncData.entries, isUpdateWaterMark); + if (errCode == E_OK) { + SetSessionEndTimestamp(std::max(dataTime.endTime, dataTime.deleteEndTime)); + } + errCode = SendPullResponseDataPkt(ackCode, syncData, context); + if (errCode == E_OK || errCode == -E_TIMEOUT) { + UpdateSendInfo(dataTime, context); + } + if (errCode == E_OK) { + if (curType == SyncType::QUERY_SYNC_TYPE && (context->GetQuery().HasLimit() || + context->GetQuery().HasOrderBy())) { + LOGI("[DataSync][PullResponseStart] query contain limit/offset/orderby, no need to update watermark."); + return E_OK; + } + SyncTimeRange tmpDataTime = SingleVerDataSyncUtils::ReviseLocalMark(curType, dataTime, isUpdateWaterMark); + SaveLocalWaterMark(curType, context, tmpDataTime); + } + return errCode; +} + +void SingleVerDataSync::UpdateQueryPeerWaterMark(SyncType syncType, const std::string &queryId, SyncTimeRange &dataTime, + const SingleVerSyncTaskContext *context, UpdateWaterMark isUpdateWaterMark) +{ + WaterMark tmpPeerWatermark = dataTime.endTime; + WaterMark tmpPeerDeletedWatermark = dataTime.deleteEndTime; + if (isUpdateWaterMark.normalUpdateMark) { + tmpPeerWatermark++; + } + if (isUpdateWaterMark.deleteUpdateMark) { + tmpPeerDeletedWatermark++; + } + UpdatePeerWaterMark(syncType, queryId, context, tmpPeerWatermark, tmpPeerDeletedWatermark); +} + +void SingleVerDataSync::UpdatePeerWaterMark(SyncType syncType, const std::string &queryId, + const SingleVerSyncTaskContext *context, WaterMark peerWatermark, WaterMark peerDeletedWatermark) +{ + if (peerWatermark == 0 && peerDeletedWatermark == 0) { + return; + } + int errCode = E_OK; + if (syncType != SyncType::QUERY_SYNC_TYPE) { + errCode = metadata_->SavePeerWaterMark(context->GetDeviceId(), peerWatermark, true); + } else { + if (peerWatermark != 0) { + LOGD("label=%s,dev=%s,endTime=%" PRIu64, label_.c_str(), STR_MASK(GetDeviceId()), peerWatermark); + errCode = metadata_->SetRecvQueryWaterMark(queryId, context->GetDeviceId(), peerWatermark); + if (errCode != E_OK) { + LOGE("[DataSync][UpdatePeerWaterMark] save query peer water mark failed,errCode=%d", errCode); + } + } + if (peerDeletedWatermark != 0) { + LOGD("label=%s,dev=%s,peerDeletedTime=%" PRIu64, + label_.c_str(), STR_MASK(GetDeviceId()), peerDeletedWatermark); + errCode = metadata_->SetRecvDeleteSyncWaterMark(context->GetDeleteSyncId(), peerDeletedWatermark); + } + } + if (errCode != E_OK) { + LOGE("[DataSync][UpdatePeerWaterMark] save peer water mark failed,errCode=%d", errCode); + } +} + +int SingleVerDataSync::DoAbilitySyncIfNeed(SingleVerSyncTaskContext *context, const Message *message, bool isControlMsg) +{ + uint16_t remoteCommunicatorVersion = 0; + if (communicateHandle_->GetRemoteCommunicatorVersion(context->GetDeviceId(), remoteCommunicatorVersion) == + -E_NOT_FOUND) { + LOGE("[DataSync][DoAbilitySyncIfNeed] get remote communicator version failed"); + return -E_VERSION_NOT_SUPPORT; + } + // If remote is not the first version, we need check SOFTWARE_VERSION_BASE + if (remoteCommunicatorVersion == 0) { + LOGI("[DataSync] set remote version 0"); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_EARLIEST); + return E_OK; + } else { + LOGI("[DataSync][DoAbilitySyncIfNeed] need do ability sync"); + if (isControlMsg) { + SendControlAck(context, message, -E_NEED_ABILITY_SYNC, 0); + } else { + SendDataAck(context, message, -E_NEED_ABILITY_SYNC, 0); + } + return -E_WAIT_NEXT_MESSAGE; + } +} + +int SingleVerDataSync::DataRequestRecvPre(SingleVerSyncTaskContext *context, const Message *message) +{ + if (context == nullptr || message == nullptr) { + return -E_INVALID_ARGS; + } + auto *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + if (context->GetRemoteSoftwareVersion() <= SOFTWARE_VERSION_BASE) { + return DoAbilitySyncIfNeed(context, message); + } + int32_t sendCode = packet->GetSendCode(); + if (sendCode == -E_VERSION_NOT_SUPPORT) { + LOGE("[DataSync] Version mismatch: ver=%u, current=%u", packet->GetVersion(), SOFTWARE_VERSION_CURRENT); + (void)SendDataAck(context, message, -E_VERSION_NOT_SUPPORT, 0); + return -E_WAIT_NEXT_MESSAGE; + } + // only deal with pull response packet errCode + if (sendCode != E_OK && sendCode != SEND_FINISHED && + message->GetSessionId() == context->GetRequestSessionId()) { + LOGE("[DataSync][DataRequestRecvPre] remote pullResponse getData sendCode=%d", sendCode); + return sendCode; + } + int errCode = RunPermissionCheck(context, message, packet); + if (errCode != E_OK) { + return errCode; + } + if (std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT) > SOFTWARE_VERSION_RELEASE_2_0) { + errCode = CheckSchemaStrategy(context, message); + } + if (errCode == E_OK) { + errCode = SingleVerDataSyncUtils::RequestQueryCheck(packet, storage_); + } + if (errCode != E_OK) { + (void)SendDataAck(context, message, errCode, 0); + } + return errCode; +} + +int SingleVerDataSync::DataRequestRecv(SingleVerSyncTaskContext *context, const Message *message, + WaterMark &pullEndWatermark) +{ + int errCode = DataRequestRecvPre(context, message); + if (errCode != E_OK) { + return errCode; + } + const DataRequestPacket *packet = message->GetObject(); + const std::vector &data = packet->GetData(); + SyncType curType = SyncOperation::GetSyncType(packet->GetMode()); + LOGI("[DataSync][DataRequestRecv] curType=%d, remote ver=%" PRIu32 ", size=%zu, errCode=%d, queryId=%s," + " Label=%s, dev=%s", static_cast(curType), packet->GetVersion(), data.size(), packet->GetSendCode(), + STR_MASK(packet->GetQueryId()), label_.c_str(), STR_MASK(GetDeviceId())); + context->SetReceiveWaterMarkErr(false); + UpdateWaterMark isUpdateWaterMark; + SyncTimeRange dataTime = SingleVerDataSyncUtils::GetRecvDataTimeRange(curType, data, isUpdateWaterMark); + errCode = RemoveDeviceDataHandle(context, message, dataTime.endTime); + if (errCode != E_OK) { + return errCode; + } + if (WaterMarkErrHandle(curType, context, message)) { + return E_OK; + } + GetPullEndWatermark(context, packet, pullEndWatermark); + // save data first + errCode = SaveData(context, data, curType, packet->GetQuery()); + if (errCode != E_OK) { + (void)SendDataAck(context, message, errCode, dataTime.endTime); + return errCode; + } + if (pullEndWatermark > 0 && !storage_->IsReadable()) { // pull mode + pullEndWatermark = 0; + errCode = SendDataAck(context, message, -E_EKEYREVOKED, dataTime.endTime); + } else { + // if data is empty, we don't know the max timestap of this packet. + errCode = SendDataAck(context, message, !data.empty() ? E_OK : WATER_MARK_INVALID, dataTime.endTime); + } + RemotePushFinished(packet->GetSendCode(), packet->GetMode(), message->GetSessionId(), + context->GetRequestSessionId()); + if (curType != SyncType::QUERY_SYNC_TYPE && isUpdateWaterMark.normalUpdateMark) { + UpdatePeerWaterMark(curType, "", context, dataTime.endTime + 1, 0); + } else if (curType == SyncType::QUERY_SYNC_TYPE && packet->IsNeedUpdateWaterMark()) { + UpdateQueryPeerWaterMark(curType, packet->GetQueryId(), dataTime, context, isUpdateWaterMark); + } + if (errCode != E_OK) { + return errCode; + } + if (packet->GetSendCode() == SEND_FINISHED) { + return -E_RECV_FINISHED; + } + return errCode; +} + +int SingleVerDataSync::SendDataPacket(SyncType syncType, const DataRequestPacket *packet, + SingleVerSyncTaskContext *context) +{ + Message *message = new (std::nothrow) Message(SingleVerDataSyncUtils::GetMessageId(syncType)); + if (message == nullptr) { + LOGE("[DataSync][SendDataPacket] new message error"); + delete packet; + packet = nullptr; + return -E_OUT_OF_MEMORY; + } + uint32_t packetLen = packet->CalculateLen(SingleVerDataSyncUtils::GetMessageId(syncType)); + int errCode = message->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete message; + message = nullptr; + LOGE("[DataSync][SendDataPacket] set external object failed errCode=%d", errCode); + return errCode; + } + SingleVerDataSyncUtils::SetMessageHeadInfo(*message, TYPE_REQUEST, context->GetDeviceId(), + context->GetSequenceId(), context->GetRequestSessionId()); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_DATA_SEND_REQUEST_TO_ACK_RECV); + } + CommErrHandler handler = std::bind(&SyncTaskContext::CommErrHandlerFunc, std::placeholders::_1, + context, message->GetSessionId()); + errCode = Send(context, message, handler, packetLen); + if (errCode != E_OK) { + delete message; + message = nullptr; + } + + return errCode; +} + +int SingleVerDataSync::SendPullResponseDataPkt(int ackCode, SyncEntry &syncOutData, + SingleVerSyncTaskContext *context) +{ + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + if (packet == nullptr) { + LOGE("[DataSync][SendPullResponseDataPkt] new data request packet error"); + return -E_OUT_OF_MEMORY; + } + SyncType syncType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + FillDataRequestPacket(packet, context, syncOutData, ackCode, SyncModeType::RESPONSE_PULL); + uint32_t packetLen = packet->CalculateLen(SingleVerDataSyncUtils::GetMessageId(syncType)); + Message *message = new (std::nothrow) Message(SingleVerDataSyncUtils::GetMessageId(syncType)); + if (message == nullptr) { + LOGE("[DataSync][SendPullResponseDataPkt] new message error"); + delete packet; + packet = nullptr; + return -E_OUT_OF_MEMORY; + } + int errCode = message->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete message; + message = nullptr; + LOGE("[SendPullResponseDataPkt] set external object failed, errCode=%d", errCode); + return errCode; + } + SingleVerDataSyncUtils::SetMessageHeadInfo(*message, TYPE_REQUEST, context->GetDeviceId(), + context->GetSequenceId(), context->GetResponseSessionId()); + SendResetWatchDogPacket(context, packetLen); + errCode = Send(context, message, nullptr, packetLen); + if (errCode != E_OK) { + delete message; + message = nullptr; + } + return errCode; +} + +void SingleVerDataSync::SendFinishedDataAck(SingleVerSyncTaskContext *context, const Message *message) +{ + SendDataAck(context, message, E_OK, 0); +} + +int SingleVerDataSync::SendDataAck(SingleVerSyncTaskContext *context, const Message *message, int32_t recvCode, + WaterMark maxSendDataTime) +{ + const DataRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + Message *ackMessage = new (std::nothrow) Message(message->GetMessageId()); + if (ackMessage == nullptr) { + LOGE("[DataSync][SendDataAck] new message error"); + return -E_OUT_OF_MEMORY; + } + DataAckPacket ack; + SetAckPacket(ack, context, packet, recvCode, maxSendDataTime); + int errCode = ackMessage->SetCopiedObject(ack); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + LOGE("[DataSync][SendDataAck] set copied object failed, errcode=%d", errCode); + return errCode; + } + SingleVerDataSyncUtils::SetMessageHeadInfo(*ackMessage, TYPE_RESPONSE, context->GetDeviceId(), + message->GetSequenceId(), message->GetSessionId()); + + errCode = Send(context, ackMessage, nullptr, 0); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + } + return errCode; +} + +bool SingleVerDataSync::AckPacketIdCheck(const Message *message) +{ + if (message == nullptr) { + LOGE("[DataSync] AckRecv message nullptr"); + return false; + } + if (message->GetMessageType() == TYPE_NOTIFY || message->IsFeedbackError()) { + return true; + } + const DataAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return false; + } + uint64_t packetId = packet->GetPacketId(); // above 102 version data request reserve[0] store packetId value + std::lock_guard lock(lock_); + uint32_t sequenceId = message->GetSequenceId(); + if (reSendMap_.count(sequenceId) != 0) { + uint64_t originalPacketId = reSendMap_[sequenceId].packetId; + if (DataAckPacket::IsPacketIdValid(packetId) && packetId != originalPacketId) { + LOGE("[DataSync] packetId[%" PRIu64 "] is not match with original[%" PRIu64 "]", packetId, + originalPacketId); + return false; + } + } + return true; +} + +int SingleVerDataSync::AckRecv(SingleVerSyncTaskContext *context, const Message *message) +{ + int errCode = SingleVerDataSyncUtils::AckMsgErrnoCheck(context, message); + if (errCode != E_OK) { + return errCode; + } + const DataAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + int32_t recvCode = packet->GetRecvCode(); + LOGD("[DataSync][AckRecv] ver=%u,recvCode=%d,myversion=%u,label=%s,dev=%s", packet->GetVersion(), recvCode, + SOFTWARE_VERSION_CURRENT, label_.c_str(), STR_MASK(GetDeviceId())); + if (recvCode == -E_VERSION_NOT_SUPPORT) { + LOGE("[DataSync][AckRecv] Version mismatch"); + return -E_VERSION_NOT_SUPPORT; + } + + if (recvCode == -E_NEED_ABILITY_SYNC || recvCode == -E_NOT_PERMIT) { + // we should ReleaseContinueToken, avoid crash + LOGI("[DataSync][AckRecv] Data sync abort,recvCode =%d,label =%s,dev=%s", recvCode, label_.c_str(), + STR_MASK(GetDeviceId())); + context->ReleaseContinueToken(); + return recvCode; + } + uint64_t data = packet->GetData(); + if (recvCode == LOCAL_WATER_MARK_NOT_INIT) { + return DealWaterMarkException(context, data, packet->GetReserved()); + } + + if (recvCode == -E_SAVE_DATA_NOTIFY && data != 0) { + // data only use low 32bit + context->StartFeedDogForSync(static_cast(data), SyncDirectionFlag::RECEIVE); + LOGI("[DataSync][AckRecv] notify ResetWatchDog=%" PRIu64 ",label=%s,dev=%s", data, label_.c_str(), + STR_MASK(GetDeviceId())); + } + + if (recvCode != E_OK && recvCode != WATER_MARK_INVALID) { + LOGW("[DataSync][AckRecv] Received a uncatched recvCode=%d,label=%s,dev=%s", recvCode, + label_.c_str(), STR_MASK(GetDeviceId())); + return recvCode; + } + + // Judge if send finished + ContinueToken token; + context->GetContinueToken(token); + if (((message->GetSessionId() == context->GetResponseSessionId()) || + (message->GetSessionId() == context->GetRequestSessionId())) && (token == nullptr)) { + return -E_NO_DATA_SEND; + } + + // send next data + return -E_SEND_DATA; +} + +void SingleVerDataSync::SendSaveDataNotifyPacket(SingleVerSyncTaskContext *context, uint32_t pktVersion, + uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) +{ + if (inMsgId != DATA_SYNC_MESSAGE && inMsgId != QUERY_SYNC_MESSAGE) { + LOGE("[SingleVerDataSync] messageId not available."); + return; + } + Message *ackMessage = new (std::nothrow) Message(inMsgId); + if (ackMessage == nullptr) { + LOGE("[DataSync][SaveDataNotify] new message failed"); + return; + } + + DataAckPacket ack; + ack.SetRecvCode(-E_SAVE_DATA_NOTIFY); + ack.SetVersion(pktVersion); + int errCode = ackMessage->SetCopiedObject(ack); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + LOGE("[DataSync][SendSaveDataNotifyPacket] set copied object failed,errcode=%d", errCode); + return; + } + SingleVerDataSyncUtils::SetMessageHeadInfo(*ackMessage, TYPE_NOTIFY, context->GetDeviceId(), sequenceId, sessionId); + + errCode = Send(context, ackMessage, nullptr, 0); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + } + LOGD("[DataSync][SaveDataNotify] Send SaveDataNotify packet Finished,errcode=%d,label=%s,dev=%s", + errCode, label_.c_str(), STR_MASK(GetDeviceId())); +} + +void SingleVerDataSync::GetPullEndWatermark(const SingleVerSyncTaskContext *context, const DataRequestPacket *packet, + WaterMark &pullEndWatermark) const +{ + if (packet == nullptr) { + return; + } + int mode = SyncOperation::TransferSyncMode(packet->GetMode()); + if ((mode == SyncModeType::PULL) || (mode == SyncModeType::PUSH_AND_PULL)) { + WaterMark endMark = packet->GetEndWaterMark(); + TimeOffset offset; + metadata_->GetTimeOffset(context->GetDeviceId(), offset); + pullEndWatermark = endMark - static_cast(offset); + LOGD("[DataSync][PullEndWatermark] packetEndMark=%" PRIu64 ",offset=%" PRId64 ",endWaterMark=%" PRIu64 "," + "label=%s,dev=%s", endMark, offset, pullEndWatermark, label_.c_str(), STR_MASK(GetDeviceId())); + } +} + +int SingleVerDataSync::DealWaterMarkException(SingleVerSyncTaskContext *context, WaterMark ackWaterMark, + const std::vector &reserved) +{ + WaterMark deletedWaterMark = 0; + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + if (curType == SyncType::QUERY_SYNC_TYPE) { + if (reserved.size() <= ACK_PACKET_RESERVED_INDEX_DELETE_WATER_MARK) { + LOGE("[DataSync] get packet reserve size failed"); + return -E_INVALID_ARGS; + } + deletedWaterMark = reserved[ACK_PACKET_RESERVED_INDEX_DELETE_WATER_MARK]; + } + LOGI("[DataSync][WaterMarkException] AckRecv water error, mark=%" PRIu64 ",deleteMark=%" PRIu64 "," + "label=%s,dev=%s", ackWaterMark, deletedWaterMark, label_.c_str(), STR_MASK(GetDeviceId())); + int errCode = SaveLocalWaterMark(curType, context, + {0, 0, ackWaterMark, deletedWaterMark}); + if (errCode != E_OK) { + return errCode; + } + context->SetRetryStatus(SyncTaskContext::NEED_RETRY); + context->IncNegotiationCount(); + SingleVerDataSyncUtils::PushAndPullKeyRevokHandle(context); + if (!context->IsNeedClearRemoteStaleData()) { + return -E_RE_SEND_DATA; + } + errCode = DealRemoveDeviceDataByAck(context, ackWaterMark, reserved); + if (errCode != E_OK) { + return errCode; + } + return -E_RE_SEND_DATA; +} + +int SingleVerDataSync::RunPermissionCheck(SingleVerSyncTaskContext *context, const Message *message, + const DataRequestPacket *packet) +{ + int mode = SyncOperation::TransferSyncMode(packet->GetMode()); + int errCode = SingleVerDataSyncUtils::RunPermissionCheck(context, storage_, label_, mode); + if (errCode != E_OK) { + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_EARLIEST) { // ver 101 can't handle this errCode + (void)SendDataAck(context, message, -E_NOT_PERMIT, 0); + } + return -E_NOT_PERMIT; + } + const std::vector &data = packet->GetData(); + WaterMark maxSendDataTime = SingleVerDataSyncUtils::GetMaxSendDataTime(data); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + if (version > SOFTWARE_VERSION_RELEASE_2_0 && (mode != SyncModeType::PULL) && + !context->GetReceivcPermitCheck()) { + bool permitReceive = SingleVerDataSyncUtils::CheckPermitReceiveData(context, communicateHandle_); + if (permitReceive) { + context->SetReceivcPermitCheck(true); + } else { + (void)SendDataAck(context, message, -E_SECURITY_OPTION_CHECK_ERROR, maxSendDataTime); + return -E_SECURITY_OPTION_CHECK_ERROR; + } + } + return errCode; +} + +// used in pull response +void SingleVerDataSync::SendResetWatchDogPacket(SingleVerSyncTaskContext *context, uint32_t packetLen) +{ + // When mtu less than 30k, we send data with bluetooth + // In order not to block the bluetooth channel, we don't send notify packet here + if (mtuSize_ >= packetLen || mtuSize_ < NOTIFY_MIN_MTU_SIZE) { + return; + } + uint64_t data = static_cast(packetLen) * static_cast(context->GetTimeoutTime()) / mtuSize_; + + Message *ackMessage = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + if (ackMessage == nullptr) { + LOGE("[DataSync][ResetWatchDog] new message failed"); + return; + } + + DataAckPacket ack; + ack.SetData(data); + ack.SetRecvCode(-E_SAVE_DATA_NOTIFY); + ack.SetVersion(std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT)); + int errCode = ackMessage->SetCopiedObject(ack); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + LOGE("[DataSync][ResetWatchDog] set copied object failed, errcode=%d", errCode); + return; + } + SingleVerDataSyncUtils::SetMessageHeadInfo(*ackMessage, TYPE_NOTIFY, context->GetDeviceId(), + context->GetSequenceId(), context->GetResponseSessionId()); + + errCode = Send(context, ackMessage, nullptr, 0); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + LOGE("[DataSync][ResetWatchDog] Send packet failed,errcode=%d,label=%s,dev=%s", errCode, label_.c_str(), + STR_MASK(GetDeviceId())); + } else { + LOGI("[DataSync][ResetWatchDog] data = %" PRIu64 ",label=%s,dev=%s", data, label_.c_str(), + STR_MASK(GetDeviceId())); + } +} + +int32_t SingleVerDataSync::ReSend(SingleVerSyncTaskContext *context, DataSyncReSendInfo reSendInfo) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + SyncEntry syncData; + int errCode = GetReSendData(syncData, context, reSendInfo); + if (!SingleVerDataSyncUtils::IsGetDataSuccessfully(errCode)) { + return errCode; + } + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + if (packet == nullptr) { + LOGE("[DataSync][ReSend] new DataRequestPacket error"); + return -E_OUT_OF_MEMORY; + } + FillRequestReSendPacket(context, packet, reSendInfo, syncData, errCode); + errCode = SendReSendPacket(packet, context, reSendInfo.sessionId, reSendInfo.sequenceId); + if (errCode == E_OK && SyncOperation::TransferSyncMode(context->GetMode()) != SyncModeType::PULL) { + // resend.end may not update in localwatermark while E_TIMEOUT occurred in send message last time. + SyncTimeRange dataTime {reSendInfo.start, reSendInfo.deleteDataStart, reSendInfo.end, reSendInfo.deleteDataEnd}; + if (reSendInfo.deleteDataEnd > reSendInfo.deleteDataStart && curType == SyncType::QUERY_SYNC_TYPE) { + dataTime.deleteEndTime += 1; + } + if (reSendInfo.end > reSendInfo.start) { + dataTime.endTime += 1; + } + SaveLocalWaterMark(curType, context, dataTime, true); + } + return errCode; +} + +int SingleVerDataSync::SendReSendPacket(const DataRequestPacket *packet, SingleVerSyncTaskContext *context, + uint32_t sessionId, uint32_t sequenceId) +{ + SyncType syncType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + Message *message = new (std::nothrow) Message(SingleVerDataSyncUtils::GetMessageId(syncType)); + if (message == nullptr) { + LOGE("[DataSync][SendDataPacket] new message error"); + delete packet; + packet = nullptr; + return -E_OUT_OF_MEMORY; + } + uint32_t packetLen = packet->CalculateLen(SingleVerDataSyncUtils::GetMessageId(syncType)); + int errCode = message->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete message; + message = nullptr; + LOGE("[DataSync][SendReSendPacket] SetExternalObject failed errCode=%d", errCode); + return errCode; + } + SingleVerDataSyncUtils::SetMessageHeadInfo(*message, TYPE_REQUEST, context->GetDeviceId(), sequenceId, sessionId); + CommErrHandler handler = std::bind(&SyncTaskContext::CommErrHandlerFunc, std::placeholders::_1, + context, message->GetSessionId()); + errCode = Send(context, message, handler, packetLen); + if (errCode != E_OK) { + delete message; + message = nullptr; + } + return errCode; +} + +int SingleVerDataSync::CheckPermitSendData(int inMode, SingleVerSyncTaskContext *context) +{ + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + int mode = SyncOperation::TransferSyncMode(inMode); + // for pull mode it just need to get data, no need to send data. + if (version <= SOFTWARE_VERSION_RELEASE_2_0 || mode == SyncModeType::PULL) { + return E_OK; + } + if (context->GetSendPermitCheck()) { + return E_OK; + } + bool isPermitSync = true; + std::string deviceId = context->GetDeviceId(); + SecurityOption remoteSecOption = context->GetRemoteSeccurityOption(); + if (mode == SyncModeType::PUSH || mode == SyncModeType::PUSH_AND_PULL || mode == SyncModeType::RESPONSE_PULL) { + isPermitSync = SingleVerDataSyncUtils::IsPermitRemoteDeviceRecvData(deviceId, remoteSecOption, storage_); + } + LOGI("[DataSync][PermitSendData] mode=%d,dev=%s,label=%d,flag=%d,PermitSync=%d", mode, STR_MASK(deviceId_), + remoteSecOption.securityLabel, remoteSecOption.securityFlag, isPermitSync); + if (isPermitSync) { + context->SetSendPermitCheck(true); + return E_OK; + } + if (mode == SyncModeType::PUSH || mode == SyncModeType::PUSH_AND_PULL) { + context->SetTaskErrCode(-E_SECURITY_OPTION_CHECK_ERROR); + return -E_SECURITY_OPTION_CHECK_ERROR; + } + if (mode == SyncModeType::RESPONSE_PULL) { + SyncEntry syncData; + SendPullResponseDataPkt(-E_SECURITY_OPTION_CHECK_ERROR, syncData, context); + return -E_SECURITY_OPTION_CHECK_ERROR; + } + if (mode == SyncModeType::SUBSCRIBE_QUERY) { + return -E_SECURITY_OPTION_CHECK_ERROR; + } + return E_OK; +} + +std::string SingleVerDataSync::GetLabel() const +{ + return label_; +} + +std::string SingleVerDataSync::GetDeviceId() const +{ + return deviceId_; +} + +bool SingleVerDataSync::WaterMarkErrHandle(SyncType syncType, SingleVerSyncTaskContext *context, const Message *message) +{ + const DataRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + LOGE("[WaterMarkErrHandle] get packet object failed"); + return -E_INVALID_ARGS; + } + WaterMark packetLocalMark = packet->GetLocalWaterMark(); + WaterMark packetDeletedMark = packet->GetDeletedWaterMark(); + WaterMark peerMark = 0; + WaterMark deletedMark = 0; + GetPeerWaterMark(syncType, packet->GetQueryId(), context->GetDeviceId(), peerMark); + if (syncType == SyncType::QUERY_SYNC_TYPE) { + GetPeerDeleteSyncWaterMark(context->GetDeleteSyncId(), deletedMark); + } + if (syncType != SyncType::QUERY_SYNC_TYPE && packetLocalMark > peerMark) { + LOGI("[DataSync][DataRequestRecv] packetLocalMark=%" PRIu64 ",current=%" PRIu64, packetLocalMark, peerMark); + context->SetReceiveWaterMarkErr(true); + SendDataAck(context, message, LOCAL_WATER_MARK_NOT_INIT, 0); + return true; + } + if (syncType == SyncType::QUERY_SYNC_TYPE && (packetLocalMark > peerMark || packetDeletedMark > deletedMark)) { + LOGI("[DataSync][DataRequestRecv] packetDeletedMark=%" PRIu64 ",deletedMark=%" PRIu64 "," + "packetLocalMark=%" PRIu64 ",peerMark=%" PRIu64, packetDeletedMark, deletedMark, packetLocalMark, + peerMark); + context->SetReceiveWaterMarkErr(true); + SendDataAck(context, message, LOCAL_WATER_MARK_NOT_INIT, 0); + return true; + } + return false; +} + +int SingleVerDataSync::CheckSchemaStrategy(SingleVerSyncTaskContext *context, const Message *message) +{ + auto *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + auto query = packet->GetQuery(); + SyncStrategy localStrategy = context->GetSyncStrategy(query); + if (!context->GetIsSchemaSync()) { + LOGE("[DataSync][CheckSchemaStrategy] isSchemaSync=%d check failed", context->GetIsSchemaSync()); + (void)SendDataAck(context, message, -E_NEED_ABILITY_SYNC, 0); + return -E_NEED_ABILITY_SYNC; + } + if (!localStrategy.permitSync) { + LOGE("[DataSync][CheckSchemaStrategy] Strategy permitSync=%d check failed", localStrategy.permitSync); + (void)SendDataAck(context, message, -E_SCHEMA_MISMATCH, 0); + return -E_SCHEMA_MISMATCH; + } + return E_OK; +} + +void SingleVerDataSync::RemotePushFinished(int sendCode, int inMode, uint32_t msgSessionId, uint32_t contextSessionId) +{ + int mode = SyncOperation::TransferSyncMode(inMode); + if ((mode != SyncModeType::PUSH) && (mode != SyncModeType::PUSH_AND_PULL) && (mode != SyncModeType::QUERY_PUSH) && + (mode != SyncModeType::QUERY_PUSH_PULL)) { + return; + } + + if ((sendCode == E_OK) && (msgSessionId != 0) && (msgSessionId != contextSessionId)) { + storage_->NotifyRemotePushFinished(deviceId_); + } +} + +void SingleVerDataSync::SetAckPacket(DataAckPacket &ackPacket, SingleVerSyncTaskContext *context, + const DataRequestPacket *packet, int32_t recvCode, WaterMark maxSendDataTime) +{ + SyncType curType = SyncOperation::GetSyncType(packet->GetMode()); + WaterMark localMark = 0; + GetLocalWaterMark(curType, packet->GetQueryId(), context, localMark); + ackPacket.SetRecvCode(recvCode); + // send ack packet + if ((recvCode == E_OK) && (maxSendDataTime != 0)) { + ackPacket.SetData(maxSendDataTime + 1); // + 1 to next start + } else if (recvCode != WATER_MARK_INVALID) { + WaterMark mark = 0; + GetPeerWaterMark(curType, packet->GetQueryId(), context->GetDeviceId(), mark); + ackPacket.SetData(mark); + } + std::vector reserved {localMark}; + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + uint64_t packetId = 0; + if (version > SOFTWARE_VERSION_RELEASE_2_0) { + packetId = packet->GetPacketId(); // above 102 version data request reserve[0] store packetId value + } + if (version > SOFTWARE_VERSION_RELEASE_2_0 && packetId > 0) { + reserved.push_back(packetId); + } + // while recv is not E_OK, data is peerMark, reserve[2] is deletedPeerMark value + if (curType == SyncType::QUERY_SYNC_TYPE && recvCode != WATER_MARK_INVALID) { + WaterMark deletedPeerMark; + GetPeerDeleteSyncWaterMark(context->GetDeleteSyncId(), deletedPeerMark); + reserved.push_back(deletedPeerMark); // query sync mode, reserve[2] store deletedPeerMark value + } + ackPacket.SetReserved(reserved); + ackPacket.SetVersion(version); +} + +int SingleVerDataSync::GetReSendData(SyncEntry &syncData, SingleVerSyncTaskContext *context, + DataSyncReSendInfo reSendInfo) +{ + int mode = SyncOperation::TransferSyncMode(context->GetMode()); + if (mode == SyncModeType::PULL) { + return E_OK; + } + ContinueToken token = nullptr; + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + size_t packetSize = (version > SOFTWARE_VERSION_RELEASE_2_0) ? + DBConstant::MAX_HPMODE_PACK_ITEM_SIZE : DBConstant::MAX_NORMAL_PACK_ITEM_SIZE; + DataSizeSpecInfo reSendDataSizeInfo = GetDataSizeSpecInfo(packetSize); + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + int errCode; + if (curType != SyncType::QUERY_SYNC_TYPE) { + errCode = storage_->GetSyncData(reSendInfo.start, reSendInfo.end + 1, syncData.entries, token, + reSendDataSizeInfo); + } else { + QuerySyncObject queryObj = context->GetQuery(); + errCode = storage_->GetSyncData(queryObj, SyncTimeRange { reSendInfo.start, reSendInfo.deleteDataStart, + reSendInfo.end + 1, reSendInfo.deleteDataEnd + 1 }, reSendDataSizeInfo, token, syncData.entries); + } + if (token != nullptr) { + storage_->ReleaseContinueToken(token); + } + if (errCode == -E_BUSY || errCode == -E_EKEYREVOKED) { + context->SetTaskErrCode(errCode); + return errCode; + } + if (!SingleVerDataSyncUtils::IsGetDataSuccessfully(errCode)) { + return errCode; + } + + int innerCode = InterceptData(syncData); + if (innerCode != E_OK) { + context->SetTaskErrCode(innerCode); + return innerCode; + } + + bool needCompressOnSync = false; + uint8_t compressionRate = DBConstant::DEFAULT_COMPTRESS_RATE; + (void)storage_->GetCompressionOption(needCompressOnSync, compressionRate); + CompressAlgorithm remoteAlgo = context->ChooseCompressAlgo(); + if (needCompressOnSync && remoteAlgo != CompressAlgorithm::NONE) { + int compressCode = GenericSingleVerKvEntry::Compress(syncData.entries, syncData.compressedEntries, + { remoteAlgo, version }); + if (compressCode != E_OK) { + return compressCode; + } + } + return errCode; +} + +int SingleVerDataSync::RemoveDeviceDataIfNeed(SingleVerSyncTaskContext *context) +{ + if (context->GetRemoteSoftwareVersion() <= SOFTWARE_VERSION_RELEASE_3_0) { + return E_OK; + } + uint64_t clearRemoteDataMark = 0; + std::lock_guard autoLock(removeDeviceDataLock_); + metadata_->GetRemoveDataMark(context->GetDeviceId(), clearRemoteDataMark); + if (clearRemoteDataMark == 0) { + return E_OK; + } + int errCode = E_OK; + if (context->IsNeedClearRemoteStaleData() && clearRemoteDataMark == REMOVE_DEVICE_DATA_MARK) { + errCode = storage_->RemoveDeviceData(context->GetDeviceId(), true); + if (errCode != E_OK) { + LOGE("clear remote %s data failed,errCode=%d", STR_MASK(GetDeviceId()), errCode); + return errCode; + } + } + if (clearRemoteDataMark == REMOVE_DEVICE_DATA_MARK) { + errCode = metadata_->ResetMetaDataAfterRemoveData(context->GetDeviceId()); + if (errCode != E_OK) { + LOGE("set %s removeDataWaterMark to false failed,errCode=%d", STR_MASK(GetDeviceId()), errCode); + return errCode; + } + } + return E_OK; +} + +void SingleVerDataSync::UpdateMtuSize() +{ + mtuSize_ = communicateHandle_->GetCommunicatorMtuSize(deviceId_) * 9 / 10; // get the 9/10 of the size +} + +void SingleVerDataSync::FillRequestReSendPacket(const SingleVerSyncTaskContext *context, DataRequestPacket *packet, + DataSyncReSendInfo reSendInfo, SyncEntry &syncData, int sendCode) +{ + SyncType curType = (context->IsQuerySync()) ? SyncType::QUERY_SYNC_TYPE : SyncType::MANUAL_FULL_SYNC_TYPE; + WaterMark peerMark = 0; + GetPeerWaterMark(curType, context->GetQuerySyncId(), context->GetDeviceId(), + peerMark); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + // transfer reSend mode, RESPONSE_PULL transfer to push or query push + // PUSH_AND_PULL mode which sequenceId lager than first transfer to push or query push + int reSendMode = SingleVerDataSyncUtils::GetReSendMode(context->GetMode(), reSendInfo.sequenceId, curType); + if (GetSessionEndTimestamp() == std::max(reSendInfo.end, reSendInfo.deleteDataEnd) || + SyncOperation::TransferSyncMode(context->GetMode()) == SyncModeType::PULL) { + LOGI("[DataSync][ReSend] set lastid,label=%s,dev=%s", label_.c_str(), STR_MASK(GetDeviceId())); + packet->SetLastSequence(); + } + if (sendCode == E_OK && GetSessionEndTimestamp() == std::max(reSendInfo.end, reSendInfo.deleteDataEnd) && + context->GetMode() == SyncModeType::RESPONSE_PULL) { + sendCode = SEND_FINISHED; + } + packet->SetData(syncData.entries); + packet->SetCompressData(syncData.compressedEntries); + packet->SetBasicInfo(sendCode, version, reSendMode); + packet->SetWaterMark(reSendInfo.start, peerMark, reSendInfo.deleteDataStart); + if (SyncOperation::TransferSyncMode(reSendMode) != SyncModeType::PUSH) { + packet->SetEndWaterMark(context->GetEndMark()); + packet->SetQuery(context->GetQuery()); + } + packet->SetQueryId(context->GetQuerySyncId()); + if (version > SOFTWARE_VERSION_RELEASE_2_0) { + std::vector reserved {reSendInfo.packetId}; + packet->SetReserved(reserved); + } + bool needCompressOnSync = false; + uint8_t compressionRate = DBConstant::DEFAULT_COMPTRESS_RATE; + (void)storage_->GetCompressionOption(needCompressOnSync, compressionRate); + CompressAlgorithm curAlgo = context->ChooseCompressAlgo(); + if (needCompressOnSync && curAlgo != CompressAlgorithm::NONE) { + packet->SetCompressDataMark(); + packet->SetCompressAlgo(curAlgo); + } +} + +DataSizeSpecInfo SingleVerDataSync::GetDataSizeSpecInfo(size_t packetSize) +{ + bool needCompressOnSync = false; + uint8_t compressionRate = DBConstant::DEFAULT_COMPTRESS_RATE; + (void)storage_->GetCompressionOption(needCompressOnSync, compressionRate); + uint32_t blockSize = std::min(static_cast(DBConstant::MAX_SYNC_BLOCK_SIZE), + mtuSize_ * 100 / compressionRate); // compressionRate max is 100 + return {blockSize, packetSize}; +} + +int SingleVerDataSync::InterceptData(SyncEntry &syncEntry) +{ + if (storage_ == nullptr) { + LOGE("Invalid DB. Can not intercept data."); + return -E_INVALID_DB; + } + + // GetLocalDeviceName get local device ID. + // GetDeviceId get remote device ID. + // If intercept data fail, entries will be released. + return storage_->InterceptData(syncEntry.entries, GetLocalDeviceName(), GetDeviceId()); +} + +int SingleVerDataSync::ControlCmdStart(SingleVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + std::shared_ptr subManager = context->GetSubscribeManager(); + if (subManager == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = ControlCmdStartCheck(context); + if (errCode != E_OK) { + return errCode; + } + ControlRequestPacket* packet = new (std::nothrow) SubscribeRequest(); + if (packet == nullptr) { + LOGE("[DataSync][ControlCmdStart] new SubscribeRequest error"); + return -E_OUT_OF_MEMORY; + } + if (context->GetMode() == SyncModeType::SUBSCRIBE_QUERY) { + errCode = subManager->ReserveLocalSubscribeQuery(context->GetDeviceId(), context->GetQuery()); + if (errCode != E_OK) { + LOGE("[DataSync][ControlCmdStart] reserve local subscribe query failed,err=%d", errCode); + delete packet; + packet = nullptr; + return errCode; + } + } + SingleVerDataSyncUtils::FillControlRequestPacket(packet, context); + errCode = SendControlPacket(packet, context); + if (errCode != E_OK && context->GetMode() == SyncModeType::SUBSCRIBE_QUERY) { + subManager->DeleteLocalSubscribeQuery(context->GetDeviceId(), context->GetQuery()); + } + return errCode; +} + +int SingleVerDataSync::ControlCmdRequestRecv(SingleVerSyncTaskContext *context, const Message *message) +{ + const ControlRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + LOGI("[SingleVerDataSync] recv control cmd message,label=%s,dev=%s,controlType=%u", label_.c_str(), + STR_MASK(GetDeviceId()), packet->GetcontrolCmdType()); + int errCode = ControlCmdRequestRecvPre(context, message); + if (errCode != E_OK) { + return errCode; + } + if (packet->GetcontrolCmdType() == ControlCmdType::SUBSCRIBE_QUERY_CMD) { + errCode = SubscribeRequestRecv(context, message); + } else if (packet->GetcontrolCmdType() == ControlCmdType::UNSUBSCRIBE_QUERY_CMD) { + errCode = UnsubscribeRequestRecv(context, message); + } + return errCode; +} + +int SingleVerDataSync::ControlCmdAckRecv(SingleVerSyncTaskContext *context, const Message *message) +{ + std::shared_ptr subManager = context->GetSubscribeManager(); + if (subManager == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = SingleVerDataSyncUtils::AckMsgErrnoCheck(context, message); + if (errCode != E_OK) { + SingleVerDataSyncUtils::ControlAckErrorHandle(context, subManager); + return errCode; + } + const ControlAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + int32_t recvCode = packet->GetRecvCode(); + uint32_t cmdType = packet->GetcontrolCmdType(); + if (recvCode != E_OK) { + LOGE("[DataSync][AckRecv] control sync abort,recvCode=%d,label=%s,dev=%s,type=%u", recvCode, label_.c_str(), + STR_MASK(GetDeviceId()), cmdType); + // for unsubscribe no need to do something + SingleVerDataSyncUtils::ControlAckErrorHandle(context, subManager); + return recvCode; + } + if (cmdType == ControlCmdType::SUBSCRIBE_QUERY_CMD) { + errCode = subManager->ActiveLocalSubscribeQuery(context->GetDeviceId(), context->GetQuery()); + } else if (cmdType == ControlCmdType::UNSUBSCRIBE_QUERY_CMD) { + subManager->RemoveLocalSubscribeQuery(context->GetDeviceId(), context->GetQuery()); + } + if (errCode != E_OK) { + LOGE("[DataSync] ack handle failed,label =%s,dev=%s,type=%u", label_.c_str(), STR_MASK(GetDeviceId()), cmdType); + return errCode; + } + return -E_NO_DATA_SEND; // means control msg send finished +} + +int SingleVerDataSync::ControlCmdStartCheck(SingleVerSyncTaskContext *context) +{ + if ((context->GetMode() != SyncModeType::SUBSCRIBE_QUERY) && + (context->GetMode() != SyncModeType::UNSUBSCRIBE_QUERY)) { + LOGE("[ControlCmdStartCheck] not support controlCmd"); + return -E_INVALID_ARGS; + } + if (context->GetMode() == SyncModeType::SUBSCRIBE_QUERY && + context->GetQuery().HasInKeys() && + context->GetRemoteDbAbility().GetAbilityItem(SyncConfig::INKEYS_QUERY) != SUPPORT_MARK) { + return -E_NOT_SUPPORT; + } + if ((context->GetMode() != SyncModeType::SUBSCRIBE_QUERY) || context->GetReceivcPermitCheck()) { + return E_OK; + } + bool permitReceive = SingleVerDataSyncUtils::CheckPermitReceiveData(context, communicateHandle_); + if (permitReceive) { + context->SetReceivcPermitCheck(true); + } else { + return -E_SECURITY_OPTION_CHECK_ERROR; + } + return E_OK; +} + +int SingleVerDataSync::SendControlPacket(const ControlRequestPacket *packet, SingleVerSyncTaskContext *context) +{ + Message *message = new (std::nothrow) Message(CONTROL_SYNC_MESSAGE); + if (message == nullptr) { + LOGE("[DataSync][SendControlPacket] new message error"); + delete packet; + packet = nullptr; + return -E_OUT_OF_MEMORY; + } + uint32_t packetLen = packet->CalculateLen(); + int errCode = message->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete message; + message = nullptr; + LOGE("[DataSync][SendControlPacket] set external object failed errCode=%d", errCode); + return errCode; + } + SingleVerDataSyncUtils::SetMessageHeadInfo(*message, TYPE_REQUEST, context->GetDeviceId(), + context->GetSequenceId(), context->GetRequestSessionId()); + CommErrHandler handler = std::bind(&SyncTaskContext::CommErrHandlerFunc, std::placeholders::_1, + context, message->GetSessionId()); + errCode = Send(context, message, handler, packetLen); + if (errCode != E_OK) { + delete message; + message = nullptr; + } + return errCode; +} + +int SingleVerDataSync::SendControlAck(SingleVerSyncTaskContext *context, const Message *message, int32_t recvCode, + uint32_t controlCmdType, const CommErrHandler &handler) +{ + Message *ackMessage = new (std::nothrow) Message(message->GetMessageId()); + if (ackMessage == nullptr) { + LOGE("[DataSync][SendControlAck] new message error"); + return -E_OUT_OF_MEMORY; + } + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + ControlAckPacket ack; + ack.SetPacketHead(recvCode, version, static_cast(controlCmdType), 0); + int errCode = ackMessage->SetCopiedObject(ack); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + LOGE("[DataSync][SendControlAck] set copied object failed, errcode=%d", errCode); + return errCode; + } + SingleVerDataSyncUtils::SetMessageHeadInfo(*ackMessage, TYPE_RESPONSE, context->GetDeviceId(), + message->GetSequenceId(), message->GetSessionId()); + errCode = Send(context, ackMessage, handler, 0); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + } + return errCode; +} + +int SingleVerDataSync::ControlCmdRequestRecvPre(SingleVerSyncTaskContext *context, const Message *message) +{ + if (context == nullptr || message == nullptr) { + return -E_INVALID_ARGS; + } + const ControlRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + uint32_t controlCmdType = packet->GetcontrolCmdType(); + if (context->GetRemoteSoftwareVersion() <= SOFTWARE_VERSION_BASE) { + return DoAbilitySyncIfNeed(context, message, true); + } + if (controlCmdType >= ControlCmdType::INVALID_CONTROL_CMD) { + SendControlAck(context, message, -E_NOT_SUPPORT, controlCmdType); + return -E_WAIT_NEXT_MESSAGE; + } + return E_OK; +} + +int SingleVerDataSync::SubscribeRequestRecvPre(SingleVerSyncTaskContext *context, const SubscribeRequest *packet, + const Message *message) +{ + uint32_t controlCmdType = packet->GetcontrolCmdType(); + if (controlCmdType != ControlCmdType::SUBSCRIBE_QUERY_CMD) { + return E_OK; + } + QuerySyncObject syncQuery = packet->GetQuery(); + int errCode; + if (!packet->IsAutoSubscribe()) { + errCode = storage_->CheckAndInitQueryCondition(syncQuery); + if (errCode != E_OK) { + LOGE("[SingleVerDataSync] check sync query failed,errCode=%d", errCode); + SendControlAck(context, message, errCode, controlCmdType); + return -E_WAIT_NEXT_MESSAGE; + } + } + int mode = SingleVerDataSyncUtils::GetModeByControlCmdType( + static_cast(packet->GetcontrolCmdType())); + if (mode >= SyncModeType::INVALID_MODE) { + LOGE("[SingleVerDataSync] invalid mode"); + SendControlAck(context, message, -E_INVALID_ARGS, controlCmdType); + return -E_WAIT_NEXT_MESSAGE; + } + errCode = CheckPermitSendData(mode, context); + if (errCode != E_OK) { + LOGE("[SingleVerDataSync] check sync query failed,errCode=%d", errCode); + SendControlAck(context, message, errCode, controlCmdType); + } + return errCode; +} + +int SingleVerDataSync::SubscribeRequestRecv(SingleVerSyncTaskContext *context, const Message *message) +{ + const SubscribeRequest *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = SubscribeRequestRecvPre(context, packet, message); + if (errCode != E_OK) { + return errCode; + } + uint32_t controlCmdType = packet->GetcontrolCmdType(); + std::shared_ptr subscribeManager = context->GetSubscribeManager(); + if (subscribeManager == nullptr) { + LOGE("[SingleVerDataSync] subscribeManager check failed"); + SendControlAck(context, message, -E_NOT_REGISTER, controlCmdType); + return -E_INVALID_ARGS; + } + errCode = storage_->AddSubscribe(packet->GetQuery().GetIdentify(), packet->GetQuery(), packet->IsAutoSubscribe()); + if (errCode != E_OK) { + LOGE("[SingleVerDataSync] add trigger failed,err=%d,label=%s,dev=%s", errCode, label_.c_str(), + STR_MASK(GetDeviceId())); + SendControlAck(context, message, errCode, controlCmdType); + return errCode; + } + errCode = subscribeManager->ReserveRemoteSubscribeQuery(context->GetDeviceId(), packet->GetQuery()); + if (errCode != E_OK) { + LOGE("[SingleVerDataSync] add remote subscribe query failed,err=%d,label=%s,dev=%s", errCode, label_.c_str(), + STR_MASK(GetDeviceId())); + storage_->RemoveSubscribe(packet->GetQuery().GetIdentify()); + SendControlAck(context, message, errCode, controlCmdType); + return errCode; + } + errCode = SendControlAck(context, message, E_OK, controlCmdType); + if (errCode != E_OK) { + storage_->RemoveSubscribe(packet->GetQuery().GetIdentify()); + subscribeManager->DeleteRemoteSubscribeQuery(context->GetDeviceId(), packet->GetQuery()); + LOGE("[SingleVerDataSync] send control msg failed,err=%d,label=%s,dev=%s", errCode, label_.c_str(), + STR_MASK(GetDeviceId())); + return errCode; + } + subscribeManager->ActiveRemoteSubscribeQuery(context->GetDeviceId(), packet->GetQuery()); + return errCode; +} + +int SingleVerDataSync::UnsubscribeRequestRecv(SingleVerSyncTaskContext *context, const Message *message) +{ + const SubscribeRequest *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + uint32_t controlCmdType = packet->GetcontrolCmdType(); + std::shared_ptr subscribeManager = context->GetSubscribeManager(); + if (subscribeManager == nullptr) { + LOGE("[SingleVerDataSync] subscribeManager check failed"); + SendControlAck(context, message, -E_NOT_REGISTER, controlCmdType); + return -E_INVALID_ARGS; + } + int errCode; + if (subscribeManager->IsRemoteContainSubscribe(context->GetDeviceId(), packet->GetQuery())) { + errCode = storage_->RemoveSubscribe(packet->GetQuery().GetIdentify()); + if (errCode != E_OK) { + LOGE("[SingleVerDataSync] remove trigger failed,err=%d,label=%s,dev=%s", errCode, label_.c_str(), + STR_MASK(GetDeviceId())); + SendControlAck(context, message, errCode, controlCmdType); + return errCode; + } + } + errCode = SendControlAck(context, message, E_OK, controlCmdType); + if (errCode != E_OK) { + LOGE("[SingleVerDataSync] send control msg failed,err=%d,label=%s,dev=%s", errCode, label_.c_str(), + STR_MASK(GetDeviceId())); + return errCode; + } + subscribeManager->RemoveRemoteSubscribeQuery(context->GetDeviceId(), packet->GetQuery()); + metadata_->RemoveQueryFromRecordSet(context->GetDeviceId(), packet->GetQuery().GetIdentify()); + return errCode; +} + +void SingleVerDataSync::PutDataMsg(Message *message) +{ + return msgSchedule_.PutMsg(message); +} + +Message *SingleVerDataSync::MoveNextDataMsg(SingleVerSyncTaskContext *context, bool &isNeedHandle, + bool &isNeedContinue) +{ + return msgSchedule_.MoveNextMsg(context, isNeedHandle, isNeedContinue); +} + +bool SingleVerDataSync::IsNeedReloadQueue() +{ + return msgSchedule_.IsNeedReloadQueue(); +} + +void SingleVerDataSync::ScheduleInfoHandle(bool isNeedHandleStatus, bool isNeedClearMap, const Message *message) +{ + msgSchedule_.ScheduleInfoHandle(isNeedHandleStatus, isNeedClearMap, message); +} + +void SingleVerDataSync::ClearDataMsg() +{ + msgSchedule_.ClearMsg(); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/single_ver_data_sync.h b/mock/distributeddb/syncer/src/single_ver_data_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..610537fa909662f371b416be2775b384886b6e5a --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_data_sync.h @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_DATA_SYNC_NEW_H +#define SINGLE_VER_DATA_SYNC_NEW_H + +#include "icommunicator.h" +#include "isync_interface.h" +#include "meta_data.h" +#include "parcel.h" +#include "single_ver_data_message_schedule.h" +#include "single_ver_data_packet.h" +#include "single_ver_kvdb_sync_interface.h" +#include "single_ver_sync_task_context.h" +#include "sync_generic_interface.h" +#include "sync_types.h" +#include "version.h" + +namespace DistributedDB { +using SendDataItem = SingleVerKvEntry *; +struct ReSendInfo { + Timestamp start = 0; + Timestamp end = 0; + Timestamp deleteBeginTime = 0; + Timestamp deleteEndTime = 0; + // packetId is used for matched ackpacket packetId which saved in ackPacket.reserve + // if equaled, means need to handle the ack, or drop. it is always increased + uint64_t packetId = 0; +}; + +struct DataSyncReSendInfo { + uint32_t sessionId = 0; + uint32_t sequenceId = 0; + Timestamp start = 0; // means normal or sync data localwatermark + Timestamp end = 0; + Timestamp deleteDataStart = 0; // means delete data localwatermark + Timestamp deleteDataEnd = 0; + uint64_t packetId = 0; +}; + +struct SyncEntry { + std::vector entries; + std::vector compressedEntries; +}; + +class SingleVerDataSync { +public: + SingleVerDataSync(); + virtual ~SingleVerDataSync(); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerDataSync); + + int Initialize(ISyncInterface *inStorage, ICommunicator *inCommunicateHandle, + std::shared_ptr &inMetadata, const std::string &deviceId); + + int SyncStart(int mode, SingleVerSyncTaskContext *context); + + int TryContinueSync(SingleVerSyncTaskContext *context, const Message *message); + + void ClearSyncStatus(); + + int PushStart(SingleVerSyncTaskContext *context); + + int PushPullStart(SingleVerSyncTaskContext *context); + + int PullRequestStart(SingleVerSyncTaskContext *context); + + int PullResponseStart(SingleVerSyncTaskContext *context); + + int DataRequestRecv(SingleVerSyncTaskContext *context, const Message *message, WaterMark &pullEndWatermark); + + bool AckPacketIdCheck(const Message *message); + + int AckRecv(SingleVerSyncTaskContext *context, const Message *message); + + void SendSaveDataNotifyPacket(SingleVerSyncTaskContext *context, uint32_t pktVersion, uint32_t sessionId, + uint32_t sequenceId, uint32_t inMsgId); + + virtual int SendDataAck(SingleVerSyncTaskContext *context, const Message *message, int32_t recvCode, + WaterMark maxSendDataTime); + + int CheckPermitSendData(int inMode, SingleVerSyncTaskContext *context); + + std::string GetLabel() const; + + std::string GetDeviceId() const; + + bool WaterMarkErrHandle(SyncType syncType, SingleVerSyncTaskContext *context, const Message *message); + + int ControlCmdStart(SingleVerSyncTaskContext *context); + + int ControlCmdRequestRecv(SingleVerSyncTaskContext *context, const Message *message); + + int ControlCmdAckRecv(SingleVerSyncTaskContext *context, const Message *message); + + void PutDataMsg(Message *message); + + Message *MoveNextDataMsg(SingleVerSyncTaskContext *context, bool &isNeedHandle, bool &isNeedContinue); + + bool IsNeedReloadQueue(); + + void SendFinishedDataAck(SingleVerSyncTaskContext *context, const Message *message); + + void ScheduleInfoHandle(bool isNeedHandleStatus, bool isNeedClearMap, const Message *message); + + void ClearDataMsg(); + +protected: + static const int SEND_FINISHED = 0xff; + static const int LOCAL_WATER_MARK_NOT_INIT = 0xaa; + static const int PEER_WATER_MARK_NOT_INIT = 0x55; + static const int WATER_MARK_INVALID = 0xbb; + static const int MTU_SIZE = 28311552; // 27MB + + void ResetSyncStatus(int inMode, SingleVerSyncTaskContext *context); + + int InnerSyncStart(SingleVerSyncTaskContext *context); + + void InnerClearSyncStatus(); + + int ReSendData(SingleVerSyncTaskContext *context); + + int32_t ReSend(SingleVerSyncTaskContext *context, DataSyncReSendInfo reSendInfo); + + void SetSessionEndTimestamp(Timestamp end); + + Timestamp GetSessionEndTimestamp() const; + + void FillDataRequestPacket(DataRequestPacket *packet, SingleVerSyncTaskContext *context, + SyncEntry &syncData, int sendCode, int mode); + + int RequestStart(SingleVerSyncTaskContext *context, int mode); + + SyncTimeRange GetSyncDataTimeRange(SyncType syncType, SingleVerSyncTaskContext *context, + const std::vector &inData, UpdateWaterMark &isUpdate); + + int GetData(SingleVerSyncTaskContext *context, std::vector &outData, size_t packetSize); + + int GetDataWithPerformanceRecord(SingleVerSyncTaskContext *context, SyncEntry &syncOutData); + + int Send(SingleVerSyncTaskContext *context, const Message *message, const CommErrHandler &handler, + uint32_t packetLen); + + int GetUnsyncData(SingleVerSyncTaskContext *context, std::vector &outData, size_t packetSize); + + int GetNextUnsyncData(SingleVerSyncTaskContext *context, std::vector &outData, size_t packetSize); + + int SaveData(const SingleVerSyncTaskContext *context, const std::vector &inData, SyncType curType, + const QuerySyncObject &query); + + int SaveLocalWaterMark(SyncType syncType, const SingleVerSyncTaskContext *context, + SyncTimeRange dataTimeRange, bool isCheckBeforUpdate = false) const; + + void GetLocalWaterMark(SyncType syncType, const std::string &queryIdentify, const SingleVerSyncTaskContext *context, + WaterMark &watermark) const; + + void GetPeerWaterMark(SyncType syncType, const std::string &queryIdentify, const DeviceID &deviceId, + WaterMark &watermark) const; + + void GetPeerDeleteSyncWaterMark(const DeviceID &deviceId, WaterMark &waterMark); + + void GetLocalDeleteSyncWaterMark(const SingleVerSyncTaskContext *context, WaterMark &waterMark) const; + + int RemoveDeviceDataHandle(SingleVerSyncTaskContext *context, const Message *message, WaterMark maxSendDataTime); + + int DealRemoveDeviceDataByAck(SingleVerSyncTaskContext *context, WaterMark ackWaterMark, + const std::vector &reserved); + + int SendDataPacket(SyncType syncType, const DataRequestPacket *packet, SingleVerSyncTaskContext *context); + + void UpdateQueryPeerWaterMark(SyncType syncType, const std::string &queryId, SyncTimeRange &dataTime, + const SingleVerSyncTaskContext *context, UpdateWaterMark isUpdateWaterMark); + + void UpdatePeerWaterMark(SyncType syncType, const std::string &queryId, const SingleVerSyncTaskContext *context, + WaterMark peerWatermark, WaterMark peerDeletedWatermark); + + std::string GetLocalDeviceName(); + + int DoAbilitySyncIfNeed(SingleVerSyncTaskContext *context, const Message *message, bool isControlMsg = false); + + int DataRequestRecvPre(SingleVerSyncTaskContext *context, const Message *message); + + void GetPullEndWatermark(const SingleVerSyncTaskContext *context, const DataRequestPacket *packet, + WaterMark &pullEndWatermark) const; + + int DealWaterMarkException(SingleVerSyncTaskContext *context, WaterMark ackWaterMark, + const std::vector &reserved); + + int RunPermissionCheck(SingleVerSyncTaskContext *context, const Message *message, + const DataRequestPacket *packet); + + void SendResetWatchDogPacket(SingleVerSyncTaskContext *context, uint32_t packetLen); + + int SendReSendPacket(const DataRequestPacket *packet, SingleVerSyncTaskContext *context, + uint32_t sessionId, uint32_t sequenceId); + + int SendPullResponseDataPkt(int ackCode, SyncEntry &syncOutData, SingleVerSyncTaskContext *context); + + int CheckSchemaStrategy(SingleVerSyncTaskContext *context, const Message *message); + + void RemotePushFinished(int sendCode, int inMode, uint32_t msgSessionId, uint32_t contextSessionId); + + void SetAckPacket(DataAckPacket &ackPacket, SingleVerSyncTaskContext *context, const DataRequestPacket *packet, + int32_t recvCode, WaterMark maxSendDataTime); + + int GetReSendData(SyncEntry &syncData, SingleVerSyncTaskContext *context, + DataSyncReSendInfo reSendInfo); + + virtual int RemoveDeviceDataIfNeed(SingleVerSyncTaskContext *context); + + virtual void UpdateSendInfo(SyncTimeRange dataTimeRange, SingleVerSyncTaskContext *context); + + void FillRequestReSendPacket(const SingleVerSyncTaskContext *context, DataRequestPacket *packet, + DataSyncReSendInfo reSendInfo, SyncEntry &syncData, int sendCode); + + void UpdateMtuSize(); + + DataSizeSpecInfo GetDataSizeSpecInfo(size_t packetSize); + + int InterceptData(SyncEntry &syncEntry); + + int ControlCmdStartCheck(SingleVerSyncTaskContext *context); + + int SendControlPacket(const ControlRequestPacket *packet, SingleVerSyncTaskContext *context); + + int ControlCmdRequestRecvPre(SingleVerSyncTaskContext *context, const Message *message); + int SubscribeRequestRecvPre(SingleVerSyncTaskContext *context, const SubscribeRequest *packet, + const Message *message); + int SubscribeRequestRecv(SingleVerSyncTaskContext *context, const Message *message); + int UnsubscribeRequestRecv(SingleVerSyncTaskContext *context, const Message *message); + int SendControlAck(SingleVerSyncTaskContext *context, const Message *message, int32_t recvCode, + uint32_t controlCmdType, const CommErrHandler &handler = nullptr); + + uint32_t mtuSize_; + SyncGenericInterface* storage_; + ICommunicator* communicateHandle_; + std::shared_ptr metadata_; + std::string label_; + std::string deviceId_; + + SingleVerDataMessageSchedule msgSchedule_; + + static const int HIGH_VERSION_WINDOW_SIZE = 3; + static const int LOW_VERSION_WINDOW_SIZE = 1; + // below param is about sliding sync info, is different from every sync task + std::mutex lock_; + int mode_ = 0; // sync mode, may diff from context mode if trigger pull_response while push finish + uint32_t sessionId_ = 0; + // sequenceId as key + std::map reSendMap_; + // remaining sending window + int32_t windowSize_ = 0; + // max sequenceId has been sent + uint32_t maxSequenceIdHasSent_ = 0; + bool isAllDataHasSent_ = false; + // in a sync session, the last data timestamp + Timestamp sessionEndTimestamp_ = 0; + + std::mutex removeDeviceDataLock_; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_DATA_SYNC_NEW_H diff --git a/mock/distributeddb/syncer/src/single_ver_data_sync_utils.cpp b/mock/distributeddb/syncer/src/single_ver_data_sync_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a2bc1d620b6d3cca2bced63ec6ce43912d56d5fa --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_data_sync_utils.cpp @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "single_ver_data_sync_utils.h" + +#include +#include "db_common.h" +#include "version.h" +#include "log_print.h" +#include "message.h" +namespace DistributedDB { +bool SingleVerDataSyncUtils::QuerySyncCheck(const SingleVerSyncTaskContext *context) +{ + if (!context->IsQuerySync()) { + return true; + } + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + // for 101 version, no need to do abilitySync, just send request to remote + if (version <= SOFTWARE_VERSION_RELEASE_1_0) { + return true; + } + if (version < SOFTWARE_VERSION_RELEASE_4_0) { + LOGE("[SingleVerDataSync] not support query sync when remote ver lower than 104"); + return false; + } + if (version < SOFTWARE_VERSION_RELEASE_5_0 && !(context->GetQuery().IsQueryOnlyByKey())) { + LOGE("[SingleVerDataSync] remote version only support prefix key"); + return false; + } + if (context->GetQuery().HasInKeys() && + context->GetRemoteDbAbility().GetAbilityItem(SyncConfig::INKEYS_QUERY) != SUPPORT_MARK) { + return false; + } + return true; +} + +int SingleVerDataSyncUtils::AckMsgErrnoCheck(const SingleVerSyncTaskContext *context, const Message *message) +{ + if (context == nullptr || message == nullptr) { + return -E_INVALID_ARGS; + } + if (message->IsFeedbackError()) { + LOGE("[DataSync][AckMsgErrnoCheck] message errNo=%d", message->GetErrorNo()); + return -static_cast(message->GetErrorNo()); + } + return E_OK; +} + +int SingleVerDataSyncUtils::RequestQueryCheck(const DataRequestPacket *packet, SyncGenericInterface *storage) +{ + if (storage == nullptr || packet == nullptr) { + return -E_INVALID_ARGS; + } + if (SyncOperation::GetSyncType(packet->GetMode()) != SyncType::QUERY_SYNC_TYPE) { + return E_OK; + } + QuerySyncObject syncQuery = packet->GetQuery(); + int errCode = storage->CheckAndInitQueryCondition(syncQuery); + if (errCode != E_OK) { + LOGE("[SingleVerDataSync] check sync query failed,errCode=%d", errCode); + return errCode; + } + return E_OK; +} + +bool SingleVerDataSyncUtils::IsPermitLocalDeviceRecvData(const std::string &deviceId, + const SecurityOption &remoteSecOption) +{ + return RuntimeContext::GetInstance()->CheckDeviceSecurityAbility(deviceId, remoteSecOption); +} + +bool SingleVerDataSyncUtils::IsPermitRemoteDeviceRecvData(const std::string &deviceId, + const SecurityOption &remoteSecOption, SyncGenericInterface *storage) +{ + if (storage == nullptr) { + return -E_INVALID_ARGS; + } + SecurityOption localSecOption; + if (remoteSecOption.securityLabel == NOT_SURPPORT_SEC_CLASSIFICATION) { + return true; + } + int errCode = storage->GetSecurityOption(localSecOption); + if (errCode == -E_NOT_SUPPORT) { + return true; + } + return RuntimeContext::GetInstance()->CheckDeviceSecurityAbility(deviceId, localSecOption); +} + +void SingleVerDataSyncUtils::TransDbDataItemToSendDataItem(const std::string &localHashName, + std::vector &outData) +{ + for (size_t i = 0; i < outData.size(); i++) { + if (outData[i] == nullptr) { + continue; + } + outData[i]->SetOrigDevice(outData[i]->GetOrigDevice().empty() ? localHashName : outData[i]->GetOrigDevice()); + if (i == 0 || i == (outData.size() - 1)) { + LOGD("[DataSync][TransToSendItem] printData packet=%zu,timestamp=%" PRIu64 ",flag=%" PRIu64, i, + outData[i]->GetTimestamp(), outData[i]->GetFlag()); + } + } +} + +std::string SingleVerDataSyncUtils::TransferForeignOrigDevName(const std::string &deviceName, + const std::string &localHashName) +{ + if (localHashName == deviceName) { + return ""; + } + return deviceName; +} + +void SingleVerDataSyncUtils::TransSendDataItemToLocal(const SingleVerSyncTaskContext *context, + const std::string &localHashName, const std::vector &data) +{ + TimeOffset offset = context->GetTimeOffset(); + Timestamp currentLocalTime = context->GetCurrentLocalTime(); + for (auto &item : data) { + if (item == nullptr) { + continue; + } + item->SetOrigDevice(TransferForeignOrigDevName(item->GetOrigDevice(), localHashName)); + Timestamp tempTimestamp = item->GetTimestamp(); + Timestamp tempWriteTimestamp = item->GetWriteTimestamp(); + item->SetTimestamp(tempTimestamp - static_cast(offset)); + if (tempWriteTimestamp != 0) { + item->SetWriteTimestamp(tempWriteTimestamp - static_cast(offset)); + } + + if (item->GetTimestamp() > currentLocalTime) { + item->SetTimestamp(currentLocalTime); + } + if (item->GetWriteTimestamp() > currentLocalTime) { + item->SetWriteTimestamp(currentLocalTime); + } + } +} + +void SingleVerDataSyncUtils::TranslateErrCodeIfNeed(int mode, uint32_t version, int &errCode) +{ + // once get data occur E_EKEYREVOKED error, should also send request to remote dev to pull data. + if (SyncOperation::TransferSyncMode(mode) == SyncModeType::PUSH_AND_PULL && + version > SOFTWARE_VERSION_RELEASE_2_0 && errCode == -E_EKEYREVOKED) { + errCode = E_OK; + } +} + +int SingleVerDataSyncUtils::RunPermissionCheck(SingleVerSyncTaskContext *context, const SyncGenericInterface* storage, + const std::string &label, int mode) +{ + std::string appId = storage->GetDbProperties().GetStringProp(KvDBProperties::APP_ID, ""); + std::string userId = storage->GetDbProperties().GetStringProp(KvDBProperties::USER_ID, ""); + std::string storeId = storage->GetDbProperties().GetStringProp(KvDBProperties::STORE_ID, ""); + uint8_t flag; + switch (mode) { + case SyncModeType::PUSH: + flag = CHECK_FLAG_RECEIVE; + break; + case SyncModeType::PULL: + flag = CHECK_FLAG_SEND; + break; + case SyncModeType::PUSH_AND_PULL: + flag = CHECK_FLAG_SEND | CHECK_FLAG_RECEIVE; + break; + default: + flag = CHECK_FLAG_RECEIVE; + break; + } + int errCode = E_OK; + if (storage->GetInterfaceType() != ISyncInterface::SYNC_RELATION) { + errCode = RuntimeContext::GetInstance()->RunPermissionCheck(userId, appId, storeId, context->GetDeviceId(), + flag); + } + if (errCode != E_OK) { + LOGE("[DataSync][RunPermissionCheck] check failed flag=%" PRIu8 ",Label=%s,dev=%s", flag, label.c_str(), + STR_MASK(context->GetDeviceId())); + } + return errCode; +} + +bool SingleVerDataSyncUtils::CheckPermitReceiveData(const SingleVerSyncTaskContext *context, + const ICommunicator *communicator) +{ + SecurityOption remoteSecOption = context->GetRemoteSeccurityOption(); + std::string localDeviceId; + if (communicator == nullptr || remoteSecOption.securityLabel == NOT_SURPPORT_SEC_CLASSIFICATION) { + return true; + } + communicator->GetLocalIdentity(localDeviceId); + bool isPermitSync = SingleVerDataSyncUtils::IsPermitLocalDeviceRecvData(localDeviceId, remoteSecOption); + if (isPermitSync) { + return isPermitSync; + } + LOGE("[DataSync][PermitReceiveData] check failed: permitReceive=%d, localDev=%s, seclabel=%d, secflag=%d", + isPermitSync, STR_MASK(localDeviceId), remoteSecOption.securityLabel, remoteSecOption.securityFlag); + return isPermitSync; +} + +void SingleVerDataSyncUtils::SetPacketId(DataRequestPacket *packet, SingleVerSyncTaskContext *context, uint32_t version) +{ + if (version > SOFTWARE_VERSION_RELEASE_2_0) { + context->IncPacketId(); // begin from 1 + std::vector reserved {context->GetPacketId()}; + packet->SetReserved(reserved); + } +} + +int SingleVerDataSyncUtils::GetMessageId(SyncType syncType) +{ + if (syncType == SyncType::QUERY_SYNC_TYPE) { + return QUERY_SYNC_MESSAGE; + } + return DATA_SYNC_MESSAGE; +} + +void SingleVerDataSyncUtils::PushAndPullKeyRevokHandle(SingleVerSyncTaskContext *context) +{ + // for push_and_pull mode it may be EKEYREVOKED error before receive watermarkexception + // should clear errCode and restart pushpull request. + int mode = SyncOperation::TransferSyncMode(context->GetMode()); + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0 && mode == SyncModeType::PUSH_AND_PULL && + context->GetTaskErrCode() == -E_EKEYREVOKED) { + context->SetTaskErrCode(E_OK); + } +} + +int SingleVerDataSyncUtils::GetReSendMode(int mode, uint32_t sequenceId, SyncType syncType) +{ + int curMode = SyncOperation::TransferSyncMode(mode); + if (curMode == SyncModeType::PUSH || curMode == SyncModeType::PULL) { + return mode; + } + if (curMode == SyncModeType::RESPONSE_PULL) { + return (syncType == SyncType::QUERY_SYNC_TYPE) ? SyncModeType::QUERY_PUSH : SyncModeType::PUSH; + } + // set push_and_pull mode when resend first sequenceId to inform remote to run RESPONSE_PULL task + // for sequenceId which is larger than first, only need to send data, means to set push or query_push mode + if (sequenceId == 1) { + return (syncType == SyncType::QUERY_SYNC_TYPE) ? SyncModeType::QUERY_PUSH_PULL : SyncModeType::PUSH_AND_PULL; + } + return (syncType == SyncType::QUERY_SYNC_TYPE) ? SyncModeType::QUERY_PUSH : SyncModeType::PUSH; +} + +void SingleVerDataSyncUtils::FillControlRequestPacket(ControlRequestPacket *packet, SingleVerSyncTaskContext *context) +{ + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + uint32_t flag = 0; + if (context->GetMode() == SyncModeType::SUBSCRIBE_QUERY && context->IsAutoSubscribe()) { + flag = flag | SubscribeRequest::IS_AUTO_SUBSCRIBE; + } + packet->SetPacketHead(E_OK, version, GetControlCmdType(context->GetMode()), flag); + packet->SetQuery(context->GetQuery()); +} + +ControlCmdType SingleVerDataSyncUtils::GetControlCmdType(int mode) +{ + if (mode == SyncModeType::SUBSCRIBE_QUERY) { + return ControlCmdType::SUBSCRIBE_QUERY_CMD; + } else if (mode == SyncModeType::UNSUBSCRIBE_QUERY) { + return ControlCmdType::UNSUBSCRIBE_QUERY_CMD; + } + return ControlCmdType::INVALID_CONTROL_CMD; +} + +int SingleVerDataSyncUtils::GetModeByControlCmdType(ControlCmdType controlCmd) +{ + if (controlCmd == ControlCmdType::SUBSCRIBE_QUERY_CMD) { + return SyncModeType::SUBSCRIBE_QUERY; + } else if (controlCmd == ControlCmdType::UNSUBSCRIBE_QUERY_CMD) { + return SyncModeType::UNSUBSCRIBE_QUERY; + } + return SyncModeType::INVALID_MODE; +} + +bool SingleVerDataSyncUtils::IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query) +{ + if (inMsg == nullptr) { + return false; + } + if (inMsg->GetMessageId() != CONTROL_SYNC_MESSAGE) { + return false; + } + const ControlRequestPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return false; + } + uint32_t controlCmdType = packet->GetcontrolCmdType(); + if (controlCmdType == ControlCmdType::SUBSCRIBE_QUERY_CMD && inMsg->GetMessageType() == TYPE_REQUEST) { + const SubscribeRequest *subPacket = inMsg->GetObject(); + if (packet == nullptr) { + return false; + } + query = subPacket->GetQuery(); + LOGI("[SingleVerDataSync] receive sub scribe query cmd,begin to trigger query auto sync"); + return true; + } + return false; +} + +void SingleVerDataSyncUtils::ControlAckErrorHandle(const SingleVerSyncTaskContext *context, + const std::shared_ptr &subManager) +{ + if (context->GetMode() == SyncModeType::SUBSCRIBE_QUERY) { + // reserve before need clear + subManager->DeleteLocalSubscribeQuery(context->GetDeviceId(), context->GetQuery()); + } +} + +void SingleVerDataSyncUtils::SetMessageHeadInfo(Message &message, uint16_t inMsgType, const std::string &inTarget, + uint32_t inSequenceId, uint32_t inSessionId) +{ + message.SetMessageType(inMsgType); + message.SetTarget(inTarget); + message.SetSequenceId(inSequenceId); + message.SetSessionId(inSessionId); +} + +bool SingleVerDataSyncUtils::IsGetDataSuccessfully(int errCode) +{ + return (errCode == E_OK || errCode == -E_UNFINISHED); +} + +Timestamp SingleVerDataSyncUtils::GetMaxSendDataTime(const std::vector &inData) +{ + Timestamp stamp = 0; + for (size_t i = 0; i < inData.size(); i++) { + if (inData[i] == nullptr) { + continue; + } + Timestamp tempStamp = inData[i]->GetTimestamp(); + if (stamp < tempStamp) { + stamp = tempStamp; + } + } + return stamp; +} + +SyncTimeRange SingleVerDataSyncUtils::GetFullSyncDataTimeRange(const std::vector &inData, + WaterMark localMark, UpdateWaterMark &isUpdate) +{ + Timestamp maxTimestamp = localMark; + Timestamp minTimestamp = localMark; + for (size_t i = 0; i < inData.size(); i++) { + if (inData[i] == nullptr) { + continue; + } + Timestamp tempStamp = inData[i]->GetTimestamp(); + if (maxTimestamp < tempStamp) { + maxTimestamp = tempStamp; + } + if (minTimestamp > tempStamp) { + minTimestamp = tempStamp; + } + isUpdate.normalUpdateMark = true; + } + return {minTimestamp, 0, maxTimestamp, 0}; +} + +SyncTimeRange SingleVerDataSyncUtils::GetQuerySyncDataTimeRange(const std::vector &inData, + WaterMark localMark, WaterMark deleteLocalMark, UpdateWaterMark &isUpdate) +{ + SyncTimeRange dataTimeRange = {localMark, deleteLocalMark, localMark, deleteLocalMark}; + for (size_t i = 0; i < inData.size(); i++) { + if (inData[i] == nullptr) { + continue; + } + Timestamp tempStamp = inData[i]->GetTimestamp(); + if ((inData[i]->GetFlag() & DataItem::DELETE_FLAG) == 0) { // query data + if (dataTimeRange.endTime < tempStamp) { + dataTimeRange.endTime = tempStamp; + } + if (dataTimeRange.beginTime > tempStamp) { + dataTimeRange.beginTime = tempStamp; + } + isUpdate.normalUpdateMark = true; + } + if ((inData[i]->GetFlag() & DataItem::DELETE_FLAG) != 0) { // delete data + if (dataTimeRange.deleteEndTime < tempStamp) { + dataTimeRange.deleteEndTime = tempStamp; + } + if (dataTimeRange.deleteBeginTime > tempStamp) { + dataTimeRange.deleteBeginTime = tempStamp; + } + isUpdate.deleteUpdateMark = true; + } + } + return dataTimeRange; +} + +SyncTimeRange SingleVerDataSyncUtils::ReviseLocalMark(SyncType syncType, SyncTimeRange &dataTimeRange, + UpdateWaterMark updateMark) +{ + SyncTimeRange tmpDataTime = dataTimeRange; + if (updateMark.deleteUpdateMark && syncType == SyncType::QUERY_SYNC_TYPE) { + tmpDataTime.deleteEndTime += 1; + } + if (updateMark.normalUpdateMark) { + tmpDataTime.endTime += 1; + } + return tmpDataTime; +} + +SyncTimeRange SingleVerDataSyncUtils::GetRecvDataTimeRange(SyncType syncType, + const std::vector &data, UpdateWaterMark &isUpdate) +{ + if (syncType != SyncType::QUERY_SYNC_TYPE) { + return SingleVerDataSyncUtils::GetFullSyncDataTimeRange(data, 0, isUpdate); + } + return SingleVerDataSyncUtils::GetQuerySyncDataTimeRange(data, 0, 0, isUpdate); +} + +SyncTimeRange SingleVerDataSyncUtils::GetSyncDataTimeRange(SyncType syncType, WaterMark localMark, WaterMark deleteMark, + const std::vector &inData, UpdateWaterMark &isUpdate) +{ + if (syncType != SyncType::QUERY_SYNC_TYPE) { + return SingleVerDataSyncUtils::GetFullSyncDataTimeRange(inData, localMark, isUpdate); + } + return SingleVerDataSyncUtils::GetQuerySyncDataTimeRange(inData, localMark, deleteMark, isUpdate); +} +} \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_data_sync_utils.h b/mock/distributeddb/syncer/src/single_ver_data_sync_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..f075ffaf211974aa779d49715a0627af1ec754a9 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_data_sync_utils.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SINGLE_VER_DATA_SYNC_UTIL_H +#define SINGLE_VER_DATA_SYNC_UTIL_H +#include "single_ver_data_packet.h" +#include "single_ver_sync_task_context.h" +#include "message.h" +namespace DistributedDB { +class SingleVerDataSyncUtils { +public: + static bool QuerySyncCheck(const SingleVerSyncTaskContext *context); + + static int AckMsgErrnoCheck(const SingleVerSyncTaskContext *context, const Message *message); + + static int RequestQueryCheck(const DataRequestPacket *packet, SyncGenericInterface *storage); + + static bool IsPermitLocalDeviceRecvData(const std::string &deviceId, const SecurityOption &remoteSecOption); + + static bool IsPermitRemoteDeviceRecvData(const std::string &deviceId, const SecurityOption &remoteSecOption, + SyncGenericInterface *storage); + + static void TransDbDataItemToSendDataItem(const std::string &localHashName, + std::vector &outData); + + static std::string TransferForeignOrigDevName(const std::string &deviceName, const std::string &localHashName); + + static void TransSendDataItemToLocal(const SingleVerSyncTaskContext *context, + const std::string &localHashName, const std::vector &data); + + static void TranslateErrCodeIfNeed(int mode, uint32_t version, int &errCode); + + static int RunPermissionCheck(SingleVerSyncTaskContext *context, const SyncGenericInterface* storage, + const std::string &label, int mode); + + static bool CheckPermitReceiveData(const SingleVerSyncTaskContext *context, const ICommunicator *communicator); + + static void SetPacketId(DataRequestPacket *packet, SingleVerSyncTaskContext *context, uint32_t version); + + static int GetMessageId(SyncType syncType); + + static void PushAndPullKeyRevokHandle(SingleVerSyncTaskContext *context); + + static int GetReSendMode(int mode, uint32_t sequenceId, SyncType syncType); + + static void FillControlRequestPacket(ControlRequestPacket *packet, SingleVerSyncTaskContext *context); + + static ControlCmdType GetControlCmdType(int mode); + + static int GetModeByControlCmdType(ControlCmdType controlCmd); + + static bool IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query); + + static void ControlAckErrorHandle(const SingleVerSyncTaskContext *context, + const std::shared_ptr &subManager); + + static void SetMessageHeadInfo(Message &message, uint16_t inMsgType, + const std::string &inTarget, uint32_t inSequenceId, uint32_t inSessionId); + + static bool IsGetDataSuccessfully(int errCode); + + static Timestamp GetMaxSendDataTime(const std::vector &inData); + + static SyncTimeRange GetFullSyncDataTimeRange(const std::vector &inData, WaterMark localMark, + UpdateWaterMark &isUpdate); + + static SyncTimeRange GetQuerySyncDataTimeRange(const std::vector &inData, WaterMark localMark, + WaterMark deleteLocalMark, UpdateWaterMark &isUpdate); + + static SyncTimeRange ReviseLocalMark(SyncType syncType, SyncTimeRange &dataTimeRange, UpdateWaterMark updateMark); + + static SyncTimeRange GetRecvDataTimeRange(SyncType syncType, + const std::vector &data, UpdateWaterMark &isUpdate); + + static SyncTimeRange GetSyncDataTimeRange(SyncType syncType, WaterMark localMark, WaterMark deleteMark, + const std::vector &inData, UpdateWaterMark &isUpdate); +}; +} +#endif // SINGLE_VER_DATA_SYNC_UTIL_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_kv_sync_task_context.cpp b/mock/distributeddb/syncer/src/single_ver_kv_sync_task_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb1f8ba5ff8b426281e2cb0d0a35a33efed1d0fb --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_kv_sync_task_context.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_kv_sync_task_context.h" + +namespace DistributedDB { +SingleVerKvSyncTaskContext::SingleVerKvSyncTaskContext() + : SingleVerSyncTaskContext(), syncStrategy_{} +{} + +SingleVerKvSyncTaskContext::~SingleVerKvSyncTaskContext() +{ +} + +std::string SingleVerKvSyncTaskContext::GetQuerySyncId() const +{ + return query_.GetIdentify(); +} + +std::string SingleVerKvSyncTaskContext::GetDeleteSyncId() const +{ + return GetDeviceId(); +} + +void SingleVerKvSyncTaskContext::SetSyncStrategy(const SyncStrategy &strategy) +{ + syncStrategy_.permitSync = strategy.permitSync; + syncStrategy_.convertOnSend = strategy.convertOnSend; + syncStrategy_.convertOnReceive = strategy.convertOnReceive; + syncStrategy_.checkOnReceive = strategy.checkOnReceive; +} + +SyncStrategy SingleVerKvSyncTaskContext::GetSyncStrategy(QuerySyncObject &querySyncObject) const +{ + (void) querySyncObject; + return syncStrategy_; +} +} \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_kv_sync_task_context.h b/mock/distributeddb/syncer/src/single_ver_kv_sync_task_context.h new file mode 100644 index 0000000000000000000000000000000000000000..5d8b615defdbeb5112b486fc7e3cc40aa762da36 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_kv_sync_task_context.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_KV_SYNC_TASK_CONTEXT_H +#define SINGLE_VER_KV_SYNC_TASK_CONTEXT_H + +#include "single_ver_sync_task_context.h" + +namespace DistributedDB { +class SingleVerKvSyncTaskContext : public SingleVerSyncTaskContext { +public: + + explicit SingleVerKvSyncTaskContext(); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerKvSyncTaskContext); + + std::string GetQuerySyncId() const override; + std::string GetDeleteSyncId() const override; + + void SetSyncStrategy(const SyncStrategy &strategy); + SyncStrategy GetSyncStrategy(QuerySyncObject &querySyncObject) const override; +protected: + ~SingleVerKvSyncTaskContext() override; + + SyncStrategy syncStrategy_; +}; +} +#endif // SINGLE_VER_KV_SYNC_TASK_CONTEXT_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_kv_syncer.cpp b/mock/distributeddb/syncer/src/single_ver_kv_syncer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c9abb08a4d78dccd2e231cd0dbb52dc5274acbbf --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_kv_syncer.cpp @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_kv_syncer.h" + +#include +#include +#include + +#include "db_common.h" +#include "ikvdb_sync_interface.h" +#include "log_print.h" +#include "meta_data.h" +#include "single_ver_sync_engine.h" +#include "sqlite_single_ver_natural_store.h" + +namespace DistributedDB { +SingleVerKVSyncer::SingleVerKVSyncer() + : autoSyncEnable_(false), triggerSyncTask_(true) +{ +} + +SingleVerKVSyncer::~SingleVerKVSyncer() +{ +} + +void SingleVerKVSyncer::EnableAutoSync(bool enable) +{ + LOGI("[Syncer] EnableAutoSync enable = %d, Label=%s", enable, label_.c_str()); + if (autoSyncEnable_ == enable) { + return; + } + + autoSyncEnable_ = enable; + if (!enable) { + return; + } + + if (!initialized_) { + LOGE("[Syncer] Syncer has not Init"); + return; + } + + std::vector devices; + GetOnlineDevices(devices); + if (devices.empty()) { + LOGI("[Syncer] EnableAutoSync no online devices"); + return; + } + int errCode = Sync(devices, SyncModeType::AUTO_PUSH, nullptr, nullptr, false); + if (errCode != E_OK) { + LOGE("[Syncer] sync start by EnableAutoSync failed err %d", errCode); + } +} + +// Local data changed callback +void SingleVerKVSyncer::LocalDataChanged(int notifyEvent) +{ + if (!initialized_) { + LOGE("[Syncer] Syncer has not Init"); + return; + } + + if (notifyEvent != SQLITE_GENERAL_FINISH_MIGRATE_EVENT && + notifyEvent != SQLITE_GENERAL_NS_PUT_EVENT) { + LOGD("[Syncer] ignore event:%d", notifyEvent); + return; + } + if (!triggerSyncTask_) { + LOGI("[Syncer] some sync task is scheduling"); + return; + } + triggerSyncTask_ = false; + RefObject::IncObjRef(syncEngine_); + // To avoid many task were produced and waiting in the queue. For example, put value in a loop. + // It will consume thread pool resources, so other task will delay until these task finish. + // In extreme situation, 10 thread run the localDataChanged task and 1 task waiting in queue. + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this] { + triggerSyncTask_ = true; + std::vector devices; + GetOnlineDevices(devices); + if (devices.empty()) { + LOGI("[Syncer] LocalDataChanged no online devices, Label=%s", label_.c_str()); + RefObject::DecObjRef(syncEngine_); + return; + } + if (!TryFullSync(devices)) { + TriggerSubQuerySync(devices); + } + RefObject::DecObjRef(syncEngine_); + }); + // if task schedule failed, but triggerSyncTask_ is not set to true, other thread may skip the schedule time + // when task schedule failed, it means unormal status, it is unable to schedule next time probably + // so it is ok if other thread skip the schedule if last task schedule failed + if (errCode != E_OK) { + triggerSyncTask_ = true; + LOGE("[TriggerSync] LocalDataChanged retCode:%d", errCode); + RefObject::DecObjRef(syncEngine_); + } + return; +} + +// remote device online callback +void SingleVerKVSyncer::RemoteDataChanged(const std::string &device) +{ + LOGI("[SingleVerKVSyncer] device online dev %s", STR_MASK(device)); + if (!initialized_) { + LOGE("[Syncer] Syncer has not Init"); + return; + } + std::string userId = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::USER_ID, ""); + std::string appId = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::APP_ID, ""); + std::string storeId = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::STORE_ID, ""); + RuntimeContext::GetInstance()->NotifyDatabaseStatusChange(userId, appId, storeId, device, true); + SingleVerSyncer::RemoteDataChanged(device); + if (autoSyncEnable_) { + RefObject::IncObjRef(syncEngine_); + int retCode = RuntimeContext::GetInstance()->ScheduleTask([this, device] { + std::vector devices; + devices.push_back(device); + int errCode = Sync(devices, SyncModeType::AUTO_PUSH, nullptr, nullptr, false); + if (errCode != E_OK) { + LOGE("[SingleVerKVSyncer] sync start by RemoteDataChanged failed err %d", errCode); + } + RefObject::DecObjRef(syncEngine_); + }); + if (retCode != E_OK) { + LOGE("[AutoLaunch] RemoteDataChanged triggler sync retCode:%d", retCode); + RefObject::DecObjRef(syncEngine_); + } + } + // db online again ,trigger subscribe + // if remote device online, subscribequery num is 0 + std::vector syncQueries; + static_cast(syncEngine_)->GetLocalSubscribeQueries(device, syncQueries); + if (syncQueries.size() == 0) { + LOGI("no need to trigger auto subscribe"); + return; + } + LOGI("[SingleVerKVSyncer] trigger local subscribe sync, queryNums=%zu", syncQueries.size()); + for (const auto &query : syncQueries) { + TriggerSubscribe(device, query); + } + static_cast(syncEngine_)->PutUnfiniedSubQueries(device, syncQueries); +} + +void SingleVerKVSyncer::QueryAutoSync(const InternalSyncParma ¶m) +{ + if (!initialized_) { + LOGE("[Syncer] Syncer has not Init"); + return; + } + LOGI("[SingleVerKVSyncer] trigger query syncmode=%u,dev=%s", param.mode, GetSyncDevicesStr(param.devices).c_str()); + RefObject::IncObjRef(syncEngine_); + int retCode = RuntimeContext::GetInstance()->ScheduleTask([this, param] { + int errCode = Sync(param); + if (errCode != E_OK) { + LOGE("[SingleVerKVSyncer] sync start by QueryAutoSync failed err %d", errCode); + } + RefObject::DecObjRef(syncEngine_); + }); + if (retCode != E_OK) { + LOGE("[SingleVerKVSyncer] QueryAutoSync triggler sync retCode:%d", retCode); + RefObject::DecObjRef(syncEngine_); + } +} + +int SingleVerKVSyncer::SyncConditionCheck(QuerySyncObject &query, int mode, bool isQuerySync, + const std::vector &devices) const +{ + if (!isQuerySync) { + return E_OK; + } + int errCode = static_cast(syncInterface_)->CheckAndInitQueryCondition(query); + if (errCode != E_OK) { + LOGE("[SingleVerKVSyncer] QuerySyncObject check failed"); + return errCode; + } + if (mode != SUBSCRIBE_QUERY) { + return E_OK; + } + if (query.HasLimit() || query.HasOrderBy()) { + LOGE("[SingleVerKVSyncer] subscribe query not support limit,offset or orderby"); + return -E_NOT_SUPPORT; + } + if (devices.size() > MAX_DEVICES_NUM) { + LOGE("[SingleVerKVSyncer] devices is overlimit"); + return -E_MAX_LIMITS; + } + return syncEngine_->SubscribeLimitCheck(devices, query); +} + +void SingleVerKVSyncer::TriggerSubscribe(const std::string &device, const QuerySyncObject &query) +{ + if (!initialized_) { + LOGE("[Syncer] Syncer has not Init"); + return; + } + RefObject::IncObjRef(syncEngine_); + int retCode = RuntimeContext::GetInstance()->ScheduleTask([this, device, query] { + std::vector devices; + devices.push_back(device); + SyncParma param; + param.devices = devices; + param.mode = SyncModeType::AUTO_SUBSCRIBE_QUERY; + param.onComplete = nullptr; + param.onFinalize = nullptr; + param.wait = false; + param.isQuerySync = true; + param.syncQuery = query; + int errCode = Sync(param); + if (errCode != E_OK) { + LOGE("[SingleVerKVSyncer] subscribe start by RemoteDataChanged failed err %d", errCode); + } + RefObject::DecObjRef(syncEngine_); + }); + if (retCode != E_OK) { + LOGE("[Syncer] triggler query subscribe start failed err %d", retCode); + RefObject::DecObjRef(syncEngine_); + } +} + +bool SingleVerKVSyncer::TryFullSync(const std::vector &devices) +{ + if (!initialized_) { + LOGE("[Syncer] Syncer has not Init"); + return true; + } + if (!autoSyncEnable_) { + LOGD("[Syncer] autoSync no enable"); + return false; + } + int errCode = Sync(devices, SyncModeType::AUTO_PUSH, nullptr, nullptr, false); + if (errCode != E_OK) { + LOGE("[Syncer] sync start by RemoteDataChanged failed err %d", errCode); + return false; + } + return true; +} + +void SingleVerKVSyncer::TriggerSubQuerySync(const std::vector &devices) +{ + if (!initialized_) { + LOGE("[Syncer] Syncer has not Init"); + return; + } + int errCode; + for (auto &device : devices) { + std::vector queries; + static_cast(syncEngine_)->GetRemoteSubscribeQueries(device, queries); + for (auto &query : queries) { + std::string queryId = query.GetIdentify(); + uint64_t lastTimestamp = metadata_->GetQueryLastTimestamp(device, queryId); + WaterMark queryWaterMark = 0; + errCode = metadata_->GetSendQueryWaterMark(queryId, device, queryWaterMark, false); + if (errCode != E_OK) { + LOGE("[Syncer] get queryId=%s,dev=%s watermark failed", STR_MASK(queryId), STR_MASK(device)); + continue; + } + if (lastTimestamp < queryWaterMark || lastTimestamp == 0) { + continue; + } + LOGD("[Syncer] lastTime=%" PRIu64 " vs WaterMark=%" PRIu64 ",trigger queryId=%s,dev=%s", lastTimestamp, + queryWaterMark, STR_MASK(queryId), STR_MASK(device)); + InternalSyncParma param; + std::vector targetDevices; + targetDevices.push_back(device); + param.devices = targetDevices; + param.mode = SyncModeType::AUTO_PUSH; + param.isQuerySync = true; + param.syncQuery = query; + QueryAutoSync(param); + } + } +} + +SyncerBasicInfo SingleVerKVSyncer::DumpSyncerBasicInfo() +{ + SyncerBasicInfo basicInfo = GenericSyncer::DumpSyncerBasicInfo(); + basicInfo.isAutoSync = autoSyncEnable_; + return basicInfo; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/single_ver_kv_syncer.h b/mock/distributeddb/syncer/src/single_ver_kv_syncer.h new file mode 100644 index 0000000000000000000000000000000000000000..c291acf2df98879ad0bab619bdfc191cb2bf7e56 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_kv_syncer.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_SYNCER_H +#define KV_SYNCER_H + +#include "single_ver_syncer.h" + +namespace DistributedDB { +class SingleVerKVSyncer final : public SingleVerSyncer { +public: + SingleVerKVSyncer(); + ~SingleVerKVSyncer() override; + + // Enable auto sync function + void EnableAutoSync(bool enable) override; + + // Local data changed callback + void LocalDataChanged(int notifyEvent) override; + + // Remote data changed callback + void RemoteDataChanged(const std::string &device) override; + + void QueryAutoSync(const InternalSyncParma ¶m) override; + + void TriggerSubscribe(const std::string &device, const QuerySyncObject &query); + + SyncerBasicInfo DumpSyncerBasicInfo() override; + +protected: + int SyncConditionCheck(QuerySyncObject &query, int mode, bool isQuerySync, + const std::vector &devices) const override; + +private: + // if trigger full sync, no need to trigger query sync again + bool TryFullSync(const std::vector &devices); + void TriggerSubQuerySync(const std::vector &devices); + bool autoSyncEnable_; + std::atomic triggerSyncTask_; +}; +} // namespace DistributedDB + +#endif // KV_SYNCER_H diff --git a/mock/distributeddb/syncer/src/single_ver_relational_sync_task_context.cpp b/mock/distributeddb/syncer/src/single_ver_relational_sync_task_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef556385f0ccca1947d16daaa1097bcf9745eb62 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_relational_sync_task_context.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_relational_sync_task_context.h" +#include "db_common.h" + +#ifdef RELATIONAL_STORE +namespace DistributedDB { +SingleVerRelationalSyncTaskContext::SingleVerRelationalSyncTaskContext() + : SingleVerSyncTaskContext() +{} + +SingleVerRelationalSyncTaskContext::~SingleVerRelationalSyncTaskContext() +{ +} + +std::string SingleVerRelationalSyncTaskContext::GetQuerySyncId() const +{ + return querySyncId_; +} + +std::string SingleVerRelationalSyncTaskContext::GetDeleteSyncId() const +{ + return deleteSyncId_; +} + +void SingleVerRelationalSyncTaskContext::Clear() +{ + querySyncId_.clear(); + deleteSyncId_.clear(); + SingleVerSyncTaskContext::Clear(); +} + +void SingleVerRelationalSyncTaskContext::CopyTargetData(const ISyncTarget *target, const TaskParam &TaskParam) +{ + SingleVerSyncTaskContext::CopyTargetData(target, TaskParam); + std::string hashTableName = DBCommon::TransferHashString(query_.GetRelationTableName()); + std::string hexTableName = DBCommon::TransferStringToHex(hashTableName); + querySyncId_ = hexTableName + query_.GetIdentify(); // save as deviceId + hexTableName + queryId + deleteSyncId_ = GetDeviceId() + hexTableName; // save as deviceId + hexTableName +} + +void SingleVerRelationalSyncTaskContext::SetRelationalSyncStrategy(RelationalSyncStrategy strategy) +{ + std::lock_guard autoLock(syncStrategyMutex_); + relationalSyncStrategy_ = strategy; +} + +SyncStrategy SingleVerRelationalSyncTaskContext::GetSyncStrategy(QuerySyncObject &querySyncObject) const +{ + std::lock_guard autoLock(syncStrategyMutex_); + auto it = relationalSyncStrategy_.find(querySyncObject.GetRelationTableName()); + if (it == relationalSyncStrategy_.end()) { + return {}; + } + return it->second; +} + +void SingleVerRelationalSyncTaskContext::SetIsNeedResetAbilitySync(bool isNeedReset) +{ + isNeedResetAbilitySync_ = isNeedReset; + if (isNeedResetAbilitySync_) { + SetIsSchemaSync(false); + } +} + +void SingleVerRelationalSyncTaskContext::SchemaChange() +{ + SetIsNeedResetAbilitySync(true); + std::lock_guard autoLock(syncStrategyMutex_); + relationalSyncStrategy_ = {}; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_relational_sync_task_context.h b/mock/distributeddb/syncer/src/single_ver_relational_sync_task_context.h new file mode 100644 index 0000000000000000000000000000000000000000..0146c42bf84c14b5f841439c644bbea02e805d9d --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_relational_sync_task_context.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_RELATIONAL_SYNC_TASK_CONTEXT_H +#define SINGLE_VER_RELATIONAL_SYNC_TASK_CONTEXT_H + +#ifdef RELATIONAL_STORE +#include "single_ver_sync_task_context.h" + +namespace DistributedDB { +class SingleVerRelationalSyncTaskContext : public SingleVerSyncTaskContext { +public: + + explicit SingleVerRelationalSyncTaskContext(); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerRelationalSyncTaskContext); + + void Clear() override; + std::string GetQuerySyncId() const override; + std::string GetDeleteSyncId() const override; + + void SetRelationalSyncStrategy(RelationalSyncStrategy strategy); + SyncStrategy GetSyncStrategy(QuerySyncObject &querySyncObject) const override; + + void SetIsNeedResetAbilitySync(bool isNeedReset) override; + + void SchemaChange() override; +protected: + ~SingleVerRelationalSyncTaskContext() override; + void CopyTargetData(const ISyncTarget *target, const TaskParam &taskParam) override; + + std::string querySyncId_; + std::string deleteSyncId_; + + // for relational syncStrategy + mutable std::mutex syncStrategyMutex_; + RelationalSyncStrategy relationalSyncStrategy_; +}; +} +#endif // RELATIONAL_STORE +#endif // SINGLE_VER_RELATIONAL_SYNC_TASK_CONTEXT_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_relational_syncer.cpp b/mock/distributeddb/syncer/src/single_ver_relational_syncer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..264ab16c05ed35f4c4adb5c98af0c03111862bdf --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_relational_syncer.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "single_ver_relational_syncer.h" +#ifdef RELATIONAL_STORE +#include "db_common.h" +#include "relational_db_sync_interface.h" +#include "single_ver_sync_engine.h" + +namespace DistributedDB { +int SingleVerRelationalSyncer::Initialize(ISyncInterface *syncInterface, bool isNeedActive) +{ + int errCode = SingleVerSyncer::Initialize(syncInterface, isNeedActive); + if (errCode != E_OK) { + return errCode; + } + auto callback = std::bind(&SingleVerRelationalSyncer::SchemaChangeCallback, this); + return static_cast(syncInterface)-> + RegisterSchemaChangedCallback(callback); +} + +int SingleVerRelationalSyncer::Sync(const SyncParma ¶m, uint64_t connectionId) +{ + if (param.mode == SYNC_MODE_PUSH_PULL) { + return -E_NOT_SUPPORT; + } + if (param.syncQuery.GetRelationTableName().empty()) { + return -E_NOT_SUPPORT; + } + return GenericSyncer::Sync(param, connectionId); +} + +int SingleVerRelationalSyncer::PrepareSync(const SyncParma ¶m, uint32_t syncId, uint64_t connectionId) +{ + const auto &syncInterface = static_cast(syncInterface_); + std::vector tablesQuery; + if (param.isQuerySync) { + tablesQuery.push_back(param.syncQuery); + } else { + tablesQuery = syncInterface->GetTablesQuery(); + } + std::set subSyncIdSet; + int errCode = GenerateEachSyncTask(param, syncId, tablesQuery, connectionId, subSyncIdSet); + if (errCode != E_OK) { + DoRollBack(subSyncIdSet); + return errCode; + } + if (param.wait) { + DoOnComplete(param, syncId); + } + return E_OK; +} + +int SingleVerRelationalSyncer::GenerateEachSyncTask(const SyncParma ¶m, uint32_t syncId, + const std::vector &tablesQuery, uint64_t connectionId, std::set &subSyncIdSet) +{ + SyncParma subParam = param; + subParam.isQuerySync = true; + int errCode = E_OK; + for (const QuerySyncObject &table : tablesQuery) { + uint32_t subSyncId = GenerateSyncId(); + std::string hashTableName = DBCommon::TransferHashString(table.GetRelationTableName()); + LOGI("[SingleVerRelationalSyncer] SubSyncId %u create by SyncId %d, hashTableName = %s", + subSyncId, syncId, STR_MASK(DBCommon::TransferStringToHex(hashTableName))); + subParam.syncQuery = table; + subParam.onComplete = std::bind(&SingleVerRelationalSyncer::DoOnSubSyncComplete, this, subSyncId, + syncId, param, std::placeholders::_1); + { + std::lock_guard lockGuard(syncMapLock_); + fullSyncIdMap_[syncId].insert(subSyncId); + } + errCode = GenericSyncer::PrepareSync(subParam, subSyncId, connectionId); + if (errCode != E_OK) { + LOGW("[SingleVerRelationalSyncer] PrepareSync failed errCode:%d", errCode); + std::lock_guard lockGuard(syncMapLock_); + fullSyncIdMap_[syncId].erase(subSyncId); + break; + } + subSyncIdSet.insert(subSyncId); + } + return errCode; +} + +void SingleVerRelationalSyncer::DoOnSubSyncComplete(const uint32_t subSyncId, const uint32_t syncId, + const SyncParma ¶m, const std::map &devicesMap) +{ + bool allFinish = true; + { + std::lock_guard lockGuard(syncMapLock_); + fullSyncIdMap_[syncId].erase(subSyncId); + allFinish = fullSyncIdMap_[syncId].empty(); + for (const auto &item : devicesMap) { + resMap_[syncId][item.first][param.syncQuery.GetRelationTableName()] = static_cast(item.second); + } + } + // block sync do callback in sync function + if (allFinish && !param.wait) { + DoOnComplete(param, syncId); + } +} + +void SingleVerRelationalSyncer::DoRollBack(std::set &subSyncIdSet) +{ + for (const auto &removeId : subSyncIdSet) { + int retCode = RemoveSyncOperation(static_cast(removeId)); + if (retCode != E_OK) { + LOGW("[SingleVerRelationalSyncer] RemoveSyncOperation failed errCode:%d, syncId:%d", retCode, removeId); + } + } +} + +void SingleVerRelationalSyncer::DoOnComplete(const SyncParma ¶m, uint32_t syncId) +{ + if (!param.relationOnComplete) { + return; + } + std::map> syncRes; + std::map> tmpMap; + { + std::lock_guard lockGuard(syncMapLock_); + tmpMap = resMap_[syncId]; + } + for (const auto &devicesRes : tmpMap) { + for (const auto &tableRes : devicesRes.second) { + syncRes[devicesRes.first].push_back( + {tableRes.first, static_cast(tableRes.second)}); + } + } + param.relationOnComplete(syncRes); + { + std::lock_guard lockGuard(syncMapLock_); + resMap_.erase(syncId); + fullSyncIdMap_.erase(syncId); + } +} + +void SingleVerRelationalSyncer::EnableAutoSync(bool enable) +{ + (void)enable; +} + +void SingleVerRelationalSyncer::LocalDataChanged(int notifyEvent) +{ + (void)notifyEvent; +} + +void SingleVerRelationalSyncer::SchemaChangeCallback() +{ + if (syncEngine_ != nullptr) { + syncEngine_->SchemaChange(); + } +} + +int SingleVerRelationalSyncer::SyncConditionCheck(QuerySyncObject &query, int mode, bool isQuerySync, + const std::vector &devices) const +{ + if (!isQuerySync) { + return E_OK; + } + int errCode = static_cast(syncInterface_)->CheckAndInitQueryCondition(query); + if (errCode != E_OK) { + LOGE("[SingleVerRelationalSyncer] QuerySyncObject check failed"); + return errCode; + } + if (mode == SUBSCRIBE_QUERY) { + return -E_NOT_SUPPORT; + } + return E_OK; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_relational_syncer.h b/mock/distributeddb/syncer/src/single_ver_relational_syncer.h new file mode 100644 index 0000000000000000000000000000000000000000..5bb7caee0821a6db7a6642c21aa871d8ee1b69a6 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_relational_syncer.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_SYNCER_H +#define RELATIONAL_SYNCER_H +#ifdef RELATIONAL_STORE +#include "single_ver_syncer.h" +namespace DistributedDB { +class SingleVerRelationalSyncer final : public SingleVerSyncer { +public: + SingleVerRelationalSyncer() = default; + ~SingleVerRelationalSyncer() override = default; + + int Initialize(ISyncInterface *syncInterface, bool isNeedActive) override; + + // Sync function. use SyncParma to reduce parameter. + int Sync(const SyncParma ¶m, uint64_t connectionId) override; + + void EnableAutoSync(bool enable) override; + + void LocalDataChanged(int notifyEvent) override; + +protected: + + int PrepareSync(const SyncParma ¶m, uint32_t syncId, uint64_t connectionId) override; + + int SyncConditionCheck(QuerySyncObject &query, int mode, bool isQuerySync, + const std::vector &devices) const override; + +private: + + int GenerateEachSyncTask(const SyncParma ¶m, uint32_t syncId, + const std::vector &tablesQuery, uint64_t connectionId, std::set &subSyncIdSet); + + void DoRollBack(std::set &subSyncIdSet); + + void DoOnComplete(const SyncParma ¶m, uint32_t syncId); + void DoOnSubSyncComplete(const uint32_t subSyncId, const uint32_t syncId, + const SyncParma ¶m, const std::map &devicesMap); + + void SchemaChangeCallback(); + + mutable std::mutex syncMapLock_; + std::map> fullSyncIdMap_; + std::map>> resMap_; +}; +} +#endif +#endif // RELATIONAL_SYNCER_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_serialize_manager.cpp b/mock/distributeddb/syncer/src/single_ver_serialize_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a6fc97b09ebbd855ffd39613ea0d94c2df125a5c --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_serialize_manager.cpp @@ -0,0 +1,713 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_serialize_manager.h" +#include "icommunicator.h" +#include "log_print.h" +#include "message_transform.h" +#include "parcel.h" +#include "generic_single_ver_kv_entry.h" +#include "sync_types.h" +#include "version.h" +#include "db_common.h" + +namespace DistributedDB { +int SingleVerSerializeManager::Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg))) { + return -E_MESSAGE_ID_ERROR; + } + if (inMsg->GetMessageId() == CONTROL_SYNC_MESSAGE) { + return ControlSerialization(buffer, length, inMsg); + } + return DataSerialization(buffer, length, inMsg); +} + +int SingleVerSerializeManager::DataSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return DataPacketSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + case TYPE_NOTIFY: + return AckPacketSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +int SingleVerSerializeManager::ControlSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return ControlPacketSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckControlPacketSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +int SingleVerSerializeManager::DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg))) { + return -E_MESSAGE_ID_ERROR; + } + if (inMsg->GetMessageId() == CONTROL_SYNC_MESSAGE) { + return ControlDeSerialization(buffer, length, inMsg); + } + return DataDeSerialization(buffer, length, inMsg); +} + +int SingleVerSerializeManager::DataDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return DataPacketDeSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + case TYPE_NOTIFY: + return AckPacketDeSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +int SingleVerSerializeManager::ControlDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return ControlPacketDeSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckControlPacketDeSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +uint32_t SingleVerSerializeManager::CalculateLen(const Message *inMsg) +{ + if (!(IsPacketValid(inMsg))) { + return 0; + } + if (inMsg->GetMessageId() == CONTROL_SYNC_MESSAGE) { + return CalculateControlLen(inMsg); + } + return CalculateDataLen(inMsg); +} + +uint32_t SingleVerSerializeManager::CalculateDataLen(const Message *inMsg) +{ + uint32_t len = 0; + int errCode; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + errCode = DataPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + LOGE("[CalculateDataLen] calculate data request packet len failed, errCode=%d", errCode); + return 0; + } + return len; + case TYPE_RESPONSE: + case TYPE_NOTIFY: + errCode = AckPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + LOGE("[CalculateDataLen] calculate data notify packet len failed errCode=%d", errCode); + return 0; + } + return len; + default: + return 0; + } +} + +uint32_t SingleVerSerializeManager::CalculateControlLen(const Message *inMsg) +{ + uint32_t len = 0; + int errCode; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + errCode = ControlPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + LOGE("[CalculateControlLen] calculate control request packet len failed, errCode=%d", errCode); + return 0; + } + return len; + case TYPE_RESPONSE: + case TYPE_NOTIFY: + errCode = AckControlPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + LOGE("[CalculateControlLen] calculate control request packet len failed, errCode=%d", errCode); + return 0; + } + return len; + default: + return 0; + } +} + +int SingleVerSerializeManager::RegisterTransformFunc() +{ + TransformFunc func; + func.computeFunc = std::bind(&SingleVerSerializeManager::CalculateLen, std::placeholders::_1); + func.serializeFunc = std::bind(&SingleVerSerializeManager::Serialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + func.deserializeFunc = std::bind(&SingleVerSerializeManager::DeSerialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + + int errCode = MessageTransform::RegTransformFunction(QUERY_SYNC_MESSAGE, func); + if (errCode != E_OK) { + return errCode; + } + errCode = MessageTransform::RegTransformFunction(DATA_SYNC_MESSAGE, func); + if (errCode != E_OK) { + return errCode; + } + return MessageTransform::RegTransformFunction(CONTROL_SYNC_MESSAGE, func); +} + +int SingleVerSerializeManager::DataPacketSyncerPartSerialization(Parcel &parcel, const DataRequestPacket *packet) +{ + parcel.WriteUInt64(packet->GetEndWaterMark()); + parcel.WriteUInt64(packet->GetLocalWaterMark()); + parcel.WriteUInt64(packet->GetPeerWaterMark()); + parcel.WriteInt(packet->GetSendCode()); + parcel.WriteInt(packet->GetMode()); + parcel.WriteUInt32(packet->GetSessionId()); + parcel.WriteVector(packet->GetReserved()); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + if (packet->GetVersion() > SOFTWARE_VERSION_RELEASE_2_0) { + parcel.WriteUInt32(packet->GetFlag()); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + } + parcel.EightByteAlign(); + return E_OK; +} + +int SingleVerSerializeManager::DataPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + auto packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + Parcel parcel(buffer, length); + + // version + int errCode = parcel.WriteUInt32(packet->GetVersion()); + if (errCode != E_OK) { + LOGE("[DataPacketSerialization] Serialize version failed"); + return errCode; + } + // sendDataItems + errCode = GenericSingleVerKvEntry::SerializeDatas( + (packet->IsCompressData() ? std::vector {} : packet->GetData()), parcel, packet->GetVersion()); + if (errCode != E_OK) { + LOGE("[DataPacketSerialization] Serialize Data failed"); + return errCode; + } + + // data sync + errCode = DataPacketSyncerPartSerialization(parcel, packet); + if (errCode != E_OK) { + LOGE("[DataPacketSerialization] Serialize Data failed"); + return errCode; + } + if (inMsg->GetMessageId() == QUERY_SYNC_MESSAGE) { + errCode = DataPacketQuerySyncSerialization(parcel, packet); // for query sync + if (errCode != E_OK) { + return errCode; + } + } + if (packet->IsCompressData()) { + // serialize compress data + errCode = GenericSingleVerKvEntry::SerializeCompressedDatas(packet->GetData(), packet->GetCompressData(), + parcel, packet->GetVersion(), packet->GetCompressAlgo()); + if (errCode != E_OK) { + LOGE("[DataPacketSerialization] Serialize compress Data failed"); + return errCode; + } + } + return E_OK; +} + +int SingleVerSerializeManager::DataPacketQuerySyncSerialization(Parcel &parcel, const DataRequestPacket *packet) +{ + // deleted record send watermark + int errCode = parcel.WriteUInt64(packet->GetDeletedWaterMark()); + if (errCode != E_OK) { + LOGE("[QuerySerialization] Serialize deleted record send watermark failed!"); + return errCode; + } + + // query identify + QuerySyncObject queryObj = packet->GetQuery(); + errCode = parcel.WriteString(packet->GetQueryId()); + if (errCode != E_OK) { + LOGE("[QuerySerialization] Serialize query id failed!"); + return errCode; + } + if ((packet->GetVersion() > SOFTWARE_VERSION_RELEASE_4_0) || packet->GetMode() != QUERY_PUSH) { + // need to check. + errCode = queryObj.SerializeData(parcel, SOFTWARE_VERSION_CURRENT); + } + return errCode; +} + +int SingleVerSerializeManager::DataPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + const DataRequestPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + len = packet->CalculateLen(inMsg->GetMessageId()); + return E_OK; +} + +int SingleVerSerializeManager::AckPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + const DataAckPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + len = packet->CalculateLen(); + return E_OK; +} + +bool SingleVerSerializeManager::IsPacketValid(const Message *inMsg) +{ + if (inMsg == nullptr) { + return false; + } + + int msgType = inMsg->GetMessageType(); + if (msgType != TYPE_REQUEST && msgType != TYPE_RESPONSE && msgType != TYPE_NOTIFY) { + LOGE("[DataSync][IsPacketValid] Message type ERROR! message type=%d", msgType); + return false; + } + return true; +} + +int SingleVerSerializeManager::AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + const DataAckPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + Parcel parcel(buffer, length); + parcel.WriteUInt32(packet->GetVersion()); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + // now V1 compatible for softWareVersion :{101, 102} + return AckPacketSyncerPartSerializationV1(parcel, packet); +} + +int SingleVerSerializeManager::AckPacketSyncerPartSerializationV1(Parcel &parcel, const DataAckPacket *packet) +{ + parcel.WriteUInt64(packet->GetData()); + parcel.WriteInt(packet->GetRecvCode()); + parcel.WriteVector(packet->GetReserved()); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + parcel.EightByteAlign(); + return E_OK; +} + +int SingleVerSerializeManager::DataPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + std::vector dataItems; + uint32_t version; + Parcel parcel(const_cast(buffer), length); + uint32_t packLen = parcel.ReadUInt32(version); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + + if (version > SOFTWARE_VERSION_CURRENT) { + return -E_VERSION_NOT_SUPPORT; + } + + packLen += static_cast(GenericSingleVerKvEntry::DeSerializeDatas(dataItems, parcel)); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + + auto packet = new (std::nothrow) DataRequestPacket(); + if (packet == nullptr) { + return -E_OUT_OF_MEMORY; + } + + packet->SetVersion(version); + packet->SetData(dataItems); + int errCode = DataPacketSyncerPartDeSerialization(parcel, packet, packLen, length, version); + if (errCode != E_OK) { + goto ERROR; + } + if (inMsg->GetMessageId() == QUERY_SYNC_MESSAGE) { + errCode = DataPacketQuerySyncDeSerialization(parcel, packet); + if (errCode != E_OK) { + goto ERROR; + } + } + if (packet->IsCompressData()) { + errCode = DataPacketCompressDataDeSerialization(parcel, packet); + if (errCode != E_OK) { + goto ERROR; + } + } + + errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + goto ERROR; + } + return errCode; + +ERROR: + delete packet; + packet = nullptr; + return errCode; +} + +int SingleVerSerializeManager::DataPacketQuerySyncDeSerialization(Parcel &parcel, DataRequestPacket *packet) +{ + WaterMark deletedWatermark = 0; + parcel.ReadUInt64(deletedWatermark); + std::string queryId; + parcel.ReadString(queryId); + if (parcel.IsError()) { + return -E_PARSE_FAIL; + } + // query identify + QuerySyncObject querySyncObj; + int errCode = E_OK; + // for version 105, query is always sent. + if ((packet->GetVersion() > SOFTWARE_VERSION_RELEASE_4_0) || packet->GetMode() != QUERY_PUSH) { + // need to check. + errCode = QuerySyncObject::DeSerializeData(parcel, querySyncObj); + } + if (errCode != E_OK) { + LOGI("[SingleVerSerializeManager] DeSerializeData object failed."); + return errCode; + } + packet->SetDeletedWaterMark(deletedWatermark); + packet->SetQueryId(queryId); + if ((packet->GetVersion() > SOFTWARE_VERSION_RELEASE_4_0) || packet->GetMode() != QUERY_PUSH) { + packet->SetQuery(querySyncObj); + } + return E_OK; +} + +int SingleVerSerializeManager::DataPacketCompressDataDeSerialization(Parcel &parcel, DataRequestPacket *packet) +{ + std::vector originalData; + int errCode = GenericSingleVerKvEntry::DeSerializeCompressedDatas(originalData, parcel); + if (errCode != E_OK) { + LOGE("[SingleVerSerializeManager] DeSerializeComptressData failed, errCode=%d", errCode); + return errCode; + } + packet->SetData(originalData); + return E_OK; +} + +int SingleVerSerializeManager::DataPacketSyncerPartDeSerialization(Parcel &parcel, DataRequestPacket *packet, + uint32_t packLen, uint32_t length, uint32_t version) +{ + WaterMark waterMark; + WaterMark localWaterMark; + WaterMark peerWaterMark; + int32_t sendCode; + int32_t mode; + uint32_t sessionId; + uint32_t flag = 0; + std::vector reserved; + + packLen += parcel.ReadUInt64(waterMark); + packLen += parcel.ReadUInt64(localWaterMark); + packLen += parcel.ReadUInt64(peerWaterMark); + packLen += parcel.ReadInt(sendCode); + packLen += parcel.ReadInt(mode); + packLen += parcel.ReadUInt32(sessionId); + packLen += parcel.ReadVector(reserved); + if (version > SOFTWARE_VERSION_RELEASE_2_0) { + packLen += parcel.ReadUInt32(flag); + packet->SetFlag(flag); + } + packLen = Parcel::GetEightByteAlign(packLen); + if (parcel.IsError()) { + LOGE("[DataSync][DataPacketDeSerialization] deserialize failed! input len=%" PRIu32 ",packLen=%" PRIu32, + length, packLen); + return -E_LENGTH_ERROR; + } + parcel.EightByteAlign(); + packet->SetEndWaterMark(waterMark); + packet->SetLocalWaterMark(localWaterMark); + packet->SetPeerWaterMark(peerWaterMark); + packet->SetSendCode(sendCode); + packet->SetMode(mode); + packet->SetSessionId(sessionId); + packet->SetReserved(reserved); + return E_OK; +} + +int SingleVerSerializeManager::AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + DataAckPacket packet; + Parcel parcel(const_cast(buffer), length); + uint32_t version; + + parcel.ReadUInt32(version); + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + if (version > SOFTWARE_VERSION_CURRENT) { + packet.SetVersion(version); + packet.SetRecvCode(-E_VERSION_NOT_SUPPORT); + return inMsg->SetCopiedObject<>(packet); + } + packet.SetVersion(version); + // now V1 compatible for softWareVersion :{101, 102} + int errCode = AckPacketSyncerPartDeSerializationV1(parcel, packet); + if (errCode != E_OK) { + return errCode; + } + + return inMsg->SetCopiedObject<>(packet); +} + +int SingleVerSerializeManager::AckPacketSyncerPartDeSerializationV1(Parcel &parcel, DataAckPacket &packet) +{ + WaterMark mark; + int32_t errCode; + std::vector reserved; + + parcel.ReadUInt64(mark); + parcel.ReadInt(errCode); + parcel.ReadVector(reserved); + if (parcel.IsError()) { + LOGE("[AckPacketSyncerPartDeSerializationV1] DeSerialization failed"); + return -E_INVALID_ARGS; + } + packet.SetData(mark); + packet.SetRecvCode(errCode); + packet.SetReserved(reserved); + return E_OK; +} + +int SingleVerSerializeManager::ControlPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + auto packet = inMsg->GetObject(); + if (packet == nullptr || packet->GetcontrolCmdType() >= INVALID_CONTROL_CMD) { + LOGE("[ControlPacketSerialization] invalid control cmd"); + return -E_INVALID_ARGS; + } + if (packet->GetcontrolCmdType() == SUBSCRIBE_QUERY_CMD || packet->GetcontrolCmdType() == UNSUBSCRIBE_QUERY_CMD) { + return SingleVerSerializeManager::SubscribeCalculateLen(inMsg, len); + } + return E_OK; +} + +int SingleVerSerializeManager::ControlPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + auto packet = inMsg->GetObject(); + if (packet == nullptr || packet->GetcontrolCmdType() >= INVALID_CONTROL_CMD) { + LOGE("[ControlPacketSerialization] invalid control cmd"); + return -E_INVALID_ARGS; + } + if (packet->GetcontrolCmdType() == SUBSCRIBE_QUERY_CMD || packet->GetcontrolCmdType() == UNSUBSCRIBE_QUERY_CMD) { + return SingleVerSerializeManager::SubscribeSerialization(buffer, length, inMsg); + } + return E_OK; +} + +int SingleVerSerializeManager::ControlPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + Parcel parcel(const_cast(buffer), length); + ControlRequestPacket packet; + int errCode = ControlRequestDeSerialization(parcel, packet); + if (errCode != E_OK) { + return errCode; + } + if (packet.GetcontrolCmdType() == SUBSCRIBE_QUERY_CMD || packet.GetcontrolCmdType() == UNSUBSCRIBE_QUERY_CMD) { + errCode = SubscribeDeSerialization(parcel, inMsg, packet); + } + return errCode; +} + +int SingleVerSerializeManager::AckControlPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + auto packet = inMsg->GetObject(); + if (packet == nullptr) { + LOGE("[AckControlPacketCalculateLen] invalid control cmd"); + return -E_INVALID_ARGS; + } + len = packet->CalculateLen(); + return E_OK; +} + +int SingleVerSerializeManager::AckControlPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + auto packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + Parcel parcel(buffer, length); + parcel.WriteUInt32(packet->GetVersion()); + parcel.WriteInt(packet->GetRecvCode()); + parcel.WriteUInt32(packet->GetcontrolCmdType()); + parcel.WriteUInt32(packet->GetFlag()); + if (parcel.IsError()) { + LOGE("[AckControlPacketSerialization] Serialization failed"); + return -E_INVALID_ARGS; + } + parcel.EightByteAlign(); + return E_OK; +} + +int SingleVerSerializeManager::AckControlPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + auto packet = new (std::nothrow) ControlAckPacket(); + if (packet == nullptr) { + return -E_OUT_OF_MEMORY; + } + Parcel parcel(const_cast(buffer), length); + int32_t recvCode = 0; + uint32_t version = 0; + uint32_t controlCmdType = 0; + uint32_t flag = 0; + parcel.ReadUInt32(version); + parcel.ReadInt(recvCode); + parcel.ReadUInt32(controlCmdType); + parcel.ReadUInt32(flag); + int errCode; + if (parcel.IsError()) { + LOGE("[AckControlPacketDeSerialization] DeSerialization failed"); + errCode = -E_INVALID_ARGS; + goto ERROR; + } + packet->SetPacketHead(recvCode, version, static_cast(controlCmdType), flag); + errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + goto ERROR; + } + return errCode; +ERROR: + delete packet; + packet = nullptr; + return errCode; +} + +int SingleVerSerializeManager::ControlRequestSerialization(Parcel &parcel, const Message *inMsg) +{ + auto packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + parcel.WriteUInt32(packet->GetVersion()); + parcel.WriteInt(packet->GetSendCode()); + parcel.WriteUInt32(packet->GetcontrolCmdType()); + parcel.WriteUInt32(packet->GetFlag()); + if (parcel.IsError()) { + LOGE("[ControlRequestSerialization] Serialization failed"); + return -E_INVALID_ARGS; + } + parcel.EightByteAlign(); + return E_OK; +} + +int SingleVerSerializeManager::ControlRequestDeSerialization(Parcel &parcel, ControlRequestPacket &packet) +{ + uint32_t version = 0; + int32_t sendCode = 0; + uint32_t controlCmdType = 0; + uint32_t flag = 0; + parcel.ReadUInt32(version); + if (version > SOFTWARE_VERSION_CURRENT) { + return -E_VERSION_NOT_SUPPORT; + } + parcel.ReadInt(sendCode); + parcel.ReadUInt32(controlCmdType); + parcel.ReadUInt32(flag); + if (parcel.IsError()) { + LOGE("[ControlRequestDeSerialization] deserialize failed!"); + return -E_LENGTH_ERROR; + } + packet.SetPacketHead(sendCode, version, static_cast(controlCmdType), flag); + return E_OK; +} + +int SingleVerSerializeManager::SubscribeCalculateLen(const Message *inMsg, uint32_t &len) +{ + auto packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + len = packet->CalculateLen(); + return E_OK; +} + +int SingleVerSerializeManager::SubscribeSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + auto packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + Parcel parcel(buffer, length); + int errCode = ControlRequestSerialization(parcel, inMsg); + if (errCode != E_OK) { + LOGE("[SubscribeSerialization] ControlRequestPacket Serialization failed, errCode=%d", errCode); + return errCode; + } + QuerySyncObject queryObj = packet->GetQuery(); + errCode = queryObj.SerializeData(parcel, SOFTWARE_VERSION_CURRENT); + if (errCode != E_OK) { + LOGE("[SubscribeSerialization] query object Serialization failed, errCode=%d", errCode); + return errCode; + } + return E_OK; +} + +int SingleVerSerializeManager::SubscribeDeSerialization(Parcel &parcel, Message *inMsg, + ControlRequestPacket &controlPacket) +{ + auto packet = new (std::nothrow) SubscribeRequest(); + if (packet == nullptr) { + return -E_OUT_OF_MEMORY; + } + QuerySyncObject querySyncObj; + int errCode = QuerySyncObject::DeSerializeData(parcel, querySyncObj); + if (errCode != E_OK) { + goto ERROR; + } + packet->SetPacketHead(controlPacket.GetSendCode(), controlPacket.GetVersion(), + static_cast(controlPacket.GetcontrolCmdType()), controlPacket.GetFlag()); + packet->SetQuery(querySyncObj); + errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + goto ERROR; + } + return errCode; +ERROR: + delete packet; + packet = nullptr; + return errCode; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_serialize_manager.h b/mock/distributeddb/syncer/src/single_ver_serialize_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..162a23da194eba92410b09b2309e607b9119ab49 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_serialize_manager.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_SERIALIZE_MANAGER_NEW_H +#define SINGLE_VER_SERIALIZE_MANAGER_NEW_H + +#include "icommunicator.h" +#include "parcel.h" +#include "single_ver_data_packet.h" + +namespace DistributedDB { +class SingleVerSerializeManager { +public: + SingleVerSerializeManager(); + virtual ~SingleVerSerializeManager(); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerSerializeManager); + + static int Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static uint32_t CalculateLen(const Message *inMsg); + + static int RegisterTransformFunc(); +private: + static bool IsPacketValid(const Message *inMsg); + + static int DataSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + static int ControlSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int DataDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + static int ControlDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static uint32_t CalculateDataLen(const Message *inMsg); + static uint32_t CalculateControlLen(const Message *inMsg); + + static int DataPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + static int DataPacketSyncerPartSerialization(Parcel &parcel, const DataRequestPacket *packet); + static int DataPacketQuerySyncSerialization(Parcel &parcel, const DataRequestPacket *packet); + static int DataPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int DataPacketQuerySyncDeSerialization(Parcel &parcel, DataRequestPacket *packet); + static int DataPacketCompressDataDeSerialization(Parcel &parcel, DataRequestPacket *packet); + static int DataPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + static int DataPacketSyncerPartDeSerialization(Parcel &parcel, DataRequestPacket *packet, uint32_t packLen, + uint32_t length, uint32_t version); + + static int AckPacketCalculateLen(const Message *inMsg, uint32_t &len); + static int AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + static int AckPacketSyncerPartSerializationV1(Parcel &parcel, const DataAckPacket *packet); + + static int AckPacketSyncerPartDeSerializationV1(Parcel &parcel, DataAckPacket &packet); + static int AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static int ControlPacketCalculateLen(const Message *inMsg, uint32_t &len); + static int ControlPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + static int ControlPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static int AckControlPacketCalculateLen(const Message *inMsg, uint32_t &len); + static int AckControlPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + static int AckControlPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static int ControlRequestSerialization(Parcel &parcel, const Message *inMsg); + static int ControlRequestDeSerialization(Parcel &parcel, ControlRequestPacket &packet); + static int SubscribeCalculateLen(const Message *inMsg, uint32_t &len); + static int SubscribeSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + static int SubscribeDeSerialization(Parcel &parcel, Message *inMsg, ControlRequestPacket &controlPacket); +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_SERIALIZE_MANAGER_NEW_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_sync_engine.cpp b/mock/distributeddb/syncer/src/single_ver_sync_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f85cb9edbbdca1ad7eeee131b960483d6013b98 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_sync_engine.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_sync_engine.h" +#include "db_common.h" +#include "single_ver_sync_task_context.h" +#include "single_ver_kv_sync_task_context.h" +#include "single_ver_relational_sync_task_context.h" +#include "log_print.h" + +namespace DistributedDB { +ISyncTaskContext *SingleVerSyncEngine::CreateSyncTaskContext() +{ + SingleVerSyncTaskContext *context = nullptr; + switch (syncInterface_->GetInterfaceType()) { + case ISyncInterface::SYNC_SVD: + context = new (std::nothrow) SingleVerKvSyncTaskContext(); + break; +#ifdef RELATIONAL_STORE + case ISyncInterface::SYNC_RELATION: + context = new (std::nothrow) SingleVerRelationalSyncTaskContext(); + break; +#endif + default: + break; + } + + if (context == nullptr) { + LOGE("[SingleVerSyncEngine][CreateSyncTaskContext] create failed, may be out of memory"); + return nullptr; + } + context->SetSyncRetry(GetSyncRetry()); + context->EnableClearRemoteStaleData(needClearRemoteStaleData_); + context->SetSubscribeManager(subManager_); + return context; +} + +void SingleVerSyncEngine::EnableClearRemoteStaleData(bool enable) +{ + LOGI("[SingleVerSyncEngine][EnableClearRemoteStaleData] enabled %d", enable); + needClearRemoteStaleData_ = enable; + std::unique_lock lock(contextMapLock_); + for (auto &iter : syncTaskContextMap_) { + auto context = static_cast(iter.second); + if (context != nullptr) { + context->EnableClearRemoteStaleData(enable); + } + } +} + +int SingleVerSyncEngine::StartAutoSubscribeTimer() +{ + std::lock_guard lockGuard(timerLock_); + if (subscribeTimerId_ > 0) { + LOGI("[SingleSyncEngine] subscribeTimerId is already set"); + return -E_INTERNAL_ERROR; + } + TimerId timerId = 0; + TimerAction timeOutCallback = std::bind(&SingleVerSyncEngine::SubscribeTimeOut, this, std::placeholders::_1); + int errCode = RuntimeContext::GetInstance()->SetTimer(SUBSCRIBE_TRIGGER_TIME_OUT, timeOutCallback, nullptr, + timerId); + if (errCode != E_OK) { + return errCode; + } + subscribeTimerId_ = timerId; + LOGI("[SingleSyncEngine] start auto subscribe timerId=%" PRIu64 " finished", timerId); + return errCode; +} + +void SingleVerSyncEngine::StopAutoSubscribeTimer() +{ + std::lock_guard lockGuard(timerLock_); + if (subscribeTimerId_ == 0) { + return; + } + LOGI("[SingleSyncEngine] stop auto subscribe timerId=%" PRIu64 " finished", subscribeTimerId_); + RuntimeContext::GetInstance()->RemoveTimer(subscribeTimerId_); + subscribeTimerId_ = 0; +} + +int SingleVerSyncEngine::SubscribeTimeOut(TimerId id) +{ + if (!queryAutoSyncCallback_) { + return E_OK; + } + std::lock_guard lockGuard(timerLock_); + std::map> allSyncQueries; + GetAllUnFinishSubQueries(allSyncQueries); + LOGI("[SingleVerSyncEngine] SubscribeTimeOut,size=%zu", allSyncQueries.size()); + if (allSyncQueries.size() == 0) { + LOGI("no need to trigger auto subscribe"); + return E_OK; + } + for (auto &item : allSyncQueries) { + for (auto &query : item.second) { + InternalSyncParma param; + GetSubscribeSyncParam(item.first, query, param); + queryAutoSyncCallback_(param); + } + } + return E_OK; +} + +void SingleVerSyncEngine::SetIsNeedResetAbilitySync(const std::string &deviceId, bool isNeedReset) +{ + ISyncTaskContext *context = GetSyncTaskContextAndInc(deviceId); + if (context != nullptr) { + context->SetIsNeedResetAbilitySync(isNeedReset); + RefObject::DecObjRef(context); + } +} +DEFINE_OBJECT_TAG_FACILITIES(SingleVerSyncEngine); +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_sync_engine.h b/mock/distributeddb/syncer/src/single_ver_sync_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..2fc8498a5847d5b8048d49c445ec3713ed857758 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_sync_engine.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_SYNC_ENGINE_H +#define SINGLE_VER_SYNC_ENGINE_H + +#include "sync_engine.h" + +namespace DistributedDB { +class SingleVerSyncEngine final : public SyncEngine { +public: + SingleVerSyncEngine() : needClearRemoteStaleData_(false) {}; + + // If set true, remote stale data will be clear when remote db rebuilt. + void EnableClearRemoteStaleData(bool enable); + + // used by SingleVerKVSyncer when db online + int StartAutoSubscribeTimer() override; + + // used by SingleVerKVSyncer when remote/local db closed + void StopAutoSubscribeTimer() override; + + int SubscribeTimeOut(TimerId id); + + void SetIsNeedResetAbilitySync(const std::string &deviceId, bool isNeedReset); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerSyncEngine); +protected: + ~SingleVerSyncEngine() override {}; + + // Create a context + ISyncTaskContext *CreateSyncTaskContext() override; + +private: + DECLARE_OBJECT_TAG(SingleVerSyncEngine); +#ifndef RUNNING_ON_TESTCASE + static constexpr int SUBSCRIBE_TRIGGER_TIME_OUT = 30 * 60 * 1000; // 30min +#else + static constexpr int SUBSCRIBE_TRIGGER_TIME_OUT = 5 * 60 * 1000; // 5min for test +#endif + + bool needClearRemoteStaleData_; + + // for subscribe timeout callback + std::mutex timerLock_; + TimerId subscribeTimerId_ = 0; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_SYNC_ENGINE_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_sync_state_machine.cpp b/mock/distributeddb/syncer/src/single_ver_sync_state_machine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2756785b567a2e06fc181f73c27f9f54866176b1 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_sync_state_machine.cpp @@ -0,0 +1,1217 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_sync_state_machine.h" + +#include +#include +#include + +#include "db_errno.h" +#include "log_print.h" +#include "sync_operation.h" +#include "message_transform.h" +#include "sync_types.h" +#include "db_common.h" +#include "runtime_context.h" +#include "performance_analysis.h" +#include "single_ver_sync_target.h" +#include "single_ver_data_sync.h" +#include "single_ver_data_sync_utils.h" + +namespace DistributedDB { +using Event = SingleVerSyncStateMachine::Event; +using State = SingleVerSyncStateMachine::State; +namespace { + // used for state switch table + const int CURRENT_STATE_INDEX = 0; + const int EVENT_INDEX = 1; + const int OUTPUT_STATE_INDEX = 2; + + // drop v1 and v2 table by one optimize, dataSend mode in all version go with slide window mode. + // State switch table v3, has three columns, CurrentState, Event, and OutSate + const std::vector> STATE_SWITCH_TABLE_V3 = { + {State::IDLE, Event::START_SYNC_EVENT, State::TIME_SYNC}, + + // In TIME_SYNC state + {State::TIME_SYNC, Event::TIME_SYNC_FINISHED_EVENT, State::ABILITY_SYNC}, + {State::TIME_SYNC, Event::TIME_OUT_EVENT, State::SYNC_TIME_OUT}, + {State::TIME_SYNC, Event::INNER_ERR_EVENT, State::INNER_ERR}, + + // In ABILITY_SYNC state, compare version num and schema + {State::ABILITY_SYNC, Event::VERSION_NOT_SUPPOR_EVENT, State::INNER_ERR}, + {State::ABILITY_SYNC, Event::ABILITY_SYNC_FINISHED_EVENT, State::START_INITIACTIVE_DATA_SYNC}, + {State::ABILITY_SYNC, Event::TIME_OUT_EVENT, State::SYNC_TIME_OUT}, + {State::ABILITY_SYNC, Event::INNER_ERR_EVENT, State::INNER_ERR}, + {State::ABILITY_SYNC, Event::CONTROL_CMD_EVENT, State::SYNC_CONTROL_CMD}, + + // In START_INITIACTIVE_DATA_SYNC state, send a sync request, and send first packt of data sync + {State::START_INITIACTIVE_DATA_SYNC, Event::NEED_ABILITY_SYNC_EVENT, State::ABILITY_SYNC}, + {State::START_INITIACTIVE_DATA_SYNC, Event::TIME_OUT_EVENT, State::SYNC_TIME_OUT}, + {State::START_INITIACTIVE_DATA_SYNC, Event::INNER_ERR_EVENT, State::INNER_ERR}, + {State::START_INITIACTIVE_DATA_SYNC, Event::SEND_FINISHED_EVENT, State::START_PASSIVE_DATA_SYNC}, + {State::START_INITIACTIVE_DATA_SYNC, Event::RE_SEND_DATA_EVENT, State::START_INITIACTIVE_DATA_SYNC}, + + // In START_PASSIVE_DATA_SYNC state, do response pull request, and send first packt of data sync + {State::START_PASSIVE_DATA_SYNC, Event::SEND_FINISHED_EVENT, State::START_PASSIVE_DATA_SYNC}, + {State::START_PASSIVE_DATA_SYNC, Event::RESPONSE_TASK_FINISHED_EVENT, State::WAIT_FOR_RECEIVE_DATA_FINISH}, + {State::START_PASSIVE_DATA_SYNC, Event::TIME_OUT_EVENT, State::SYNC_TIME_OUT}, + {State::START_PASSIVE_DATA_SYNC, Event::INNER_ERR_EVENT, State::INNER_ERR}, + {State::START_PASSIVE_DATA_SYNC, Event::NEED_ABILITY_SYNC_EVENT, State::ABILITY_SYNC}, + {State::START_PASSIVE_DATA_SYNC, Event::RE_SEND_DATA_EVENT, State::START_PASSIVE_DATA_SYNC}, + + // In WAIT_FOR_RECEIVE_DATA_FINISH, + {State::WAIT_FOR_RECEIVE_DATA_FINISH, Event::RECV_FINISHED_EVENT, State::SYNC_TASK_FINISHED}, + {State::WAIT_FOR_RECEIVE_DATA_FINISH, Event::START_PULL_RESPONSE_EVENT, State::START_PASSIVE_DATA_SYNC}, + {State::WAIT_FOR_RECEIVE_DATA_FINISH, Event::TIME_OUT_EVENT, State::SYNC_TIME_OUT}, + {State::WAIT_FOR_RECEIVE_DATA_FINISH, Event::INNER_ERR_EVENT, State::INNER_ERR}, + {State::WAIT_FOR_RECEIVE_DATA_FINISH, Event::NEED_ABILITY_SYNC_EVENT, State::ABILITY_SYNC}, + + {State::SYNC_CONTROL_CMD, Event::SEND_FINISHED_EVENT, State::SYNC_TASK_FINISHED}, + {State::SYNC_CONTROL_CMD, Event::TIME_OUT_EVENT, State::SYNC_TIME_OUT}, + {State::SYNC_CONTROL_CMD, Event::INNER_ERR_EVENT, State::INNER_ERR}, + {State::SYNC_CONTROL_CMD, Event::NEED_ABILITY_SYNC_EVENT, State::ABILITY_SYNC}, + + // In SYNC_TASK_FINISHED, + {State::SYNC_TASK_FINISHED, Event::ALL_TASK_FINISHED_EVENT, State::IDLE}, + {State::SYNC_TASK_FINISHED, Event::START_SYNC_EVENT, State::TIME_SYNC}, + + // SYNC_TIME_OUT and INNE_ERR state, just do some exception resolve + {State::SYNC_TIME_OUT, Event::ANY_EVENT, State::SYNC_TASK_FINISHED}, + {State::INNER_ERR, Event::ANY_EVENT, State::SYNC_TASK_FINISHED}, + }; +} + +std::mutex SingleVerSyncStateMachine::stateSwitchTableLock_; +std::vector SingleVerSyncStateMachine::stateSwitchTables_; +bool SingleVerSyncStateMachine::isStateSwitchTableInited_ = false; + +SingleVerSyncStateMachine::SingleVerSyncStateMachine() + : context_(nullptr), + syncInterface_(nullptr), + timeSync_(nullptr), + abilitySync_(nullptr), + dataSync_(nullptr), + currentRemoteVersionId_(0) +{ +} + +SingleVerSyncStateMachine::~SingleVerSyncStateMachine() +{ + LOGD("~SingleVerSyncStateMachine"); + Clear(); +} + +int SingleVerSyncStateMachine::Initialize(ISyncTaskContext *context, ISyncInterface *syncInterface, + std::shared_ptr &metaData, ICommunicator *communicator) +{ + if ((context == nullptr) || (syncInterface == nullptr) || (metaData == nullptr) || (communicator == nullptr)) { + return -E_INVALID_ARGS; + } + + int errCode = SyncStateMachine::Initialize(context, syncInterface, metaData, communicator); + if (errCode != E_OK) { + return errCode; + } + + timeSync_ = std::make_unique(); + dataSync_ = std::make_shared(); + abilitySync_ = std::make_unique(); + + errCode = timeSync_->Initialize(communicator, metaData, syncInterface, context->GetDeviceId()); + if (errCode != E_OK) { + goto ERROR_OUT; + } + errCode = dataSync_->Initialize(syncInterface, communicator, metaData, context->GetDeviceId()); + if (errCode != E_OK) { + goto ERROR_OUT; + } + errCode = abilitySync_->Initialize(communicator, syncInterface, metaData, context->GetDeviceId()); + if (errCode != E_OK) { + goto ERROR_OUT; + } + + currentState_ = IDLE; + context_ = static_cast(context); + syncInterface_ = static_cast(syncInterface); + + InitStateSwitchTables(); + InitStateMapping(); + return E_OK; + +ERROR_OUT: + Clear(); + return errCode; +} + +void SingleVerSyncStateMachine::SyncStep() +{ + RefObject::IncObjRef(context_); + RefObject::IncObjRef(communicator_); + int errCode = RuntimeContext::GetInstance()->ScheduleTask( + std::bind(&SingleVerSyncStateMachine::SyncStepInnerLocked, this)); + if (errCode != E_OK) { + LOGE("[StateMachine][SyncStep] Schedule SyncStep failed"); + RefObject::DecObjRef(communicator_); + RefObject::DecObjRef(context_); + } +} + +int SingleVerSyncStateMachine::ReceiveMessageCallback(Message *inMsg) +{ + int errCode = MessageCallbackPre(inMsg); + if (errCode != E_OK) { + return errCode; + } + switch (inMsg->GetMessageId()) { + case TIME_SYNC_MESSAGE: + errCode = TimeMarkSyncRecv(inMsg); + break; + case ABILITY_SYNC_MESSAGE: + errCode = AbilitySyncRecv(inMsg); + break; + case DATA_SYNC_MESSAGE: + case QUERY_SYNC_MESSAGE: + errCode = DataPktRecv(inMsg); + break; + case CONTROL_SYNC_MESSAGE: + errCode = ControlPktRecv(inMsg); + break; + default: + errCode = -E_NOT_SUPPORT; + } + return errCode; +} + +void SingleVerSyncStateMachine::SyncStepInnerLocked() +{ + if (context_->IncUsedCount() != E_OK) { + goto SYNC_STEP_OUT; + } + { + std::lock_guard lock(stateMachineLock_); + SyncStepInner(); + } + context_->SafeExit(); + +SYNC_STEP_OUT: + RefObject::DecObjRef(communicator_); + RefObject::DecObjRef(context_); +} + +void SingleVerSyncStateMachine::SyncStepInner() +{ + Event event = INNER_ERR_EVENT; + do { + auto iter = stateMapping_.find(currentState_); + if (iter != stateMapping_.end()) { + event = static_cast(iter->second()); + } else { + LOGE("[StateMachine][SyncStepInner] can not find state=%d,label=%s,dev=%s", currentState_, + dataSync_->GetLabel().c_str(), STR_MASK(context_->GetDeviceId())); + break; + } + } while (event != Event::WAIT_ACK_EVENT && SwitchMachineState(event) == E_OK && currentState_ != IDLE); +} + +void SingleVerSyncStateMachine::SetCurStateErrStatus() +{ + currentState_ = State::INNER_ERR; +} + +int SingleVerSyncStateMachine::StartSyncInner() +{ + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_MACHINE_START_TO_PUSH_SEND); + } + int errCode = PrepareNextSyncTask(); + if (errCode == E_OK) { + SwitchStateAndStep(Event::START_SYNC_EVENT); + } + return errCode; +} + +void SingleVerSyncStateMachine::AbortInner() +{ + LOGE("[StateMachine][AbortInner] error occurred,abort,label=%s,dev=%s", dataSync_->GetLabel().c_str(), + STR_MASK(context_->GetDeviceId())); + if (context_->IsKilled()) { + dataSync_->ClearDataMsg(); + } + dataSync_->ClearSyncStatus(); + ContinueToken token; + context_->GetContinueToken(token); + if (token != nullptr) { + syncInterface_->ReleaseContinueToken(token); + } + context_->SetContinueToken(nullptr); + context_->Clear(); +} + +const std::vector &SingleVerSyncStateMachine::GetStateSwitchTables() const +{ + return stateSwitchTables_; +} + +int SingleVerSyncStateMachine::PrepareNextSyncTask() +{ + int errCode = StartWatchDog(); + if (errCode != E_OK) { + LOGE("[StateMachine][PrepareNextSyncTask] WatchDog start failed,err=%d", errCode); + return errCode; + } + + if (currentState_ != State::IDLE && currentState_ != State::SYNC_TASK_FINISHED) { + LOGW("[StateMachine][PrepareNextSyncTask] PreSync may get an err, state=%" PRIu8 ",dev=%s", + currentState_, STR_MASK(context_->GetDeviceId())); + currentState_ = State::IDLE; + } + return E_OK; +} + +void SingleVerSyncStateMachine::SendSaveDataNotifyPacket(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) +{ + dataSync_->SendSaveDataNotifyPacket(context_, + std::min(context_->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT), sessionId, sequenceId, inMsgId); +} + +void SingleVerSyncStateMachine::CommErrAbort() +{ + std::lock_guard lock(stateMachineLock_); + if (SwitchMachineState(Event::INNER_ERR_EVENT) == E_OK) { + SyncStep(); + } +} + +void SingleVerSyncStateMachine::InitStateSwitchTables() +{ + if (isStateSwitchTableInited_) { + return; + } + + std::lock_guard lock(stateSwitchTableLock_); + if (isStateSwitchTableInited_) { + return; + } + + InitStateSwitchTable(SINGLE_VER_SYNC_PROCTOL_V3, STATE_SWITCH_TABLE_V3); + std::sort(stateSwitchTables_.begin(), stateSwitchTables_.end(), + [](const auto &tableA, const auto &tableB) { + return tableA.version > tableB.version; + }); // descending + isStateSwitchTableInited_ = true; +} + +void SingleVerSyncStateMachine::InitStateSwitchTable(uint32_t version, + const std::vector> &switchTable) +{ + StateSwitchTable table; + table.version = version; + for (const auto &stateSwitch : switchTable) { + if (stateSwitch.size() <= OUTPUT_STATE_INDEX) { + LOGE("[StateMachine][InitSwitchTable] stateSwitch size err,size=%zu", stateSwitch.size()); + return; + } + if (table.switchTable.count(stateSwitch[CURRENT_STATE_INDEX]) == 0) { + EventToState eventToState; // new EventToState + eventToState[stateSwitch[EVENT_INDEX]] = stateSwitch[OUTPUT_STATE_INDEX]; + table.switchTable[stateSwitch[CURRENT_STATE_INDEX]] = eventToState; + } else { // key stateSwitch[CURRENT_STATE_INDEX] already has EventToState + EventToState &eventToState = table.switchTable[stateSwitch[CURRENT_STATE_INDEX]]; + eventToState[stateSwitch[EVENT_INDEX]] = stateSwitch[OUTPUT_STATE_INDEX]; + } + } + stateSwitchTables_.push_back(table); +} + +void SingleVerSyncStateMachine::InitStateMapping() +{ + stateMapping_[TIME_SYNC] = std::bind(&SingleVerSyncStateMachine::DoTimeSync, this); + stateMapping_[ABILITY_SYNC] = std::bind(&SingleVerSyncStateMachine::DoAbilitySync, this); + stateMapping_[WAIT_FOR_RECEIVE_DATA_FINISH] = std::bind(&SingleVerSyncStateMachine::DoWaitForDataRecv, this); + stateMapping_[SYNC_TASK_FINISHED] = std::bind(&SingleVerSyncStateMachine::DoSyncTaskFinished, this); + stateMapping_[SYNC_TIME_OUT] = std::bind(&SingleVerSyncStateMachine::DoTimeout, this); + stateMapping_[INNER_ERR] = std::bind(&SingleVerSyncStateMachine::DoInnerErr, this); + stateMapping_[START_INITIACTIVE_DATA_SYNC] = + std::bind(&SingleVerSyncStateMachine::DoInitiactiveDataSyncWithSlidingWindow, this); + stateMapping_[START_PASSIVE_DATA_SYNC] = + std::bind(&SingleVerSyncStateMachine::DoPassiveDataSyncWithSlidingWindow, this); + stateMapping_[SYNC_CONTROL_CMD] = std::bind(&SingleVerSyncStateMachine::DoInitiactiveControlSync, this); +} + +Event SingleVerSyncStateMachine::DoInitiactiveDataSyncWithSlidingWindow() +{ + LOGD("[StateMachine][activeDataSync] mode=%d,label=%s,dev=%s", context_->GetMode(), + dataSync_->GetLabel().c_str(), STR_MASK(context_->GetDeviceId())); + int errCode = E_OK; + switch (context_->GetMode()) { + case SyncModeType::PUSH: + case SyncModeType::QUERY_PUSH: + context_->SetOperationStatus(SyncOperation::OP_RECV_FINISHED); + errCode = dataSync_->SyncStart(context_->GetMode(), context_); + break; + case SyncModeType::PULL: + case SyncModeType::QUERY_PULL: + context_->SetOperationStatus(SyncOperation::OP_SEND_FINISHED); + errCode = dataSync_->SyncStart(context_->GetMode(), context_); + break; + case SyncModeType::PUSH_AND_PULL: + case SyncModeType::QUERY_PUSH_PULL: + errCode = dataSync_->SyncStart(context_->GetMode(), context_); + break; + case SyncModeType::RESPONSE_PULL: + errCode = dataSync_->SyncStart(context_->GetMode(), context_); + break; + default: + errCode = -E_NOT_SUPPORT; + break; + } + if (errCode == E_OK) { + return Event::WAIT_ACK_EVENT; + } + // once E_EKEYREVOKED error occurred, PUSH_AND_PULL mode should wait for ack to pull remote data. + if (SyncOperation::TransferSyncMode(context_->GetMode()) == SyncModeType::PUSH_AND_PULL && + errCode == -E_EKEYREVOKED) { + return Event::WAIT_ACK_EVENT; + } + + // when response task step dataSync again while request task is running, ignore the errCode + bool ignoreInnerErr = (context_->GetResponseSessionId() != 0 && context_->GetRequestSessionId() != 0); + Event event = TransformErrCodeToEvent(errCode); + return (ignoreInnerErr && event == INNER_ERR_EVENT) ? SEND_FINISHED_EVENT : event; +} + +Event SingleVerSyncStateMachine::DoPassiveDataSyncWithSlidingWindow() +{ + { + RefObject::AutoLock lock(context_); + if (context_->GetRspTargetQueueSize() != 0) { + PreStartPullResponse(); + } else { + return RESPONSE_TASK_FINISHED_EVENT; + } + } + int errCode = dataSync_->SyncStart(SyncModeType::RESPONSE_PULL, context_); + if (errCode != E_OK) { + LOGW("[SingleVerSyncStateMachine][DoPassiveDataSyncWithSlidingWindow] response pull send failed[%d]", errCode); + return RESPONSE_TASK_FINISHED_EVENT; + } + return Event::WAIT_ACK_EVENT; +} + +Event SingleVerSyncStateMachine::DoInitiactiveControlSync() +{ + LOGD("[StateMachine][activeControlSync] mode=%d,label=%s,dev=%s", context_->GetMode(), + dataSync_->GetLabel().c_str(), STR_MASK(context_->GetDeviceId())); + context_->SetOperationStatus(SyncOperation::OP_RECV_FINISHED); + int errCode = dataSync_->ControlCmdStart(context_); + if (errCode == E_OK) { + return Event::WAIT_ACK_EVENT; + } + context_->SetTaskErrCode(errCode); + return TransformErrCodeToEvent(errCode); +} + +int SingleVerSyncStateMachine::HandleControlAckRecv(const Message *inMsg) +{ + std::lock_guard lock(stateMachineLock_); + if (IsNeedResetWatchdog(inMsg)) { + (void)ResetWatchDog(); + } + int errCode = dataSync_->ControlCmdAckRecv(context_, inMsg); + ControlAckRecvErrCodeHandle(errCode); + SwitchStateAndStep(TransformErrCodeToEvent(errCode)); + return E_OK; +} + +Event SingleVerSyncStateMachine::DoWaitForDataRecv() const +{ + if (context_->GetRspTargetQueueSize() != 0) { + return START_PULL_RESPONSE_EVENT; + } + if (context_->GetOperationStatus() == SyncOperation::OP_FINISHED_ALL) { + return RECV_FINISHED_EVENT; + } + if (SyncOperation::TransferSyncMode(context_->GetMode()) == SyncModeType::PUSH_AND_PULL && + context_->GetOperationStatus() == SyncOperation::OP_EKEYREVOKED_FAILURE && + context_->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0) { + return RECV_FINISHED_EVENT; + } + return Event::WAIT_ACK_EVENT; +} + +Event SingleVerSyncStateMachine::DoTimeSync() +{ + if (timeSync_->IsNeedSync()) { + CommErrHandler handler = nullptr; + // Auto sync need do retry don't use errHandler to return. + if (!context_->IsAutoSync()) { + handler = std::bind(&SyncTaskContext::CommErrHandlerFunc, std::placeholders::_1, + context_, context_->GetRequestSessionId()); + } + int errCode = timeSync_->SyncStart(handler, context_->GetRequestSessionId()); + if (errCode == E_OK) { + return Event::WAIT_ACK_EVENT; + } + context_->SetTaskErrCode(errCode); + return TransformErrCodeToEvent(errCode); + } + + return Event::TIME_SYNC_FINISHED_EVENT; +} + +Event SingleVerSyncStateMachine::DoAbilitySync() +{ + uint16_t remoteCommunicatorVersion = 0; + int errCode = communicator_->GetRemoteCommunicatorVersion(context_->GetDeviceId(), remoteCommunicatorVersion); + if (errCode != E_OK) { + LOGE("[StateMachine][DoAbilitySync] Get RemoteCommunicatorVersion errCode=%d", errCode); + return Event::INNER_ERR_EVENT; + } + // Fistr version, not support AbilitySync + if (remoteCommunicatorVersion == 0) { + context_->SetRemoteSoftwareVersion(SOFTWARE_VERSION_EARLIEST); + return GetEventAfterTimeSync(context_->GetMode()); + } + if (context_->GetIsNeedResetAbilitySync()) { + abilitySync_->SetAbilitySyncFinishedStatus(false); + context_->SetIsNeedResetAbilitySync(false); + } + if (abilitySync_->GetAbilitySyncFinishedStatus()) { + return GetEventAfterTimeSync(context_->GetMode()); + } + + CommErrHandler handler = std::bind(&SyncTaskContext::CommErrHandlerFunc, std::placeholders::_1, + context_, context_->GetRequestSessionId()); + LOGI("[StateMachine][AbilitySync] start abilitySync,label=%s,dev=%s", dataSync_->GetLabel().c_str(), + STR_MASK(context_->GetDeviceId())); + errCode = abilitySync_->SyncStart(context_->GetRequestSessionId(), context_->GetSequenceId(), + remoteCommunicatorVersion, handler); + if (errCode != E_OK) { + LOGE("[StateMachine][DoAbilitySync] ability sync start failed,errCode=%d", errCode); + return TransformErrCodeToEvent(errCode); + } + return Event::WAIT_ACK_EVENT; +} + +Event SingleVerSyncStateMachine::GetEventAfterTimeSync(int mode) const +{ + if (mode == SyncModeType::SUBSCRIBE_QUERY || mode == SyncModeType::UNSUBSCRIBE_QUERY) { + return Event::CONTROL_CMD_EVENT; + } + return Event::ABILITY_SYNC_FINISHED_EVENT; +} + +Event SingleVerSyncStateMachine::DoSyncTaskFinished() +{ + StopWatchDog(); + dataSync_->ClearSyncStatus(); + RefObject::AutoLock lock(syncContext_); + int errCode = ExecNextTask(); + if (errCode == E_OK) { + return Event::START_SYNC_EVENT; + } + return TransformErrCodeToEvent(errCode); +} + +Event SingleVerSyncStateMachine::DoTimeout() +{ + RefObject::AutoLock lock(context_); + if (context_->GetMode() == SyncModeType::SUBSCRIBE_QUERY) { + std::shared_ptr subManager = context_->GetSubscribeManager(); + if (subManager != nullptr) { + subManager->DeleteLocalSubscribeQuery(context_->GetDeviceId(), context_->GetQuery()); + } + } + context_->Abort(SyncOperation::OP_TIMEOUT); + context_->Clear(); + AbortInner(); + return Event::ANY_EVENT; +} + +Event SingleVerSyncStateMachine::DoInnerErr() +{ + RefObject::AutoLock lock(context_); + if (!context_->IsCommNormal()) { + if (context_->GetMode() == SyncModeType::SUBSCRIBE_QUERY) { + std::shared_ptr subManager = context_->GetSubscribeManager(); + if (subManager != nullptr) { + subManager->DeleteLocalSubscribeQuery(context_->GetDeviceId(), context_->GetQuery()); + } + } + context_->Abort(SyncOperation::OP_COMM_ABNORMAL); + } else { + int status = GetSyncOperationStatus(context_->GetTaskErrCode()); + context_->Abort(status); + } + context_->Clear(); + AbortInner(); + return Event::ANY_EVENT; +} + +int SingleVerSyncStateMachine::AbilitySyncRecv(const Message *inMsg) +{ + if (inMsg->GetMessageType() == TYPE_REQUEST) { + return abilitySync_->RequestRecv(inMsg, context_); + } + + if (inMsg->GetMessageType() == TYPE_RESPONSE && AbilityMsgSessionIdCheck(inMsg)) { + int errCode = abilitySync_->AckRecv(inMsg, context_); + std::lock_guard lock(stateMachineLock_); + (void)ResetWatchDog(); + if (errCode != E_OK) { + LOGE("[StateMachine][AbilitySyncRecv] handle ackRecv failed,errCode=%d", errCode); + SwitchStateAndStep(TransformErrCodeToEvent(errCode)); + } else if (context_->GetRemoteSoftwareVersion() <= SOFTWARE_VERSION_RELEASE_2_0) { + abilitySync_->SetAbilitySyncFinishedStatus(true); + LOGI("[StateMachine][AbilitySyncRecv] ability Sync Finished,label=%s,dev=%s", + dataSync_->GetLabel().c_str(), STR_MASK(context_->GetDeviceId())); + currentRemoteVersionId_ = context_->GetRemoteSoftwareVersionId(); + JumpStatusAfterAbilitySync(context_->GetMode()); + } + return E_OK; + } + if (inMsg->GetMessageType() == TYPE_NOTIFY) { + const AbilitySyncAckPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + int ackCode = packet->GetAckCode(); + if (ackCode != AbilitySync::CHECK_SUCCESS && ackCode != AbilitySync::LAST_NOTIFY) { + LOGE("[StateMachine][AbilitySyncRecv] ackCode check failed,ackCode=%d", ackCode); + std::lock_guard lock(stateMachineLock_); + SwitchStateAndStep(Event::INNER_ERR_EVENT); + return E_OK; + } + if (ackCode == AbilitySync::LAST_NOTIFY && AbilityMsgSessionIdCheck(inMsg)) { + abilitySync_->SetAbilitySyncFinishedStatus(true); + // while recv last notify means ability sync finished,it is better to reset watchDog to avoid timeout. + LOGI("[StateMachine][AbilitySyncRecv] ability sync finished,label=%s,dev=%s", + dataSync_->GetLabel().c_str(), STR_MASK(context_->GetDeviceId())); + currentRemoteVersionId_ = context_->GetRemoteSoftwareVersionId(); + (static_cast(context_))->SetIsSchemaSync(true); + std::lock_guard lock(stateMachineLock_); + (void)ResetWatchDog(); + JumpStatusAfterAbilitySync(context_->GetMode()); + } else if (ackCode != AbilitySync::LAST_NOTIFY) { + abilitySync_->AckNotifyRecv(inMsg, context_); + } + return E_OK; + } + + LOGE("[StateMachine][AbilitySyncRecv] msg type invalid"); + return -E_NOT_SUPPORT; +} + +int SingleVerSyncStateMachine::HandleDataRequestRecv(const Message *inMsg) +{ + TimeOffset offset = 0; + uint32_t timeout = communicator_->GetTimeout(context_->GetDeviceId()); + // If message is data sync request, we should check timeoffset. + int errCode = timeSync_->GetTimeOffset(offset, timeout); + if (errCode != E_OK) { + LOGE("[StateMachine][HandleDataRequestRecv] GetTimeOffset err! errCode=%d", errCode); + return errCode; + } + context_->SetTimeOffset(offset); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_DATA_REQUEST_RECV_TO_SEND_ACK); + } + DecRefCountOfFeedDogTimer(SyncDirectionFlag::RECEIVE); + { + std::lock_guard lockWatchDog(stateMachineLock_); + if (IsNeedResetWatchdog(inMsg)) { + (void)ResetWatchDog(); + } + } + + // RequestRecv will save data, it may cost a long time. + // So we need to send save data notify to keep remote alive. + bool isNeedStop = StartSaveDataNotify(inMsg->GetSessionId(), inMsg->GetSequenceId(), inMsg->GetMessageId()); + WaterMark pullEndWaterkark = 0; + errCode = dataSync_->DataRequestRecv(context_, inMsg, pullEndWaterkark); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_DATA_REQUEST_RECV_TO_SEND_ACK); + } + if (isNeedStop) { + StopSaveDataNotify(); + } + // only higher than 102 version receive this errCode here. + // while both RequestSessionId is not equal,but get this errCode;slwr would seem to handle first secquencid. + // so while receive the same secquencid after abiitysync it wouldn't handle. + if (errCode == -E_NEED_ABILITY_SYNC) { + return errCode; + } + std::lock_guard lock(stateMachineLock_); + DataRecvErrCodeHandle(inMsg->GetSessionId(), errCode); + if (pullEndWaterkark > 0) { + AddPullResponseTarget(inMsg, pullEndWaterkark); + } + return E_OK; +} + +void SingleVerSyncStateMachine::HandleDataAckRecvWithSlidingWindow(int errCode, const Message *inMsg, + bool ignoreInnerErr) +{ + if (errCode == -E_RE_SEND_DATA) { // LOCAL_WATER_MARK_NOT_INIT + dataSync_->ClearSyncStatus(); + } + if (errCode == -E_NO_DATA_SEND || errCode == -E_SEND_DATA) { + int ret = dataSync_->TryContinueSync(context_, inMsg); + if (ret == -E_FINISHED) { + SwitchStateAndStep(Event::SEND_FINISHED_EVENT); + return; + } else if (ret == E_OK) { // do nothing and waiting for all ack receive + return; + } + errCode = ret; + } + ResponsePullError(errCode, ignoreInnerErr); +} + +void SingleVerSyncStateMachine::NeedAbilitySyncHandle() +{ + // if the remote device version num is overdue, + // mean the version num has been reset when syncing data, + // there should not clear the new version cache again. + if (currentRemoteVersionId_ == context_->GetRemoteSoftwareVersionId()) { + LOGI("[StateMachine] set remote version 0, currentRemoteVersionId_ = %" PRIu64, currentRemoteVersionId_); + context_->SetRemoteSoftwareVersion(0); + } else { + currentRemoteVersionId_ = context_->GetRemoteSoftwareVersionId(); + } + abilitySync_->SetAbilitySyncFinishedStatus(false); + dataSync_->ClearSyncStatus(); +} + +int SingleVerSyncStateMachine::HandleDataAckRecv(const Message *inMsg) +{ + if (inMsg->GetMessageType() == TYPE_RESPONSE) { + DecRefCountOfFeedDogTimer(SyncDirectionFlag::SEND); + } + std::lock_guard lock(stateMachineLock_); + // Unfortunately we use stateMachineLock_ in many sync process + // So a bad ack will check before the lock and wait + // And then another process is running, it will get the lock.After this process, the ack became invalid. + // If we don't check ack again, it will be delivered to dataSyncer. + if (!IsPacketValid(inMsg)) { + return -E_INVALID_ARGS; + } + if (IsNeedResetWatchdog(inMsg)) { + (void)ResetWatchDog(); + } + if (context_->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0 && !dataSync_->AckPacketIdCheck(inMsg)) { + // packetId not match but sequence id matched scene, means resend map has be rebuilt + // this is old ack, shoulb be dropped and wait for the same packetId sequence. + return E_OK; + } + // AckRecv will save meta data, it may cost a long time. if another thread is saving data + // So we need to send save data notify to keep remote alive. + // eg. remote do pull sync + bool isNeedStop = StartSaveDataNotify(inMsg->GetSessionId(), inMsg->GetSequenceId(), inMsg->GetMessageId()); + int errCode = dataSync_->AckRecv(context_, inMsg); + if (isNeedStop) { + StopSaveDataNotify(); + } + if (errCode == -E_NEED_ABILITY_SYNC || errCode == -E_RE_SEND_DATA) { + StopFeedDogForSync(SyncDirectionFlag::SEND); + } else if (errCode == -E_SAVE_DATA_NOTIFY) { + return errCode; + } + // when this msg is from response task while request task is running, ignore the errCode + bool ignoreInnerErr = inMsg->GetSessionId() == context_->GetResponseSessionId() && + context_->GetRequestSessionId() != 0; + DataAckRecvErrCodeHandle(errCode, !ignoreInnerErr); + HandleDataAckRecvWithSlidingWindow(errCode, inMsg, ignoreInnerErr); + return errCode; +} + +int SingleVerSyncStateMachine::DataPktRecv(Message *inMsg) +{ + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + int errCode = E_OK; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + ScheduleMsgAndHandle(inMsg); + errCode = -E_NOT_NEED_DELETE_MSG; + break; + case TYPE_RESPONSE: + case TYPE_NOTIFY: + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_DATA_SEND_REQUEST_TO_ACK_RECV); + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_ACK_RECV_TO_USER_CALL_BACK); + } + errCode = HandleDataAckRecv(inMsg); + break; + default: + errCode = -E_INVALID_ARGS; + break; + } + return errCode; +} + +void SingleVerSyncStateMachine::ScheduleMsgAndHandle(Message *inMsg) +{ + dataSync_->PutDataMsg(inMsg); + while (true) { + bool isNeedHandle = true; + bool isNeedContinue = true; + Message *msg = dataSync_->MoveNextDataMsg(context_, isNeedHandle, isNeedContinue); + if (!isNeedContinue) { + break; + } + if (msg == nullptr) { + if (dataSync_->IsNeedReloadQueue()) { + continue; + } + break; + } + bool isNeedClearMap = false; + if (isNeedHandle) { + int errCode = HandleDataRequestRecv(msg); + if (context_->IsReceiveWaterMarkErr() || errCode == -E_NEED_ABILITY_SYNC) { + isNeedClearMap = true; + } + if (errCode == -E_TIMEOUT) { + isNeedHandle = false; + } + } else { + dataSync_->SendFinishedDataAck(context_, msg); + } + if (context_->GetRemoteSoftwareVersion() < SOFTWARE_VERSION_RELEASE_3_0) { + // for lower version, no need to handle map schedule info, just reset schedule working status + isNeedHandle = false; + } + dataSync_->ScheduleInfoHandle(isNeedHandle, isNeedClearMap, msg); + delete msg; + } +} + +int SingleVerSyncStateMachine::ControlPktRecv(Message *inMsg) +{ + int errCode = E_OK; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + errCode = dataSync_->ControlCmdRequestRecv(context_, inMsg); + break; + case TYPE_RESPONSE: + errCode = HandleControlAckRecv(inMsg); + break; + default: + errCode = -E_INVALID_ARGS; + break; + } + return errCode; +} + +void SingleVerSyncStateMachine::StepToTimeout(TimerId timerId) +{ + std::lock_guard lock(stateMachineLock_); + TimerId timer = syncContext_->GetTimerId(); + if (timer != timerId) { + return; + } + SwitchStateAndStep(Event::TIME_OUT_EVENT); +} + +int SingleVerSyncStateMachine::GetSyncOperationStatus(int errCode) const +{ + static const std::map statusMap = { + { -E_SCHEMA_MISMATCH, SyncOperation::OP_SCHEMA_INCOMPATIBLE }, + { -E_EKEYREVOKED, SyncOperation::OP_EKEYREVOKED_FAILURE }, + { -E_SECURITY_OPTION_CHECK_ERROR, SyncOperation::OP_SECURITY_OPTION_CHECK_FAILURE }, + { -E_BUSY, SyncOperation::OP_BUSY_FAILURE }, + { -E_NOT_PERMIT, SyncOperation::OP_PERMISSION_CHECK_FAILED }, + { -E_TIMEOUT, SyncOperation::OP_TIMEOUT }, + { -E_INVALID_QUERY_FORMAT, SyncOperation::OP_QUERY_FORMAT_FAILURE }, + { -E_INVALID_QUERY_FIELD, SyncOperation::OP_QUERY_FIELD_FAILURE }, + { -E_FEEDBACK_UNKNOWN_MESSAGE, SyncOperation::OP_NOT_SUPPORT }, + { -E_FEEDBACK_COMMUNICATOR_NOT_FOUND, SyncOperation::OP_COMM_ABNORMAL }, + { -E_NOT_SUPPORT, SyncOperation::OP_NOT_SUPPORT }, + { -E_INTERCEPT_DATA_FAIL, SyncOperation::OP_INTERCEPT_DATA_FAIL }, + { -E_MAX_LIMITS, SyncOperation::OP_MAX_LIMITS }, + { -E_DISTRIBUTED_SCHEMA_CHANGED, SyncOperation::OP_SCHEMA_CHANGED }, + { -E_NOT_REGISTER, SyncOperation::OP_NOT_SUPPORT }, + }; + auto iter = statusMap.find(errCode); + if (iter != statusMap.end()) { + return iter->second; + } + return SyncOperation::OP_FAILED; +} + +int SingleVerSyncStateMachine::TimeMarkSyncRecv(const Message *inMsg) +{ + LOGD("[StateMachine][TimeMarkSyncRecv] type=%d,label=%s,dev=%s", inMsg->GetMessageType(), + dataSync_->GetLabel().c_str(), STR_MASK(context_->GetDeviceId())); + { + std::lock_guard lock(stateMachineLock_); + (void)ResetWatchDog(); + } + if (inMsg->GetMessageType() == TYPE_REQUEST) { + return timeSync_->RequestRecv(inMsg); + } else if (inMsg->GetMessageType() == TYPE_RESPONSE) { + int errCode = timeSync_->AckRecv(inMsg, context_->GetRequestSessionId()); + if (errCode != E_OK) { + LOGE("[StateMachine][TimeMarkSyncRecv] AckRecv failed errCode=%d", errCode); + if (inMsg->GetSessionId() != 0 && inMsg->GetSessionId() == context_->GetRequestSessionId()) { + context_->SetTaskErrCode(errCode); + CommErrAbort(); + } + return errCode; + } + std::lock_guard lock(stateMachineLock_); + SwitchStateAndStep(TIME_SYNC_FINISHED_EVENT); + return E_OK; + } else { + return -E_INVALID_ARGS; + } +} + +void SingleVerSyncStateMachine::Clear() +{ + dataSync_ = nullptr; + timeSync_ = nullptr; + abilitySync_ = nullptr; + context_ = nullptr; + syncInterface_ = nullptr; +} + +bool SingleVerSyncStateMachine::IsPacketValid(const Message *inMsg) const +{ + if (inMsg == nullptr) { + return false; + } + + if ((inMsg->GetMessageId() < TIME_SYNC_MESSAGE) || (inMsg->GetMessageId() >= UNKNOW_MESSAGE)) { + LOGE("[StateMachine][IsPacketValid] Message is invalid, id=%d", inMsg->GetMessageId()); + return false; + } + // filter invalid ack at first + bool isResponseType = (inMsg->GetMessageType() == TYPE_RESPONSE); + if (isResponseType && (inMsg->GetMessageId() == CONTROL_SYNC_MESSAGE) && + (inMsg->GetSessionId() != context_->GetRequestSessionId())) { + LOGE("[StateMachine][IsPacketValid] Control Message is invalid, label=%s, dev=%s", + dataSync_->GetLabel().c_str(), STR_MASK(context_->GetDeviceId())); + return false; + } + if (isResponseType && (inMsg->GetMessageId() != TIME_SYNC_MESSAGE) && + (inMsg->GetSessionId() != context_->GetRequestSessionId()) && + (inMsg->GetSessionId() != context_->GetResponseSessionId())) { + LOGE("[StateMachine][IsPacketValid] Data Message is invalid, label=%s, dev=%s", + dataSync_->GetLabel().c_str(), STR_MASK(context_->GetDeviceId())); + return false; + } + + // timeSync and abilitySync don't need to check sequenceId + if (inMsg->GetMessageId() == TIME_SYNC_MESSAGE || inMsg->GetMessageId() == ABILITY_SYNC_MESSAGE || + inMsg->GetMessageId() == CONTROL_SYNC_MESSAGE) { + return true; + } + // sequenceId will be checked in dataSync + return true; +} + +void SingleVerSyncStateMachine::PreStartPullResponse() +{ + SingleVerSyncTarget target; + context_->PopResponseTarget(target); + context_->SetEndMark(target.GetEndWaterMark()); + context_->SetResponseSessionId(target.GetResponseSessionId()); + context_->SetMode(SyncModeType::RESPONSE_PULL); + context_->ReSetSequenceId(); + context_->SetQuerySync(target.IsQuerySync()); + context_->SetQuery(target.GetQuery()); +} + +bool SingleVerSyncStateMachine::CheckIsStartPullResponse() const +{ + // Other state will step to do pull response, only this statem we need to step the statemachine + if (currentState_ == WAIT_FOR_RECEIVE_DATA_FINISH) { + return true; + } + return false; +} + +int SingleVerSyncStateMachine::MessageCallbackPre(const Message *inMsg) +{ + RefObject::AutoLock lock(context_); + if (context_->IsKilled()) { + return -E_OBJ_IS_KILLED; + } + + if (!IsPacketValid(inMsg)) { + return -E_INVALID_ARGS; + } + return E_OK; +} + +void SingleVerSyncStateMachine::AddPullResponseTarget(const Message *inMsg, WaterMark pullEndWatermark) +{ + int messageType = static_cast(inMsg->GetMessageId()); + uint32_t sessionId = inMsg->GetSessionId(); + if (pullEndWatermark == 0) { + LOGE("[StateMachine][AddPullResponseTarget] pullEndWatermark is 0!"); + return; + } + if (context_->GetResponseSessionId() == sessionId || context_->FindResponseSyncTarget(sessionId)) { + LOGI("[StateMachine][AddPullResponseTarget] task is already running"); + return; + } + const DataRequestPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + LOGE("[AddPullResponseTarget] get packet object failed"); + return; + } + SingleVerSyncTarget *targetTmp = new (std::nothrow) SingleVerSyncTarget; + if (targetTmp == nullptr) { + LOGE("[StateMachine][AddPullResponseTarget] add failed, may oom"); + return; + } + targetTmp->SetTaskType(ISyncTarget::RESPONSE); + if (messageType == QUERY_SYNC_MESSAGE) { + targetTmp->SetQuery(packet->GetQuery()); + targetTmp->SetQuerySync(true); + } + targetTmp->SetMode(SyncModeType::RESPONSE_PULL); + targetTmp->SetEndWaterMark(pullEndWatermark); + targetTmp->SetResponseSessionId(sessionId); + if (context_->AddSyncTarget(targetTmp) != E_OK) { + delete targetTmp; + return; + } + if (CheckIsStartPullResponse()) { + SwitchStateAndStep(TransformErrCodeToEvent(-E_NEED_PULL_REPONSE)); + } +} + +Event SingleVerSyncStateMachine::TransformErrCodeToEvent(int errCode) +{ + switch (errCode) { + case -E_TIMEOUT: + return TransforTimeOutErrCodeToEvent(); + case -VERSION_NOT_SUPPOR_EVENT: + return Event::VERSION_NOT_SUPPOR_EVENT; + case -E_SEND_DATA: + return Event::SEND_DATA_EVENT; + case -E_NO_DATA_SEND: + return Event::SEND_FINISHED_EVENT; + case -E_RECV_FINISHED: + return Event::RECV_FINISHED_EVENT; + case -E_NEED_ABILITY_SYNC: + return Event::NEED_ABILITY_SYNC_EVENT; + case -E_NO_SYNC_TASK: + return Event::ALL_TASK_FINISHED_EVENT; + case -E_NEED_PULL_REPONSE: + return Event::START_PULL_RESPONSE_EVENT; + case -E_RE_SEND_DATA: + return Event::RE_SEND_DATA_EVENT; + default: + return Event::INNER_ERR_EVENT; + } +} + +bool SingleVerSyncStateMachine::IsNeedResetWatchdog(const Message *inMsg) const +{ + if (inMsg == nullptr) { + return false; + } + + if (IsNeedErrCodeHandle(inMsg->GetSessionId())) { + return true; + } + + int msgType = inMsg->GetMessageType(); + if (msgType == TYPE_RESPONSE || msgType == TYPE_NOTIFY) { + if (inMsg->GetSessionId() == context_->GetResponseSessionId()) { + // Pull response ack also should reset watchdog + return true; + } + } + + return false; +} + +Event SingleVerSyncStateMachine::TransforTimeOutErrCodeToEvent() +{ + if (syncContext_->IsSyncTaskNeedRetry() && (syncContext_->GetRetryTime() < syncContext_->GetSyncRetryTimes())) { + return Event::WAIT_TIME_OUT_EVENT; + } else { + return Event::TIME_OUT_EVENT; + } +} + +bool SingleVerSyncStateMachine::IsNeedErrCodeHandle(uint32_t sessionId) const +{ + // omit to set sessionId so version_3 should skip to compare sessionid. + if (sessionId == context_->GetRequestSessionId() || + context_->GetRemoteSoftwareVersion() == SOFTWARE_VERSION_RELEASE_2_0) { + return true; + } + return false; +} + +void SingleVerSyncStateMachine::PushPullDataRequestEvokeErrHandle() +{ + // the pushpull sync task should wait for send finished after remote dev get data occur E_EKEYREVOKED error. + if (context_->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0 && + SyncOperation::TransferSyncMode(context_->GetMode()) == SyncModeType::PUSH_AND_PULL) { + LOGI("data request errCode = %d, wait for send finished", -E_EKEYREVOKED); + context_->SetTaskErrCode(-E_EKEYREVOKED); + context_->SetOperationStatus(SyncOperation::OP_RECV_FINISHED); + SwitchStateAndStep(Event::RECV_FINISHED_EVENT); + } else { + context_->SetTaskErrCode(-E_EKEYREVOKED); + SwitchStateAndStep(Event::INNER_ERR_EVENT); + } +} + +void SingleVerSyncStateMachine::DataRecvErrCodeHandle(uint32_t sessionId, int errCode) +{ + if (IsNeedErrCodeHandle(sessionId)) { + switch (errCode) { + case E_OK: + break; + case -E_RECV_FINISHED: + context_->SetOperationStatus(SyncOperation::OP_RECV_FINISHED); + SwitchStateAndStep(Event::RECV_FINISHED_EVENT); + break; + case -E_EKEYREVOKED: + PushPullDataRequestEvokeErrHandle(); + break; + case -E_BUSY: + case -E_DISTRIBUTED_SCHEMA_CHANGED: + case -E_DISTRIBUTED_SCHEMA_NOT_FOUND: + case -E_FEEDBACK_COMMUNICATOR_NOT_FOUND: + case -E_FEEDBACK_UNKNOWN_MESSAGE: + case -E_INTERCEPT_DATA_FAIL: + case -E_INVALID_QUERY_FIELD: + case -E_INVALID_QUERY_FORMAT: + case -E_MAX_LIMITS: + case -E_NOT_REGISTER: + case -E_NOT_SUPPORT: + case -E_SECURITY_OPTION_CHECK_ERROR: + context_->SetTaskErrCode(errCode); + SwitchStateAndStep(Event::INNER_ERR_EVENT); + break; + default: + SwitchStateAndStep(Event::INNER_ERR_EVENT); + break; + } + } +} + +bool SingleVerSyncStateMachine::AbilityMsgSessionIdCheck(const Message *inMsg) +{ + if (inMsg != nullptr && inMsg->GetSessionId() == context_->GetRequestSessionId()) { + return true; + } + LOGE("[AbilitySync] session check failed,dev=%s", STR_MASK(context_->GetDeviceId())); + return false; +} + +SyncType SingleVerSyncStateMachine::GetSyncType(uint32_t messageId) const +{ + if (messageId == QUERY_SYNC_MESSAGE) { + return SyncType::QUERY_SYNC_TYPE; + } + return SyncType::MANUAL_FULL_SYNC_TYPE; +} + +void SingleVerSyncStateMachine::DataAckRecvErrCodeHandle(int errCode, bool handleError) +{ + switch (errCode) { + case -E_NEED_ABILITY_SYNC: + NeedAbilitySyncHandle(); + break; + case -E_NOT_PERMIT: + if (handleError) { + context_->SetOperationStatus(SyncOperation::OP_PERMISSION_CHECK_FAILED); + } + break; + case -E_BUSY: + case -E_DISTRIBUTED_SCHEMA_CHANGED: + case -E_DISTRIBUTED_SCHEMA_NOT_FOUND: + case -E_EKEYREVOKED: + case -E_FEEDBACK_COMMUNICATOR_NOT_FOUND: + case -E_FEEDBACK_UNKNOWN_MESSAGE: + case -E_INTERCEPT_DATA_FAIL: + case -E_INVALID_QUERY_FIELD: + case -E_INVALID_QUERY_FORMAT: + case -E_MAX_LIMITS: + case -E_NOT_REGISTER: + case -E_NOT_SUPPORT: + case -E_SECURITY_OPTION_CHECK_ERROR: + if (handleError) { + context_->SetTaskErrCode(errCode); + } + break; + default: + break; + } +} + +bool SingleVerSyncStateMachine::IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query) +{ + return SingleVerDataSyncUtils::IsNeedTriggerQueryAutoSync(inMsg, query); +} + +void SingleVerSyncStateMachine::JumpStatusAfterAbilitySync(int mode) +{ + if ((mode == SyncModeType::SUBSCRIBE_QUERY) || (mode == SyncModeType::UNSUBSCRIBE_QUERY)) { + SwitchStateAndStep(CONTROL_CMD_EVENT); + } else { + SwitchStateAndStep(ABILITY_SYNC_FINISHED_EVENT); + } +} + +void SingleVerSyncStateMachine::ControlAckRecvErrCodeHandle(int errCode) +{ + switch (errCode) { + case -E_NEED_ABILITY_SYNC: + NeedAbilitySyncHandle(); + break; + case -E_NO_DATA_SEND: + context_->SetOperationStatus(SyncOperation::OP_SEND_FINISHED); + break; + case -E_NOT_PERMIT: + context_->SetOperationStatus(SyncOperation::OP_PERMISSION_CHECK_FAILED); + break; + // other errCode use default + default: + context_->SetTaskErrCode(errCode); + break; + } +} + +void SingleVerSyncStateMachine::GetLocalWaterMark(const DeviceID &deviceId, uint64_t &outValue) +{ + metadata_->GetLocalWaterMark(deviceId, outValue); +} + +int SingleVerSyncStateMachine::GetSendQueryWaterMark(const std::string &queryId, const DeviceID &deviceId, + bool isAutoLift, uint64_t &outValue) +{ + return metadata_->GetSendQueryWaterMark(queryId, deviceId, outValue, isAutoLift); +} + +void SingleVerSyncStateMachine::ResponsePullError(int errCode, bool ignoreInnerErr) +{ + Event event = TransformErrCodeToEvent(errCode); + SwitchStateAndStep((ignoreInnerErr && event == INNER_ERR_EVENT) ? RESPONSE_TASK_FINISHED_EVENT : event); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/single_ver_sync_state_machine.h b/mock/distributeddb/syncer/src/single_ver_sync_state_machine.h new file mode 100644 index 0000000000000000000000000000000000000000..c847b5a86391e99100ad5fe6c63ea5b81ff6b628 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_sync_state_machine.h @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_SYNC_STATE_MACHINE_H +#define SINGLE_VER_SYNC_STATE_MACHINE_H + +#include +#include + +#include "ability_sync.h" +#include "message.h" +#include "meta_data.h" +#include "semaphore_utils.h" +#include "single_ver_data_sync.h" +#include "single_ver_sync_task_context.h" +#include "sync_state_machine.h" +#include "sync_target.h" + +#include "time_sync.h" +#include "time_helper.h" + +namespace DistributedDB { +using StateMappingHandler = std::function; +class SingleVerSyncStateMachine : public SyncStateMachine { +public: + enum State { + IDLE = 0, + TIME_SYNC, + ABILITY_SYNC, + WAIT_FOR_RECEIVE_DATA_FINISH, // all data send finished, wait for data revice if has pull request + SYNC_TASK_FINISHED, // current sync task finihsed, try to schedule next sync task + SYNC_TIME_OUT, + INNER_ERR, + START_INITIACTIVE_DATA_SYNC, // used to do sync started by local device, use sliding window + START_PASSIVE_DATA_SYNC, // used to do pull response, use sliding window + SYNC_CONTROL_CMD // used to send control cmd. + }; + + enum Event { + START_SYNC_EVENT = 1, + TIME_SYNC_FINISHED_EVENT, + ABILITY_SYNC_FINISHED_EVENT, + VERSION_NOT_SUPPOR_EVENT, + SEND_DATA_EVENT, + SEND_FINISHED_EVENT, + RECV_FINISHED_EVENT, + NEED_ABILITY_SYNC_EVENT, + RESPONSE_TASK_FINISHED_EVENT, + START_PULL_RESPONSE_EVENT, + WAIT_ACK_EVENT, + ALL_TASK_FINISHED_EVENT, + TIME_OUT_EVENT, + INNER_ERR_EVENT, + WAIT_TIME_OUT_EVENT, + RE_SEND_DATA_EVENT, + CONTROL_CMD_EVENT, + ANY_EVENT + }; + SingleVerSyncStateMachine(); + ~SingleVerSyncStateMachine() override; + + // Init the SingleVerSyncStateMachine + int Initialize(ISyncTaskContext *context, ISyncInterface *syncInterface, std::shared_ptr &metadata, + ICommunicator *communicator) override; + + // send Message to the StateMachine + int ReceiveMessageCallback(Message *inMsg) override; + + // Called by CommErrHandler, used to abort sync when handle err + void CommErrAbort() override; + + int HandleDataRequestRecv(const Message *inMsg); + + bool IsNeedErrCodeHandle(uint32_t sessionId) const; + + void PushPullDataRequestEvokeErrHandle(); + + void DataRecvErrCodeHandle(uint32_t sessionId, int errCode); + + bool IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query) override; + + void GetLocalWaterMark(const DeviceID &deviceId, uint64_t &outValue); + + int GetSendQueryWaterMark(const std::string &queryId, const DeviceID &deviceId, bool isAutoLift, + uint64_t &outValue); + +protected: + // Step the SingleVerSyncStateMachine + void SyncStep() override; + + // SyncOperation is timeout, step to timeout state + void StepToTimeout(TimerId timerId) override; + + void SyncStepInnerLocked() override; + + // Do state machine step with no lock, for inner use + void SyncStepInner() override; + + int StartSyncInner() override; + + void AbortInner() override; + + void SetCurStateErrStatus() override; + + // Used to get instance class' stateSwitchTables + const std::vector &GetStateSwitchTables() const override; + + // Do some init for run a next sync task + int PrepareNextSyncTask() override; + + // Called by StartSaveDataNotifyTimer, used to send a save data notify packet + void SendSaveDataNotifyPacket(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) override; + + int TimeMarkSyncRecv(const Message *inMsg); + + void DataAckRecvErrCodeHandle(int errCode, bool handleError); + +private: + // Used to init sync state machine switchbables + static void InitStateSwitchTables(); + + // To generate the statemachine switchtable with the given version + static void InitStateSwitchTable(uint32_t version, const std::vector> &switchTable); + + void InitStateMapping(); + + // Do TimeSync, for first sync + Event DoTimeSync(); + + // Do AbilitySync, for first sync + Event DoAbilitySync(); + + // Waiting for pull data revice finish, if coming a pull request, should goto START_PASSIVE_DATA_SYNC state + Event DoWaitForDataRecv() const; + + // Sync task finihsed, should do some data clear and exec next task. + Event DoSyncTaskFinished(); + + // Do something when sync timeout. + Event DoTimeout(); + + // Do something when sync get some err. + Event DoInnerErr(); + + Event DoInitiactiveDataSyncWithSlidingWindow(); + + Event DoPassiveDataSyncWithSlidingWindow(); + + Event DoInitiactiveControlSync(); + + Event GetEventAfterTimeSync(int mode) const; + + int HandleControlAckRecv(const Message *inMsg); + + int GetSyncOperationStatus(int errCode) const; + + int AbilitySyncRecv(const Message *inMsg); + + int DataPktRecv(Message *inMsg); + + void ScheduleMsgAndHandle(Message *inMsg); + + int ControlPktRecv(Message *inMsg); + + void NeedAbilitySyncHandle(); + + int HandleDataAckRecv(const Message *inMsg); + + void HandleDataAckRecvWithSlidingWindow(int errCode, const Message *inMsg, bool ignoreInnerErr); + + void ResponsePullError(int errCode, bool ignoreInnerErr); + + void Clear(); + + bool IsPacketValid(const Message *inMsg) const; + + void PreStartPullResponse(); + + bool CheckIsStartPullResponse() const; + + int MessageCallbackPre(const Message *inMsg); + + void AddPullResponseTarget(const Message *inMsg, WaterMark pullEndWatermark); + + Event TransformErrCodeToEvent(int errCode); + + bool IsNeedResetWatchdog(const Message *inMsg) const; + + Event TransforTimeOutErrCodeToEvent(); + + bool AbilityMsgSessionIdCheck(const Message *inMsg); + + SyncType GetSyncType(uint32_t messageId) const; + + void JumpStatusAfterAbilitySync(int mode); + + void ControlAckRecvErrCodeHandle(int errCode); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerSyncStateMachine); + + static std::mutex stateSwitchTableLock_; + static bool isStateSwitchTableInited_; + static std::vector stateSwitchTables_; + SingleVerSyncTaskContext *context_; + SingleVerKvDBSyncInterface *syncInterface_; + std::unique_ptr timeSync_; + std::unique_ptr abilitySync_; + std::shared_ptr dataSync_; + uint64_t currentRemoteVersionId_; + std::map stateMapping_; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_SYNC_STATE_MACHINE_H diff --git a/mock/distributeddb/syncer/src/single_ver_sync_target.cpp b/mock/distributeddb/syncer/src/single_ver_sync_target.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2771ec16416d668de2d6861720751caeb18ff7ad --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_sync_target.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_sync_target.h" + +#include + +#include "db_errno.h" +#include "sync_operation.h" +#include "log_print.h" + +namespace DistributedDB { +SingleVerSyncTarget::SingleVerSyncTarget() + : endWaterMark_(0), + query_(QuerySyncObject()) +{ +} + +SingleVerSyncTarget::~SingleVerSyncTarget() +{ +} + +void SingleVerSyncTarget::SetSyncOperation(SyncOperation *operation) +{ + SyncTarget::SetSyncOperation(operation); + if ((operation != nullptr) && !operation->IsKilled()) { + query_ = operation->GetQuery(); + isQuerySync_ = operation->IsQuerySync(); + } +} + +void SingleVerSyncTarget::SetEndWaterMark(WaterMark waterMark) +{ + endWaterMark_ = waterMark; +} + +WaterMark SingleVerSyncTarget::GetEndWaterMark() const +{ + return endWaterMark_; +} + +void SingleVerSyncTarget::SetResponseSessionId(uint32_t responseSessionId) +{ + responseSessionId_ = responseSessionId; +} + +uint32_t SingleVerSyncTarget::GetResponseSessionId() const +{ + return responseSessionId_; +} + +void SingleVerSyncTarget::SetQuery(const QuerySyncObject &query) +{ + query_ = query; +} + +QuerySyncObject SingleVerSyncTarget::GetQuery() const +{ + return query_; +} + +void SingleVerSyncTarget::SetQuerySync(bool isQuerySync) +{ + isQuerySync_ = isQuerySync; +} + +bool SingleVerSyncTarget::IsQuerySync() const +{ + return isQuerySync_; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/single_ver_sync_target.h b/mock/distributeddb/syncer/src/single_ver_sync_target.h new file mode 100644 index 0000000000000000000000000000000000000000..d0c405c6b8f59ec8ff7b6a5088ae935edcb8efcf --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_sync_target.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_SYNC_TARGET_H +#define SINGLE_VER_SYNC_TARGET_H + +#include "db_types.h" +#include "query_sync_object.h" +#include "sync_target.h" + +namespace DistributedDB { +class SingleVerSyncTarget final : public SyncTarget { +public: + SingleVerSyncTarget(); + ~SingleVerSyncTarget() override; + + // Set a syncOperation + void SetSyncOperation(SyncOperation *operation) override; + + // Set the end water mark of this task + void SetEndWaterMark(WaterMark waterMark); + + // Get the end water mark of this task + WaterMark GetEndWaterMark() const; + + // For pull response sync + void SetResponseSessionId(uint32_t responseSessionId); + uint32_t GetResponseSessionId() const override; + + // For query sync + void SetQuery(const QuerySyncObject &query); + QuerySyncObject GetQuery() const; + void SetQuerySync(bool isQuerySync); + bool IsQuerySync() const; +private: + WaterMark endWaterMark_; + uint32_t responseSessionId_ = 0; + QuerySyncObject query_; + bool isQuerySync_ = false; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_SYNC_TARGET_H diff --git a/mock/distributeddb/syncer/src/single_ver_sync_task_context.cpp b/mock/distributeddb/syncer/src/single_ver_sync_task_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a14dd9937dfacc2949eaaae3026e475b20e0ac0a --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_sync_task_context.cpp @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "single_ver_sync_task_context.h" + +#include +#include "db_common.h" +#include "db_dfx_adapter.h" +#include "db_errno.h" +#include "log_print.h" +#include "isyncer.h" +#include "single_ver_sync_state_machine.h" +#include "single_ver_sync_target.h" +#include "sync_types.h" + +namespace DistributedDB { +SingleVerSyncTaskContext::SingleVerSyncTaskContext() + : SyncTaskContext(), + token_(nullptr), + endMark_(0), + needClearRemoteStaleData_(false) +{} + +SingleVerSyncTaskContext::~SingleVerSyncTaskContext() +{ + token_ = nullptr; + subManager_ = nullptr; +} + +int SingleVerSyncTaskContext::Initialize(const std::string &deviceId, + ISyncInterface *syncInterface, std::shared_ptr &metadata, ICommunicator *communicator) +{ + if (deviceId.empty() || syncInterface == nullptr || metadata == nullptr || + communicator == nullptr) { + return -E_INVALID_ARGS; + } + stateMachine_ = new (std::nothrow) SingleVerSyncStateMachine; + if (stateMachine_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + deviceId_ = deviceId; + std::vector dbIdentifier = syncInterface->GetIdentifier(); + dbIdentifier.resize(3); // only show 3 bytes + syncActionName_ = DBDfxAdapter::SYNC_ACTION + "_" + + DBCommon::VectorToHexString(dbIdentifier) + "_" + deviceId_.c_str(); + TimerAction timeOutCallback; + int errCode = stateMachine_->Initialize(this, syncInterface, metadata, communicator); + if (errCode != E_OK) { + LOGE("[SingleVerSyncTaskContext] stateMachine Initialize failed, err %d.", errCode); + goto ERROR_OUT; + } + + timeHelper_ = std::make_unique(); + errCode = timeHelper_->Initialize(syncInterface, metadata); + if (errCode != E_OK) { + LOGE("[SingleVerSyncTaskContext] timeHelper Initialize failed, err %d.", errCode); + goto ERROR_OUT; + } + timeOutCallback = std::bind(&SyncStateMachine::TimeoutCallback, + static_cast(stateMachine_), + std::placeholders::_1); + SetTimeoutCallback(timeOutCallback); + + syncInterface_ = syncInterface; + communicator_ = communicator; + taskExecStatus_ = INIT; + OnKill([this]() { this->KillWait(); }); + { + std::lock_guard lock(synTaskContextSetLock_); + synTaskContextSet_.insert(this); + } + return errCode; + +ERROR_OUT: + delete stateMachine_; + stateMachine_ = nullptr; + return errCode; +} + +int SingleVerSyncTaskContext::AddSyncOperation(SyncOperation *operation) +{ + if (operation == nullptr) { + return -E_INVALID_ARGS; + } + + // If auto sync, just update the end watermark + if (operation->IsAutoSync()) { + std::lock_guard lock(targetQueueLock_); + bool isQuerySync = operation->IsQuerySync(); + std::string queryId = operation->GetQueryId(); + auto iter = std::find_if(requestTargetQueue_.begin(), requestTargetQueue_.end(), + [isQuerySync, queryId](const ISyncTarget *target) { + if (target == nullptr) { + return false; + } + if (isQuerySync) { + SyncOperation *tmpOperation = nullptr; + target->GetSyncOperation(tmpOperation); + return (tmpOperation != nullptr && tmpOperation->GetQueryId() == queryId) && target->IsAutoSync(); + } + return target->IsAutoSync(); + }); + if (iter != requestTargetQueue_.end()) { + static_cast(*iter)->SetEndWaterMark(timeHelper_->GetTime()); + operation->SetStatus(deviceId_, SyncOperation::OP_FINISHED_ALL); + return E_OK; + } + } + + auto *newTarget = new (std::nothrow) SingleVerSyncTarget; + if (newTarget == nullptr) { + return -E_OUT_OF_MEMORY; + } + newTarget->SetSyncOperation(operation); + Timestamp timstamp = timeHelper_->GetTime(); + newTarget->SetEndWaterMark(timstamp); + newTarget->SetTaskType(ISyncTarget::REQUEST); + AddSyncTarget(newTarget); + return E_OK; +} + +void SingleVerSyncTaskContext::SetEndMark(WaterMark endMark) +{ + endMark_ = endMark; +} + +WaterMark SingleVerSyncTaskContext::GetEndMark() const +{ + return endMark_; +} + +void SingleVerSyncTaskContext::GetContinueToken(ContinueToken &outToken) const +{ + outToken = token_; +} + +void SingleVerSyncTaskContext::SetContinueToken(ContinueToken token) +{ + token_ = token; + return; +} + +void SingleVerSyncTaskContext::ReleaseContinueToken() +{ + if (token_ != nullptr) { + static_cast(syncInterface_)->ReleaseContinueToken(token_); + token_ = nullptr; + } +} + +int SingleVerSyncTaskContext::PopResponseTarget(SingleVerSyncTarget &target) +{ + std::lock_guard lock(targetQueueLock_); + LOGD("[SingleVerSyncTaskContext] GetFrontExtWaterMarak size = %zu", responseTargetQueue_.size()); + if (!responseTargetQueue_.empty()) { + ISyncTarget *tmpTarget = responseTargetQueue_.front(); + responseTargetQueue_.pop_front(); + target = *(static_cast(tmpTarget)); + delete tmpTarget; + tmpTarget = nullptr; + return E_OK; + } + return -E_LENGTH_ERROR; +} + +int SingleVerSyncTaskContext::GetRspTargetQueueSize() const +{ + std::lock_guard lock(targetQueueLock_); + return responseTargetQueue_.size(); +} + +void SingleVerSyncTaskContext::SetResponseSessionId(uint32_t responseSessionId) +{ + responseSessionId_ = responseSessionId; +} + +uint32_t SingleVerSyncTaskContext::GetResponseSessionId() const +{ + return responseSessionId_; +} + +void SingleVerSyncTaskContext::CopyTargetData(const ISyncTarget *target, const TaskParam &taskParam) +{ + const SingleVerSyncTarget *targetTmp = static_cast(target); + SyncTaskContext::CopyTargetData(target, taskParam); + mode_ = targetTmp->GetMode(); + endMark_ = targetTmp->GetEndWaterMark(); + if (mode_ == SyncModeType::RESPONSE_PULL) { + responseSessionId_ = targetTmp->GetResponseSessionId(); + } + query_ = targetTmp->GetQuery(); + isQuerySync_ = targetTmp->IsQuerySync(); +} + +void SingleVerSyncTaskContext::Clear() +{ + retryTime_ = 0; + ClearSyncOperation(); + SyncTaskContext::Clear(); + SetMode(SyncModeType::INVALID_MODE); + syncId_ = 0; + isAutoSync_ = false; + SetOperationStatus(SyncOperation::OP_WAITING); + SetEndMark(0); + SetResponseSessionId(0); + query_ = QuerySyncObject(); + isQuerySync_ = false; +} + +void SingleVerSyncTaskContext::Abort(int status) +{ + { + std::lock_guard lock(operationLock_); + if (syncOperation_ != nullptr) { + syncOperation_->SetStatus(deviceId_, status); + if ((status >= SyncOperation::OP_FINISHED_ALL)) { + UnlockObj(); + if (syncOperation_->CheckIsAllFinished()) { + syncOperation_->Finished(); + } + LockObj(); + } + } + } + StopFeedDogForSync(SyncDirectionFlag::SEND); + StopFeedDogForSync(SyncDirectionFlag::RECEIVE); + Clear(); +} + +void SingleVerSyncTaskContext::ClearAllSyncTask() +{ + // clear request queue sync task and responsequeue first. + std::list targetQueue; + { + std::lock_guard lock(targetQueueLock_); + LOGI("[SingleVerSyncTaskContext] request taskcount=%zu, responsecount=%zu", requestTargetQueue_.size(), + responseTargetQueue_.size()); + while (!requestTargetQueue_.empty()) { + ISyncTarget *tmpTarget = nullptr; + tmpTarget = requestTargetQueue_.front(); + requestTargetQueue_.pop_front(); + targetQueue.push_back(tmpTarget); + } + while (!responseTargetQueue_.empty()) { + ISyncTarget *tmpTarget = nullptr; + tmpTarget = responseTargetQueue_.front(); + responseTargetQueue_.pop_front(); + delete tmpTarget; + tmpTarget = nullptr; + } + } + while (!targetQueue.empty()) { + ISyncTarget *target = nullptr; + target = targetQueue.front(); + targetQueue.pop_front(); + SyncOperation *tmpOperation = nullptr; + target->GetSyncOperation(tmpOperation); + if (tmpOperation == nullptr) { + LOGE("[ClearAllSyncTask] tmpOperation is nullptr"); + continue; // not exit this scene + } + LOGI("[SingleVerSyncTaskContext] killing syncId=%d,dev=%s", tmpOperation->GetSyncId(), STR_MASK(deviceId_)); + if (target->IsAutoSync()) { + tmpOperation->SetStatus(deviceId_, SyncOperation::OP_FINISHED_ALL); + } else { + tmpOperation->SetStatus(deviceId_, SyncOperation::OP_COMM_ABNORMAL); + } + if (tmpOperation->CheckIsAllFinished()) { + tmpOperation->Finished(); + } + delete target; + target = nullptr; + } + if (GetTaskExecStatus() == SyncTaskContext::RUNNING) { + // clear syncing task. + isCommNormal_ = false; + stateMachine_->CommErrAbort(); + } + // reset last push status for sync merge + ResetLastPushTaskStatus(); +} + +void SingleVerSyncTaskContext::EnableClearRemoteStaleData(bool enable) +{ + needClearRemoteStaleData_ = enable; +} + +bool SingleVerSyncTaskContext::IsNeedClearRemoteStaleData() const +{ + return needClearRemoteStaleData_; +} + +bool SingleVerSyncTaskContext::StartFeedDogForSync(uint32_t time, SyncDirectionFlag flag) +{ + return stateMachine_->StartFeedDogForSync(time, flag); +} + +void SingleVerSyncTaskContext::StopFeedDogForSync(SyncDirectionFlag flag) +{ + stateMachine_->StopFeedDogForSync(flag); +} + +int SingleVerSyncTaskContext::HandleDataRequestRecv(const Message *msg) +{ + return static_cast(stateMachine_)->HandleDataRequestRecv(msg); +} + +bool SingleVerSyncTaskContext::IsReceiveWaterMarkErr() const +{ + return isReceiveWaterMarkErr_; +} + +void SingleVerSyncTaskContext::SetReceiveWaterMarkErr(bool isErr) +{ + isReceiveWaterMarkErr_ = isErr; +} + +void SingleVerSyncTaskContext::SetRemoteSeccurityOption(SecurityOption secOption) +{ + remoteSecOption_ = secOption; +} + +SecurityOption SingleVerSyncTaskContext::GetRemoteSeccurityOption() const +{ + return remoteSecOption_; +} + +void SingleVerSyncTaskContext::SetReceivcPermitCheck(bool isChecked) +{ + isReceivcPermitChecked_ = isChecked; +} + +bool SingleVerSyncTaskContext::GetReceivcPermitCheck() const +{ + return isReceivcPermitChecked_; +} + +void SingleVerSyncTaskContext::SetSendPermitCheck(bool isChecked) +{ + isSendPermitChecked_ = isChecked; +} + +bool SingleVerSyncTaskContext::GetSendPermitCheck() const +{ + return isSendPermitChecked_; +} + +void SingleVerSyncTaskContext::SetIsSchemaSync(bool isSchemaSync) +{ + isSchemaSync_ = isSchemaSync; +} + +bool SingleVerSyncTaskContext::GetIsSchemaSync() const +{ + return isSchemaSync_; +} + +bool SingleVerSyncTaskContext::IsSkipTimeoutError(int errCode) const +{ + if (errCode == -E_TIMEOUT && IsSyncTaskNeedRetry() && (GetRetryTime() < GetSyncRetryTimes())) { + LOGE("[SingleVerSyncTaskContext] send message timeout error occurred"); + return true; + } else { + return false; + } +} + +bool SingleVerSyncTaskContext::FindResponseSyncTarget(uint32_t responseSessionId) const +{ + std::lock_guard lock(targetQueueLock_); + auto iter = std::find_if(responseTargetQueue_.begin(), responseTargetQueue_.end(), + [responseSessionId](const ISyncTarget *target) { + return target->GetResponseSessionId() == responseSessionId; + }); + if (iter == responseTargetQueue_.end()) { + return false; + } + return true; +} + +void SingleVerSyncTaskContext::SetQuery(const QuerySyncObject &query) +{ + query_ = query; +} + +const QuerySyncObject &SingleVerSyncTaskContext::GetQuery() const +{ + return query_; +} + +void SingleVerSyncTaskContext::SetQuerySync(bool isQuerySync) +{ + isQuerySync_ = isQuerySync; +} + +bool SingleVerSyncTaskContext::IsQuerySync() const +{ + return isQuerySync_; +} + +std::set SingleVerSyncTaskContext::GetRemoteCompressAlgo() const +{ + std::set compressAlgoSet; + for (const auto &algo : SyncConfig::COMPRESSALGOMAP) { + if (remoteDbAbility_.GetAbilityItem(algo.second) == SUPPORT_MARK) { + compressAlgoSet.insert(static_cast(algo.first)); + } + } + return compressAlgoSet; +} + +std::string SingleVerSyncTaskContext::GetRemoteCompressAlgoStr() const +{ + static std::map algoMap = {{CompressAlgorithm::ZLIB, "zlib"}}; + std::set remoteCompressAlgoSet = GetRemoteCompressAlgo(); + if (remoteCompressAlgoSet.size() == 0) { + return "none"; + } + std::string currentAlgoStr; + for (const auto &algo : remoteCompressAlgoSet) { + auto iter = algoMap.find(algo); + if (iter != algoMap.end()) { + currentAlgoStr += algoMap[algo] + ","; + } + } + return currentAlgoStr.substr(0, currentAlgoStr.length() - 1); +} + +void SingleVerSyncTaskContext::SetDbAbility(DbAbility &remoteDbAbility) +{ + remoteDbAbility_ = remoteDbAbility; + LOGI("[SingleVerSyncTaskContext] set dev=%s compressAlgo=%s, IsSupAllPredicateQuery=%u," + "IsSupSubscribeQuery=%u, inKeys=%u", + STR_MASK(GetDeviceId()), GetRemoteCompressAlgoStr().c_str(), + remoteDbAbility.GetAbilityItem(SyncConfig::ALLPREDICATEQUERY), + remoteDbAbility.GetAbilityItem(SyncConfig::SUBSCRIBEQUERY), + remoteDbAbility.GetAbilityItem(SyncConfig::INKEYS_QUERY)); +} + +CompressAlgorithm SingleVerSyncTaskContext::ChooseCompressAlgo() const +{ + std::set remoteAlgo = GetRemoteCompressAlgo(); + if (remoteAlgo.size() == 0) { + return CompressAlgorithm::NONE; + } + std::set localAlgorithmSet; + (void)(static_cast(syncInterface_))->GetCompressionAlgo(localAlgorithmSet); + std::set algoIntersection; + set_intersection(remoteAlgo.begin(), remoteAlgo.end(), localAlgorithmSet.begin(), localAlgorithmSet.end(), + inserter(algoIntersection, algoIntersection.begin())); + if (algoIntersection.size() == 0) { + return CompressAlgorithm::NONE; + } + return *(algoIntersection.begin()); +} + +const DbAbility& SingleVerSyncTaskContext::GetRemoteDbAbility() const +{ + return remoteDbAbility_; +} + +void SingleVerSyncTaskContext::SetSubscribeManager(std::shared_ptr &subManager) +{ + subManager_ = subManager; +} + +std::shared_ptr SingleVerSyncTaskContext::GetSubscribeManager() const +{ + return subManager_; +} +DEFINE_OBJECT_TAG_FACILITIES(SingleVerSyncTaskContext) + +bool SingleVerSyncTaskContext::IsCurrentSyncTaskCanBeSkipped() const +{ + if (mode_ == SyncModeType::PUSH) { + if (lastFullSyncTaskStatus_ != SyncOperation::OP_FINISHED_ALL) { + return false; + } + } else if (mode_ == SyncModeType::QUERY_PUSH) { + if (syncOperation_ == nullptr) { + return true; + } + auto it = lastQuerySyncTaskStatusMap_.find(syncOperation_->GetQueryId()); + if (it == lastQuerySyncTaskStatusMap_.end()) { + // no last query_push and push + if (lastFullSyncTaskStatus_ != SyncOperation::OP_FINISHED_ALL) { + LOGD("no prev query push or successful prev push"); + return false; + } + } else { + if (it->second != SyncOperation::OP_FINISHED_ALL) { + LOGD("last query push status = %d.", it->second); + return false; + } + } + } else { + return false; + } + + Timestamp maxTimestampInDb; + syncInterface_->GetMaxTimestamp(maxTimestampInDb); + uint64_t localWaterMark = 0; + int errCode = GetCorrectedSendWaterMarkForCurrentTask(localWaterMark); + if (errCode != E_OK) { + LOGE("GetLocalWaterMark in state machine failed: %d", errCode); + return false; + } + if (localWaterMark > maxTimestampInDb) { + LOGI("skip current push task, deviceId_ = %s, localWaterMark = %" PRIu64 ", maxTimestampInDb = %" PRIu64, + STR_MASK(deviceId_), localWaterMark, maxTimestampInDb); + return true; + } + return false; +} + +void SingleVerSyncTaskContext::SaveLastPushTaskExecStatus(int finalStatus) +{ + if (IsTargetQueueEmpty()) { + LOGD("sync que is empty, reset last push status"); + ResetLastPushTaskStatus(); + return; + } + if (mode_ == SyncModeType::PUSH || mode_ == SyncModeType::PUSH_AND_PULL || mode_ == SyncModeType::RESPONSE_PULL) { + lastFullSyncTaskStatus_ = finalStatus; + } else if (mode_ == SyncModeType::QUERY_PUSH || mode_ == SyncModeType::QUERY_PUSH_PULL) { + lastQuerySyncTaskStatusMap_[syncOperation_->GetQueryId()] = finalStatus; + } +} + +int SingleVerSyncTaskContext::GetCorrectedSendWaterMarkForCurrentTask(uint64_t &waterMark) const +{ + if (syncOperation_->IsQuerySync()) { + LOGD("Is QuerySync"); + int errCode = static_cast(stateMachine_)->GetSendQueryWaterMark( + syncOperation_->GetQueryId(), deviceId_, + lastFullSyncTaskStatus_ == SyncOperation::OP_FINISHED_ALL, waterMark); + if (errCode != E_OK) { + return errCode; + } + } else { + LOGD("Not QuerySync"); + static_cast(stateMachine_)->GetLocalWaterMark(deviceId_, waterMark); + } + return E_OK; +} + +void SingleVerSyncTaskContext::ResetLastPushTaskStatus() +{ + lastFullSyncTaskStatus_ = SyncOperation::OP_WAITING; + lastQuerySyncTaskStatusMap_.clear(); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/single_ver_sync_task_context.h b/mock/distributeddb/syncer/src/single_ver_sync_task_context.h new file mode 100644 index 0000000000000000000000000000000000000000..f0619a41087daf8e063edc5cc4de81b50ff63262 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_sync_task_context.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_SYNC_TASK_CONTEXT_H +#define SINGLE_VER_SYNC_TASK_CONTEXT_H + +#include +#include +#include +#include + +#include "db_ability.h" +#include "query_sync_object.h" +#include "schema_negotiate.h" +#include "single_ver_kvdb_sync_interface.h" +#include "single_ver_sync_target.h" +#include "subscribe_manager.h" +#include "sync_target.h" +#include "sync_task_context.h" +#include "time_helper.h" + + +namespace DistributedDB { +class SingleVerSyncTaskContext : public SyncTaskContext { +public: + + explicit SingleVerSyncTaskContext(); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerSyncTaskContext); + + // Init SingleVerSyncTaskContext + int Initialize(const std::string &deviceId, ISyncInterface *syncInterface, std::shared_ptr &metadata, + ICommunicator *communicator) override; + + // Add a sync task target with the operation to the queue + int AddSyncOperation(SyncOperation *operation) override; + + bool IsCurrentSyncTaskCanBeSkipped() const override; + + // Set the end water mark of this task + void SetEndMark(WaterMark endMark); + + // Get the end water mark of this task + WaterMark GetEndMark() const; + + void GetContinueToken(ContinueToken &outToken) const; + + void SetContinueToken(ContinueToken token); + + void ReleaseContinueToken(); + + int PopResponseTarget(SingleVerSyncTarget &target); + + int GetRspTargetQueueSize() const; + + // responseSessionId used for mark the pull response task + void SetResponseSessionId(uint32_t responseSessionId); + + // responseSessionId used for mark the pull response task + uint32_t GetResponseSessionId() const; + + void Clear() override; + + void Abort(int status) override; + + void ClearAllSyncTask() override; + + // If set true, remote stale data will be clear when remote db rebuiled. + void EnableClearRemoteStaleData(bool enable); + + // Check if need to clear remote device stale data in syncing, when the remote db rebuilt. + bool IsNeedClearRemoteStaleData() const; + + // start a timer to ResetWatchDog when sync data one (key,value) size bigger than mtu + bool StartFeedDogForSync(uint32_t time, SyncDirectionFlag flag); + + // stop timer to ResetWatchDog when sync data one (key,value) size bigger than mtu + void StopFeedDogForSync(SyncDirectionFlag flag); + + virtual int HandleDataRequestRecv(const Message *msg); + + // is receive warterMark err + bool IsReceiveWaterMarkErr() const; + + // set receive warterMark err + void SetReceiveWaterMarkErr(bool isErr); + + void SetRemoteSeccurityOption(SecurityOption secOption); + + SecurityOption GetRemoteSeccurityOption() const; + + void SetReceivcPermitCheck(bool isChecked); + + bool GetReceivcPermitCheck() const; + + void SetSendPermitCheck(bool isChecked); + + bool GetSendPermitCheck() const; + + virtual SyncStrategy GetSyncStrategy(QuerySyncObject &querySyncObject) const = 0; + + void SetIsSchemaSync(bool isSchemaSync); + + bool GetIsSchemaSync() const; + + bool IsSkipTimeoutError(int errCode) const; + + bool FindResponseSyncTarget(uint32_t responseSessionId) const; + + // For query sync + void SetQuery(const QuerySyncObject &query); + const QuerySyncObject &GetQuery() const; + void SetQuerySync(bool isQuerySync); + bool IsQuerySync() const; + std::set GetRemoteCompressAlgo() const; + std::string GetRemoteCompressAlgoStr() const; + void SetDbAbility(DbAbility &remoteDbAbility); + CompressAlgorithm ChooseCompressAlgo() const; + const DbAbility& GetRemoteDbAbility() const; + + void SetSubscribeManager(std::shared_ptr &subManager); + std::shared_ptr GetSubscribeManager() const; + + void SaveLastPushTaskExecStatus(int finalStatus) override; + void ResetLastPushTaskStatus() override; + + virtual std::string GetQuerySyncId() const = 0; + virtual std::string GetDeleteSyncId() const = 0; +protected: + ~SingleVerSyncTaskContext() override; + void CopyTargetData(const ISyncTarget *target, const TaskParam &taskParam) override; + + // For querySync + QuerySyncObject query_; + bool isQuerySync_ = false; +private: + int GetCorrectedSendWaterMarkForCurrentTask(uint64_t &waterMark) const; + + constexpr static int64_t REDUNDACE_WATER_MARK = 1 * 1000LL * 1000LL * 10LL; // 1s + + DECLARE_OBJECT_TAG(SingleVerSyncTaskContext); + + ContinueToken token_; + WaterMark endMark_; + uint32_t responseSessionId_ = 0; + + bool needClearRemoteStaleData_; + SecurityOption remoteSecOption_ = {0, 0}; // remote targe can handle secOption data or not. + bool isReceivcPermitChecked_ = false; + bool isSendPermitChecked_ = false; + std::atomic isSchemaSync_ = false; + + // is receive waterMark err, peerWaterMark bigger than remote localWaterMark + bool isReceiveWaterMarkErr_ = false; + + // For db ability + DbAbility remoteDbAbility_; + + // For subscribe manager + std::shared_ptr subManager_; + + // for merge sync task + int lastFullSyncTaskStatus_ = SyncOperation::Status::OP_WAITING; + // + std::unordered_map lastQuerySyncTaskStatusMap_; +}; +} // namespace DistributedDB + +#endif // SYNC_TASK_CONTEXT_H diff --git a/mock/distributeddb/syncer/src/single_ver_syncer.cpp b/mock/distributeddb/syncer/src/single_ver_syncer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c923405f322db07832c29af46ec92bce6a9b8117 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_syncer.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "single_ver_syncer.h" + +#include "db_common.h" +#include "single_ver_sync_engine.h" + +namespace DistributedDB { +void SingleVerSyncer::RemoteDataChanged(const std::string &device) +{ + LOGI("[SingleVerSyncer] device online dev %s", STR_MASK(device)); + // while remote db is online again, need to do abilitySync + static_cast(syncEngine_)->SetIsNeedResetAbilitySync(device, true); +} + +void SingleVerSyncer::RemoteDeviceOffline(const std::string &device) +{ + LOGI("[SingleVerRelationalSyncer] device offline dev %s", STR_MASK(device)); + std::string userId = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::USER_ID, ""); + std::string appId = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::APP_ID, ""); + std::string storeId = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::STORE_ID, ""); + RuntimeContext::GetInstance()->NotifyDatabaseStatusChange(userId, appId, storeId, device, false); + RefObject::IncObjRef(syncEngine_); + static_cast(syncEngine_)->OfflineHandleByDevice(device); + RefObject::DecObjRef(syncEngine_); +} + +int SingleVerSyncer::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) +{ + return EraseDeviceWaterMark(deviceId, isNeedHash, ""); +} + +int SingleVerSyncer::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, + const std::string &tableName) +{ + if (metadata_ == nullptr) { + return -E_NOT_INIT; + } + return metadata_->EraseDeviceWaterMark(deviceId, isNeedHash, tableName); +} + +int SingleVerSyncer::SetStaleDataWipePolicy(WipePolicy policy) +{ + std::lock_guard lock(syncerLock_); + if (closing_) { + LOGE("[Syncer] Syncer is closing, return!"); + return -E_BUSY; + } + if (syncEngine_ == nullptr) { + return -E_NOT_INIT; + } + int errCode = E_OK; + switch (policy) { + case RETAIN_STALE_DATA: + static_cast(syncEngine_)->EnableClearRemoteStaleData(false); + break; + case WIPE_STALE_DATA: + static_cast(syncEngine_)->EnableClearRemoteStaleData(true); + break; + default: + errCode = -E_NOT_SUPPORT; + break; + } + return errCode; +} + +ISyncEngine *SingleVerSyncer::CreateSyncEngine() +{ + return new (std::nothrow) SingleVerSyncEngine(); +} +} diff --git a/mock/distributeddb/syncer/src/single_ver_syncer.h b/mock/distributeddb/syncer/src/single_ver_syncer.h new file mode 100644 index 0000000000000000000000000000000000000000..9dc5e0e1781185a7d96d9bedab6d5c8c7012d3b9 --- /dev/null +++ b/mock/distributeddb/syncer/src/single_ver_syncer.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SYNCER_H +#define SYNCER_H + +#include "generic_syncer.h" + +namespace DistributedDB { +class SingleVerSyncer : public GenericSyncer { +public: + void RemoteDataChanged(const std::string &device) override; + + void RemoteDeviceOffline(const std::string &device) override; + + // Set stale data wipe policy + int SetStaleDataWipePolicy(WipePolicy policy) override; + + // delete specified device's watermark + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) override; + + // delete specified device's and table's watermark + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, + const std::string &tableName) override; + +protected: + // Create a sync engine, if has memory error, will return nullptr. + ISyncEngine *CreateSyncEngine() override; +}; +} +#endif // SYNCER_H diff --git a/mock/distributeddb/syncer/src/subscribe_manager.cpp b/mock/distributeddb/syncer/src/subscribe_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..572cd91544b37928bfd22a50e93468ab002ed154 --- /dev/null +++ b/mock/distributeddb/syncer/src/subscribe_manager.cpp @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "subscribe_manager.h" +#include "db_common.h" +#include "sync_types.h" + +namespace DistributedDB { +void SubscribeManager::ClearRemoteSubscribeQuery(const std::string &device) +{ + std::unique_lock lockGuard(remoteSubscribedMapLock_); + ClearSubscribeQuery(device, remoteSubscribedMap_, remoteSubscribedTotalMap_); +} + +void SubscribeManager::ClearAllRemoteQuery() +{ + std::unique_lock lockGuard(remoteSubscribedMapLock_); + remoteSubscribedMap_.clear(); + remoteSubscribedTotalMap_.clear(); +} + +void SubscribeManager::ClearLocalSubscribeQuery(const std::string &device) +{ + std::unique_lock lockGuard(localSubscribeMapLock_); + unFinishedLocalAutoSubMap_.erase(device); + ClearSubscribeQuery(device, localSubscribeMap_, localSubscribeTotalMap_); +} + +int SubscribeManager::ReserveRemoteSubscribeQuery(const std::string &device, const QuerySyncObject &query) +{ + std::unique_lock lockGuard(remoteSubscribedMapLock_); + int errCode = ReserveSubscribeQuery(device, query, remoteSubscribedMap_, remoteSubscribedTotalMap_); + LOGI("[SubscribeManager] dev=%s,queryId=%s remote reserve err=%d", STR_MASK(device), STR_MASK(query.GetIdentify()), + errCode); + return errCode; +} + +int SubscribeManager::ActiveRemoteSubscribeQuery(const std::string &device, const QuerySyncObject &query) +{ + std::unique_lock lockGuard(remoteSubscribedMapLock_); + std::string queryId = query.GetIdentify(); + int errCode = ActiveSubscribeQuery(device, queryId, remoteSubscribedMap_, remoteSubscribedTotalMap_); + LOGI("[SubscribeManager] dev=%s,queryId=%s remote active err=%d", STR_MASK(device), STR_MASK(queryId), errCode); + return errCode; +} + +int SubscribeManager::ReserveLocalSubscribeQuery(const std::string &device, const QuerySyncObject &query) +{ + std::unique_lock lockGuard(localSubscribeMapLock_); + int errCode = ReserveSubscribeQuery(device, query, localSubscribeMap_, localSubscribeTotalMap_); + LOGI("[SubscribeManager] dev=%s,queryId=%s local reserve err=%d", STR_MASK(device), STR_MASK(query.GetIdentify()), + errCode); + return errCode; +} + +int SubscribeManager::ActiveLocalSubscribeQuery(const std::string &device, const QuerySyncObject &query) +{ + std::unique_lock lockGuard(localSubscribeMapLock_); + std::string queryId = query.GetIdentify(); + int errCode = ActiveSubscribeQuery(device, queryId, localSubscribeMap_, localSubscribeTotalMap_); + LOGI("[SubscribeManager] dev=%s,queryId=%s local active err=%d", STR_MASK(device), STR_MASK(queryId), errCode); + if (errCode != E_OK) { + return errCode; + } + if (unFinishedLocalAutoSubMap_.find(device) != unFinishedLocalAutoSubMap_.end() && + unFinishedLocalAutoSubMap_[device].find(queryId) != unFinishedLocalAutoSubMap_[device].end()) { + unFinishedLocalAutoSubMap_[device].erase(queryId); + } + return errCode; +} + +void SubscribeManager::DeleteLocalSubscribeQuery(const std::string &device, const QuerySyncObject &query) +{ + std::unique_lock lockGuard(localSubscribeMapLock_); + std::string queryId = query.GetIdentify(); + DeleteSubscribeQuery(device, queryId, localSubscribeMap_, localSubscribeTotalMap_); +} + +void SubscribeManager::DeleteRemoteSubscribeQuery(const std::string &device, const QuerySyncObject &query) +{ + std::unique_lock lockGuard(remoteSubscribedMapLock_); + std::string queryId = query.GetIdentify(); + DeleteSubscribeQuery(device, queryId, remoteSubscribedMap_, remoteSubscribedTotalMap_); +} + +void SubscribeManager::PutLocalUnFiniedSubQueries(const std::string &device, + std::vector &subscribeQueries) +{ + LOGI("[SubscribeManager] put local unfinished subscribe queries, nums=%zu", subscribeQueries.size()); + std::unique_lock lockGuard(localSubscribeMapLock_); + if (subscribeQueries.size() == 0) { + unFinishedLocalAutoSubMap_.erase(device); + return; + } + unFinishedLocalAutoSubMap_[device].clear(); + auto iter = unFinishedLocalAutoSubMap_.find(device); + for (const auto &query : subscribeQueries) { + iter->second.insert(query.GetIdentify()); + } +} + +void SubscribeManager::GetAllUnFinishSubQueries( + std::map> &allSyncQueries) const +{ + std::shared_lock lock(localSubscribeMapLock_); + for (auto &item : unFinishedLocalAutoSubMap_) { + if (item.second.size() == 0) { + continue; + } + allSyncQueries[item.first] = {}; + auto iter = allSyncQueries.find(item.first); + for (const auto &queryId : item.second) { + auto iterTmp = localSubscribeTotalMap_.find(queryId); + if (iterTmp == localSubscribeTotalMap_.end()) { + LOGI("[SubscribeManager] queryId=%s not in localTotalMap", STR_MASK(queryId)); + continue; + } + iter->second.push_back(iterTmp->second.first); + } + } +} + +void SubscribeManager::RemoveRemoteSubscribeQuery(const std::string &device, const QuerySyncObject &query) +{ + std::unique_lock lockGuard(remoteSubscribedMapLock_); + std::string queryId = query.GetIdentify(); + RemoveSubscribeQuery(device, queryId, remoteSubscribedMap_, remoteSubscribedTotalMap_); +} + +void SubscribeManager::RemoveLocalSubscribeQuery(const std::string &device, const QuerySyncObject &query) +{ + std::unique_lock lockGuard(localSubscribeMapLock_); + std::string queryId = query.GetIdentify(); + RemoveSubscribeQuery(device, queryId, localSubscribeMap_, localSubscribeTotalMap_); + if (unFinishedLocalAutoSubMap_.find(device) != unFinishedLocalAutoSubMap_.end() && + unFinishedLocalAutoSubMap_[device].find(queryId) != unFinishedLocalAutoSubMap_[device].end()) { + unFinishedLocalAutoSubMap_[device].erase(queryId); + LOGI("[SubscribeManager] dev=%s,queryId=%s delete from UnFinishedMap", STR_MASK(device), STR_MASK(queryId)); + if (unFinishedLocalAutoSubMap_[device].size() == 0) { + LOGI("[SubscribeManager] dev=%s delete from unFinish map", STR_MASK(device)); + unFinishedLocalAutoSubMap_.erase(device); + } + } +} + +void SubscribeManager::GetLocalSubscribeQueries(const std::string &device, + std::vector &subscribeQueries) const +{ + std::shared_lock lock(localSubscribeMapLock_); + GetSubscribeQueries(device, localSubscribeMap_, localSubscribeTotalMap_, subscribeQueries); +} + +void SubscribeManager::GetRemoteSubscribeQueries(const std::string &device, + std::vector &subscribeQueries) const +{ + std::shared_lock lockGuard(remoteSubscribedMapLock_); + GetSubscribeQueries(device, remoteSubscribedMap_, remoteSubscribedTotalMap_, subscribeQueries); +} + +bool SubscribeManager::IsRemoteContainSubscribe(const std::string &device, const QuerySyncObject &query) const +{ + std::shared_lock lockGuard(remoteSubscribedMapLock_); + auto iter = remoteSubscribedMap_.find(device); + if (iter == remoteSubscribedMap_.end()) { + LOGD("[SubscribeManager] dev=%s not in remoteSubscribedMap", STR_MASK(device)); + return false; + } + std::string queryId = query.GetIdentify(); + auto subIter = iter->second.find(queryId); + if (subIter == iter->second.end()) { + LOGE("[SubscribeManager] queryId=%s not in RemoteTotalMap", STR_MASK(queryId)); + return false; + } + return true; +} + +void SubscribeManager::GetRemoteSubscribeQueryIds(const std::string &device, + std::vector &subscribeQueryIds) const +{ + std::shared_lock lockGuard(remoteSubscribedMapLock_); + auto iter = remoteSubscribedMap_.find(device); + if (iter == remoteSubscribedMap_.end()) { + LOGI("[SubscribeManager] dev=%s not in remoteSubscribedMap", STR_MASK(device)); + return; + } + for (const auto &queryInfo : iter->second) { + if (remoteSubscribedTotalMap_.find(queryInfo.first) == remoteSubscribedTotalMap_.end()) { + LOGE("[SubscribeManager] queryId=%s not in RemoteTotalMap", STR_MASK(queryInfo.first)); + continue; + } + subscribeQueryIds.push_back(queryInfo.first); + } +} + +int SubscribeManager::LocalSubscribeLimitCheck(const std::vector &devices, QuerySyncObject &query) const +{ + std::shared_lock lock(localSubscribeMapLock_); + size_t devNum = localSubscribeMap_.size(); + for (const auto &device : devices) { + if (localSubscribeMap_.find(device) != localSubscribeMap_.end()) { + continue; + } + devNum++; + if (devNum > MAX_DEVICES_NUM) { + LOGE("[SubscribeManager] local subscribe devices is over limit"); + return -E_MAX_LIMITS; + } + } + std::string queryId = query.GetIdentify(); + auto allIter = localSubscribeTotalMap_.find(queryId); + if (allIter == localSubscribeTotalMap_.end() && localSubscribeTotalMap_.size() >= MAX_SUBSCRIBE_NUM_PER_DB) { + LOGE("[SubscribeManager] all local subscribe sums is over limit"); + return -E_MAX_LIMITS; + } + return E_OK; +} + +void SubscribeManager::ClearSubscribeQuery(const std::string &device, SubscribeMap &subscribeMap, + SubscribedTotalMap &subscribedTotalMap) +{ + if (subscribeMap.find(device) == subscribeMap.end()) { + LOGI("[SubscribeManager] dev=%s not in SubscribedMap", STR_MASK(device)); + return; + } + for (const auto &queryInfo : subscribeMap[device]) { + if (subscribedTotalMap.find(queryInfo.first) != subscribedTotalMap.end()) { + if (subscribedTotalMap[queryInfo.first].second > 0) { + subscribedTotalMap[queryInfo.first].second--; + } + if (subscribedTotalMap[queryInfo.first].second == 0) { + LOGI("[SubscribeManager] queryId=%s delete from TotalMap", STR_MASK(queryInfo.first)); + subscribedTotalMap.erase(queryInfo.first); + } + } + } + subscribeMap.erase(device); + LOGI("[SubscribeManager] clear dev=%s remote subscribe queies finished", STR_MASK(device)); +} + +int SubscribeManager::ReserveSubscribeQuery(const std::string &device, const QuerySyncObject &query, + SubscribeMap &subscribeMap, SubscribedTotalMap &subscribedTotalMap) +{ + std::string queryId = query.GetIdentify(); + auto iter = subscribeMap.find(device); + auto allIter = subscribedTotalMap.find(queryId); + // limit check + if (allIter == subscribedTotalMap.end() && subscribedTotalMap.size() >= MAX_SUBSCRIBE_NUM_PER_DB) { + LOGE("[SubscribeManager] all subscribe sums is over limit"); + return -E_MAX_LIMITS; + } + if (iter == subscribeMap.end() && subscribeMap.size() >= MAX_DEVICES_NUM) { + LOGE("[SubscribeManager] subscribe devices is over limit"); + return -E_MAX_LIMITS; + } + if (iter != subscribeMap.end() && iter->second.find(queryId) == iter->second.end() && + iter->second.size() >= MAX_SUBSCRIBE_NUM_PER_DEV) { + LOGE("[SubscribeManager] subscribe sums is over limit"); + return -E_MAX_LIMITS; + } + if (iter != subscribeMap.end() && iter->second.find(queryId) != iter->second.end() && + iter->second[queryId] == SubscribeStatus::ACTIVE) { + LOGE("[SubscribeManager] dev=%s,queryId=%s already active in map", STR_MASK(device), STR_MASK(queryId)); + return E_OK; + } + + if (iter == subscribeMap.end()) { + subscribeMap[device] = std::map {}; + } + bool isNeedInc = false; + if (subscribeMap[device].find(queryId) == subscribeMap[device].end()) { + subscribeMap[device][queryId] = SubscribeStatus::NOT_ACTIVE; + isNeedInc = true; + } + if (allIter == subscribedTotalMap.end()) { + subscribedTotalMap[queryId] = {query, 1}; + } else if (isNeedInc) { + subscribedTotalMap[queryId].second++; + } + return E_OK; +} + +int SubscribeManager::ActiveSubscribeQuery(const std::string &device, const std::string &queryId, + SubscribeMap &subscribeMap, SubscribedTotalMap &subscribedTotalMap) +{ + if (subscribedTotalMap.find(queryId) == subscribedTotalMap.end()) { + LOGE("[SubscribeManager] can not find queryId=%s in SubscribeTotalMap", STR_MASK(queryId)); + return -E_INTERNAL_ERROR; + } + if (subscribeMap.find(device) == subscribeMap.end()) { + LOGE("[SubscribeManager] can not find dev=%s in localSubscribeMap", STR_MASK(device)); + return -E_INTERNAL_ERROR; + } + if (subscribeMap[device].find(queryId) == subscribeMap[device].end()) { + LOGE("[SubscribeManager] can not find dev=%s,queryId=%s in map", STR_MASK(device), STR_MASK(queryId)); + return -E_INTERNAL_ERROR; + } + subscribeMap[device][queryId] = SubscribeStatus::ACTIVE; + return E_OK; +} + +void SubscribeManager::DeleteSubscribeQuery(const std::string &device, const std::string &queryId, + SubscribeMap &subscribeMap, SubscribedTotalMap &subscribedTotalMap) +{ + if (subscribeMap.find(device) == subscribeMap.end()) { + LOGE("[SubscribeManager] can not find dev=%s in map", STR_MASK(device)); + return; + } + if (subscribeMap[device].find(queryId) == subscribeMap[device].end()) { + LOGE("[SubscribeManager] can not find dev=%s,queryId=%s in map", STR_MASK(device), STR_MASK(queryId)); + return; + } + SubscribeStatus queryStatus = subscribeMap[device][queryId]; + // not permit to delete the query when something wrong this time,because it is subscribed successfully last time + if (queryStatus == SubscribeStatus::ACTIVE) { + LOGE("[SubscribeManager] dev=%s,queryId=%s is active, no need to del", STR_MASK(device), STR_MASK(queryId)); + return; + } + subscribeMap[device].erase(queryId); + auto iter = subscribedTotalMap.find(queryId); + if (iter == subscribedTotalMap.end()) { + LOGE("[SubscribeManager] can not find queryId=%s in SubscribeTotalMap", STR_MASK(queryId)); + return; + } + iter->second.second--; + if (iter->second.second <= 0) { + LOGI("[SubscribeManager] del queryId=%s from SubscribeTotalMap", STR_MASK(queryId)); + subscribedTotalMap.erase(queryId); + } + LOGI("[SubscribeManager] dev=%s,queryId=%s remove from SubscribeMap success", STR_MASK(device), STR_MASK(queryId)); +} + +void SubscribeManager::RemoveSubscribeQuery(const std::string &device, const std::string &queryId, + SubscribeMap &subscribeMap, SubscribedTotalMap &subscribedTotalMap) +{ + auto iter = subscribeMap.find(device); + if (iter == subscribeMap.end()) { + LOGE("[SubscribeManager] dev=%s not in SubscribedMap", STR_MASK(device)); + return; + } + if (iter->second.find(queryId) == subscribeMap[device].end()) { + LOGI("[SubscribeManager] dev=%s,queryId=%s not in SubscribedMap", STR_MASK(device), STR_MASK(queryId)); + return; + } + iter->second.erase(queryId); + auto allIter = subscribedTotalMap.find(queryId); + if (allIter == subscribedTotalMap.end()) { + LOGI("[SubscribeManager] queryId=%s not in TotalMap", STR_MASK(queryId)); + return; + } + allIter->second.second--; + if (allIter->second.second <= 0) { + subscribedTotalMap.erase(queryId); + LOGI("[SubscribeManager] queryId=%s delete from TotalMap", STR_MASK(queryId)); + } + LOGI("[SubscribeManager] dev=%s,queryId=%s remove from SubscribedMap success", STR_MASK(device), STR_MASK(queryId)); +} + +void SubscribeManager::GetSubscribeQueries(const std::string &device, const SubscribeMap &subscribeMap, + const SubscribedTotalMap &subscribedTotalMap, std::vector &subscribeQueries) const +{ + auto iter = subscribeMap.find(device); + if (iter == subscribeMap.end()) { + LOGD("[SubscribeManager] dev=%s not in localSubscribeMap", STR_MASK(device)); + return; + } + for (const auto &queryInfo : iter->second) { + auto iterTmp = subscribedTotalMap.find(queryInfo.first); + if (iterTmp == subscribedTotalMap.end()) { + LOGE("[SubscribeManager] queryId=%s not in localTotalMap", STR_MASK(queryInfo.first)); + continue; + } + subscribeQueries.push_back(iterTmp->second.first); + } +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/subscribe_manager.h b/mock/distributeddb/syncer/src/subscribe_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..6b3bb1d387cf7b3dd450e144b2020ca8138fe00f --- /dev/null +++ b/mock/distributeddb/syncer/src/subscribe_manager.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SINGLE_VER_SUBSCRIBE_MANAGER_H +#define SINGLE_VER_SUBSCRIBE_MANAGER_H + +#include +#include +#include "query_sync_object.h" + +namespace DistributedDB { +enum class SubscribeStatus { + NOT_ACTIVE = 0, + ACTIVE = 1, +}; + +using SubscribeMap = std::map>; +using SubscribedTotalMap = std::map>; + +class SubscribeManager { +public: + SubscribeManager() = default; + ~SubscribeManager() {}; + + DISABLE_COPY_ASSIGN_MOVE(SubscribeManager); + + // clear remoteSubscribeMap_[device] list when remote db is closed or dev offline. + void ClearRemoteSubscribeQuery(const std::string &device); + + // clear localSubscribeMap_[device] list when device is offline. + void ClearLocalSubscribeQuery(const std::string &device); + + void ClearAllRemoteQuery(); + + // add query when receive subscribe command + int ReserveRemoteSubscribeQuery(const std::string &device, const QuerySyncObject &query); + + // active query to ACTIVE when send ack ok + int ActiveRemoteSubscribeQuery(const std::string &device, const QuerySyncObject &query); + + // reserve query when user call SubscribeRemoteQuery, status set to NOT_ACTIVE + int ReserveLocalSubscribeQuery(const std::string &device, const QuerySyncObject &query); + + // active query to ACTIVE when receive ack ok + int ActiveLocalSubscribeQuery(const std::string &device, const QuerySyncObject &query); + + // delete local subscribe query when recv wrong errCode, only not_active status allowed to del + void DeleteLocalSubscribeQuery(const std::string &device, const QuerySyncObject &query); + + // delete remote subscribe query when send msg failed, only not_active status allowed to del + void DeleteRemoteSubscribeQuery(const std::string &device, const QuerySyncObject &query); + + // put subscribe queries into unfinished map when remote db online + void PutLocalUnFiniedSubQueries(const std::string &device, std::vector &subscribeQueries); + + // get all device unFinished subscribe queries which triggered by auto subscribe and need retry subscribe + void GetAllUnFinishSubQueries(std::map> &allSyncQueries) const; + + // remove query when receive unsubscribe command + void RemoveRemoteSubscribeQuery(const std::string &device, const QuerySyncObject &query); + + // remove query when user call UnSubscribeRemoteQuery + void RemoveLocalSubscribeQuery(const std::string &device, const QuerySyncObject &query); + + // get device active subscribeQueries from localSubscribeMap_ + void GetLocalSubscribeQueries(const std::string &device, std::vector &subscribeQueries) const; + + // get device remote queryId from remoteSubscribedMap_ while data change + void GetRemoteSubscribeQueryIds(const std::string &device, std::vector &subscribeQueryIds) const; + // get device remote subscribeQueries from remoteSubscribedMap_ while data change + void GetRemoteSubscribeQueries(const std::string &device, std::vector &subscribeQueries) const; + + bool IsRemoteContainSubscribe(const std::string &device, const QuerySyncObject &query) const; + + int LocalSubscribeLimitCheck(const std::vector &devices, QuerySyncObject &query) const; +private: + void ClearSubscribeQuery(const std::string &device, SubscribeMap &subscribeMap, + SubscribedTotalMap &subscribedTotalMap); + + int ReserveSubscribeQuery(const std::string &device, const QuerySyncObject &query, SubscribeMap &subscribeMap, + SubscribedTotalMap &subscribedTotalMap); + + int ActiveSubscribeQuery(const std::string &device, const std::string &queryId, SubscribeMap &subscribeMap, + SubscribedTotalMap &subscribedTotalMap); + + void DeleteSubscribeQuery(const std::string &device, const std::string &queryId, SubscribeMap &subscribeMap, + SubscribedTotalMap &subscribedTotalMap); + + void RemoveSubscribeQuery(const std::string &device, const std::string &queryId, SubscribeMap &subscribeMap, + SubscribedTotalMap &subscribedTotalMap); + + void GetSubscribeQueries(const std::string &device, const SubscribeMap &subscribeMap, + const SubscribedTotalMap &subscribedTotalMap, std::vector &subscribeQueries) const; + + mutable std::shared_mutex localSubscribeMapLock_; + // subscribe sponsor, key: device, value: pair map + // status 0: active, 1: not active + SubscribeMap localSubscribeMap_; + + // used retry subscribe in db open scene, key: device value: set + std::map> unFinishedLocalAutoSubMap_; + + // subscribe sponsor total query info, key:queryId, value: + // while use_num is 0, delete item from the map + SubscribedTotalMap localSubscribeTotalMap_; + + mutable std::shared_mutex remoteSubscribedMapLock_; + // subscribed, key: device, value: pair map + // status 0: active, 1: not active + SubscribeMap remoteSubscribedMap_; + + // subscribed total query info, key:queryId, value: + // while use_num is 0, delete item from the map + SubscribedTotalMap remoteSubscribedTotalMap_; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_SUBSCRIBE_MANAGER_H \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/sync_config.cpp b/mock/distributeddb/syncer/src/sync_config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b3c6b89687ed0a32b25265c3bde347016ba24a8 --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_config.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sync_config.h" +namespace DistributedDB { +const AbilityItem SyncConfig::DATABASE_COMPRESSION_ZLIB = {0, 1}; +const AbilityItem SyncConfig::ALLPREDICATEQUERY = {1, 1}; // 0b10 {1: start at second bit, 1: 1 bit len} +const AbilityItem SyncConfig::SUBSCRIBEQUERY = {2, 1}; // 0b100 +const AbilityItem SyncConfig::INKEYS_QUERY = {3, 1}; // 0b1000 + +const std::vector SyncConfig::ABILITYBITS = { + DATABASE_COMPRESSION_ZLIB, + ALLPREDICATEQUERY, + SUBSCRIBEQUERY, + INKEYS_QUERY}; + +const std::map SyncConfig::COMPRESSALGOMAP = { + {static_cast(CompressAlgorithm::ZLIB), DATABASE_COMPRESSION_ZLIB}, +}; +} // DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/sync_config.h b/mock/distributeddb/syncer/src/sync_config.h new file mode 100644 index 0000000000000000000000000000000000000000..dc2c252def3661ca70226ef46ea5f995345eea0f --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_config.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SYNC_CONFIG_H +#define SYNC_CONFIG_H + +#include +#include +#include +#include "macro_utils.h" +#include "parcel.h" +#include "types_export.h" + +// db ability config +namespace DistributedDB { +// offset, used_bits_num, used_bits_num < 64 +using AbilityItem = std::pair; +// format: {offset, used_bits_num} +/* +if need to add new ability, just add append to the last ability +current ability format: +|first bit|second bit|third bit| +|DATABASE_COMPRESSION_ZLIB|ALLPREDICATEQUERY|SUBSCRIBEQUERY| +*/ +class SyncConfig final { +public: + static const AbilityItem DATABASE_COMPRESSION_ZLIB; + static const AbilityItem ALLPREDICATEQUERY; + static const AbilityItem SUBSCRIBEQUERY; + static const AbilityItem INKEYS_QUERY; + static const std::vector ABILITYBITS; + static const std::map COMPRESSALGOMAP; +}; +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/sync_engine.cpp b/mock/distributeddb/syncer/src/sync_engine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..15688bed3c27acd70788710fae4a5ee362c896fd --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_engine.cpp @@ -0,0 +1,1067 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sync_engine.h" + +#include +#include +#include + +#include "ability_sync.h" +#include "db_common.h" +#include "db_dump_helper.h" +#include "db_errno.h" +#include "device_manager.h" +#include "hash.h" +#include "isync_state_machine.h" +#include "log_print.h" +#include "runtime_context.h" +#include "single_ver_serialize_manager.h" +#include "subscribe_manager.h" +#include "time_sync.h" + +#ifndef OMIT_MULTI_VER +#include "commit_history_sync.h" +#include "multi_ver_data_sync.h" +#include "value_slice_sync.h" +#endif + +namespace DistributedDB { +int SyncEngine::queueCacheSize_ = 0; +int SyncEngine::maxQueueCacheSize_ = DEFAULT_CACHE_SIZE; +unsigned int SyncEngine::discardMsgNum_ = 0; +std::mutex SyncEngine::queueLock_; + +SyncEngine::SyncEngine() + : syncInterface_(nullptr), + communicator_(nullptr), + deviceManager_(nullptr), + metadata_(nullptr), + timeChangedListener_(nullptr), + execTaskCount_(0), + isSyncRetry_(false), + communicatorProxy_(nullptr), + isActive_(false) +{ +} + +SyncEngine::~SyncEngine() +{ + LOGD("[SyncEngine] ~SyncEngine!"); + ClearInnerResource(); + equalIdentifierMap_.clear(); + subManager_ = nullptr; + LOGD("[SyncEngine] ~SyncEngine ok!"); +} + +int SyncEngine::Initialize(ISyncInterface *syncInterface, std::shared_ptr &metadata, + const std::function &onRemoteDataChanged, const std::function &offlineChanged, + const std::function &queryAutoSyncCallback) +{ + if ((syncInterface == nullptr) || (metadata == nullptr)) { + return -E_INVALID_ARGS; + } + int errCode = StartAutoSubscribeTimer(); + if (errCode != OK) { + return errCode; + } + syncInterface_ = syncInterface; + errCode = InitComunicator(syncInterface); + if (errCode != E_OK) { + LOGE("[SyncEngine] Init Communicator failed"); + // There need to set nullptr. other wise, syncInterface will be + // DecRef in th destroy-method. + StopAutoSubscribeTimer(); + syncInterface_ = nullptr; + return errCode; + } + onRemoteDataChanged_ = onRemoteDataChanged; + offlineChanged_ = offlineChanged; + queryAutoSyncCallback_ = queryAutoSyncCallback; + errCode = InitDeviceManager(onRemoteDataChanged, offlineChanged); + if (errCode != E_OK) { + // reset ptr if initialize device manager failed + syncInterface_ = nullptr; + StopAutoSubscribeTimer(); + return errCode; + } + if (subManager_ == nullptr) { + subManager_ = std::make_shared(); + } + metadata_ = metadata; + errCode = InitTimeChangedListener(); + if (errCode != E_OK) { + syncInterface_ = nullptr; + StopAutoSubscribeTimer(); + return errCode; + } + isActive_ = true; + LOGI("[SyncEngine] Engine init ok"); + return E_OK; +} + +int SyncEngine::Close() +{ + LOGI("[SyncEngine] SyncEngine[%s] close enter!", label_.c_str()); + isActive_ = false; + UnRegCommunicatorsCallback(); + StopAutoSubscribeTimer(); + + std::unique_lock closeLock(execTaskCountLock_); + bool isTimeout = execTaskCv_.wait_for(closeLock, std::chrono::milliseconds(DBConstant::MIN_TIMEOUT), + [this]() { return execTaskCount_ == 0; }); + if (!isTimeout) { + LOGD("SyncEngine Close with executing task!"); + } + // Clear SyncContexts + { + std::unique_lock lock(contextMapLock_); + for (auto &iter : syncTaskContextMap_) { + ISyncTaskContext *tempContext = iter.second; + lock.unlock(); + RefObject::KillAndDecObjRef(tempContext); + tempContext = nullptr; + lock.lock(); + iter.second = nullptr; + } + syncTaskContextMap_.clear(); + } + + ReleaseCommunicators(); + std::lock_guard msgLock(queueLock_); + while (!msgQueue_.empty()) { + Message *inMsg = msgQueue_.front(); + msgQueue_.pop_front(); + if (inMsg != nullptr) { + queueCacheSize_ -= GetMsgSize(inMsg); + delete inMsg; + inMsg = nullptr; + } + } + // close db, rekey or import scene, need clear all remote query info + // local query info will destroy with syncEngine destruct + if (subManager_ != nullptr) { + subManager_->ClearAllRemoteQuery(); + } + ClearInnerResource(); + LOGI("[SyncEngine] SyncEngine closed!"); + return E_OK; +} + +int SyncEngine::AddSyncOperation(SyncOperation *operation) +{ + if (operation == nullptr) { + LOGE("[SyncEngine] operation is nullptr"); + return -E_INVALID_ARGS; + } + + std::vector devices = operation->GetDevices(); + for (const auto &deviceId : devices) { + if (deviceId.size() == 0) { + operation->SetStatus(deviceId, SyncOperation::OP_INVALID_ARGS); + continue; + } + operation->SetStatus(deviceId, SyncOperation::OP_WAITING); + int errCode = AddSyncOperForContext(deviceId, operation); + if (errCode != E_OK) { + operation->SetStatus(deviceId, SyncOperation::OP_FAILED); + } + } + return E_OK; +} + +void SyncEngine::RemoveSyncOperation(int syncId) +{ + std::lock_guard lock(contextMapLock_); + for (auto &iter : syncTaskContextMap_) { + ISyncTaskContext *context = iter.second; + if (context != nullptr) { + context->RemoveSyncOperation(syncId); + } + } +} + +void SyncEngine::BroadCastDataChanged() const +{ + if (deviceManager_ != nullptr) { + (void)deviceManager_->SendBroadCast(LOCAL_DATA_CHANGED); + } +} + +void SyncEngine::RegConnectCallback() +{ + if (communicator_ == nullptr) { + LOGE("[SyncEngine][RegConnCB] communicator is not set!"); + return; + } + LOGD("[SyncEngine] RegOnConnectCallback"); + int errCode = communicator_->RegOnConnectCallback( + std::bind(&DeviceManager::OnDeviceConnectCallback, deviceManager_, + std::placeholders::_1, std::placeholders::_2), nullptr); + if (errCode != E_OK) { + LOGE("[SyncEngine][RegConnCB] register failed, auto sync can not use! err %d", errCode); + return; + } + communicator_->Activate(); +} + +void SyncEngine::GetOnlineDevices(std::vector &devices) const +{ + devices.clear(); + if (deviceManager_ != nullptr) { + deviceManager_->GetOnlineDevices(devices); + } +} + +int SyncEngine::InitDeviceManager(const std::function &onRemoteDataChanged, + const std::function &offlineChanged) +{ + deviceManager_ = new (std::nothrow) DeviceManager(); + if (deviceManager_ == nullptr) { + LOGE("[SyncEngine] deviceManager alloc failed!"); + return -E_OUT_OF_MEMORY; + } + + int errCode = deviceManager_->Initialize(communicatorProxy_, onRemoteDataChanged, offlineChanged); + if (errCode != E_OK) { + LOGE("[SyncEngine] deviceManager init failed! err %d", errCode); + delete deviceManager_; + deviceManager_ = nullptr; + return errCode; + } + return E_OK; +} + +int SyncEngine::InitComunicator(const ISyncInterface *syncInterface) +{ + ICommunicatorAggregator *communicatorAggregator = nullptr; + int errCode = RuntimeContext::GetInstance()->GetCommunicatorAggregator(communicatorAggregator); + if (communicatorAggregator == nullptr) { + LOGE("[SyncEngine] Get ICommunicatorAggregator error when init the sync engine err = %d", errCode); + return errCode; + } + std::vector label = syncInterface->GetIdentifier(); + bool isSyncDualTupleMode = syncInterface->GetDbProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, + false); + if (isSyncDualTupleMode) { + std::vector dualTuplelabel = syncInterface->GetDualTupleIdentifier(); + LOGI("[SyncEngine] dual tuple mode, original identifier=%.6s, target identifier=%.6s", VEC_TO_STR(label), + VEC_TO_STR(dualTuplelabel)); + communicator_ = communicatorAggregator->AllocCommunicator(dualTuplelabel, errCode); + } else { + communicator_ = communicatorAggregator->AllocCommunicator(label, errCode); + } + if (communicator_ == nullptr) { + LOGE("[SyncEngine] AllocCommunicator error when init the sync engine! err = %d", errCode); + return errCode; + } + + errCode = communicator_->RegOnMessageCallback( + std::bind(&SyncEngine::MessageReciveCallback, this, std::placeholders::_1, std::placeholders::_2), + []() {}); + if (errCode != E_OK) { + LOGE("[SyncEngine] SyncRequestCallback register failed! err = %d", errCode); + communicatorAggregator->ReleaseCommunicator(communicator_); + communicator_ = nullptr; + return errCode; + } + + communicatorProxy_ = new (std::nothrow) CommunicatorProxy(); + if (communicatorProxy_ == nullptr) { + communicatorAggregator->ReleaseCommunicator(communicator_); + communicator_ = nullptr; + return -E_OUT_OF_MEMORY; + } + + communicatorProxy_->SetMainCommunicator(communicator_); + label.resize(3); // only show 3 Bytes enough + label_ = DBCommon::VectorToHexString(label); + LOGD("[SyncEngine] RegOnConnectCallback"); + return errCode; +} + +int SyncEngine::AddSyncOperForContext(const std::string &deviceId, SyncOperation *operation) +{ + int errCode = E_OK; + ISyncTaskContext *context = nullptr; + { + std::lock_guard lock(contextMapLock_); + context = FindSyncTaskContext(deviceId); + if (context == nullptr) { + if (!IsKilled()) { + context = GetSyncTaskContext(deviceId, errCode); + } + if (context == nullptr) { + return errCode; + } + } + if (context->IsKilled()) { + return -E_OBJ_IS_KILLED; + } + // IncRef for SyncEngine to make sure context is valid, to avoid a big lock + RefObject::IncObjRef(context); + } + + errCode = context->AddSyncOperation(operation); + RefObject::DecObjRef(context); + return errCode; +} + +void SyncEngine::MessageReciveCallbackTask(ISyncTaskContext *context, const ICommunicator *communicator, + Message *inMsg) +{ + std::string deviceId = context->GetDeviceId(); + + if (inMsg->GetMessageId() != LOCAL_DATA_CHANGED) { + int errCode = context->ReceiveMessageCallback(inMsg); + if (errCode == -E_NOT_NEED_DELETE_MSG) { + goto MSG_CALLBACK_OUT_NOT_DEL; + } + // add auto sync here while recv subscribe request + QuerySyncObject syncObject; + if (errCode == E_OK && context->IsNeedTriggerQueryAutoSync(inMsg, syncObject)) { + InternalSyncParma param; + GetQueryAutoSyncParam(deviceId, syncObject, param); + queryAutoSyncCallback_(param); + } + } + + delete inMsg; + inMsg = nullptr; +MSG_CALLBACK_OUT_NOT_DEL: + ScheduleTaskOut(context, communicator); +} + +void SyncEngine::RemoteDataChangedTask(ISyncTaskContext *context, const ICommunicator *communicator, Message *inMsg) +{ + do { + std::string deviceId = context->GetDeviceId(); + if (onRemoteDataChanged_ && deviceManager_->IsDeviceOnline(deviceId)) { + onRemoteDataChanged_(deviceId); + } else { + LOGE("[SyncEngine] onRemoteDataChanged is null!"); + } + } while (false); + delete inMsg; + inMsg = nullptr; + ScheduleTaskOut(context, communicator); +} + +void SyncEngine::ScheduleTaskOut(ISyncTaskContext *context, const ICommunicator *communicator) +{ + (void)DealMsgUtilQueueEmpty(); + DecExecTaskCount(); + RefObject::DecObjRef(communicator); + RefObject::DecObjRef(context); +} + +int SyncEngine::DealMsgUtilQueueEmpty() +{ + if (!isActive_) { + return -E_BUSY; // db is closing just return + } + int errCode = E_OK; + Message *inMsg = nullptr; + { + std::lock_guard lock(queueLock_); + if (msgQueue_.empty()) { + return errCode; + } + inMsg = msgQueue_.front(); + msgQueue_.pop_front(); + queueCacheSize_ -= GetMsgSize(inMsg); + } + + IncExecTaskCount(); + // it will deal with the first message in queue, we should increase object reference counts and sure that resources + // could be prevented from destroying by other threads. + do { + ISyncTaskContext *nextContext = GetConextForMsg(inMsg->GetTarget(), errCode); + if (errCode != E_OK) { + break; + } + errCode = ScheduleDealMsg(nextContext, inMsg); + if (errCode != E_OK) { + RefObject::DecObjRef(nextContext); + } + } while (false); + if (errCode != E_OK) { + delete inMsg; + inMsg = nullptr; + DecExecTaskCount(); + } + return errCode; +} + +ISyncTaskContext *SyncEngine::GetConextForMsg(const std::string &targetDev, int &errCode) +{ + ISyncTaskContext *context = nullptr; + { + std::lock_guard lock(contextMapLock_); + context = FindSyncTaskContext(targetDev); + if (context != nullptr) { + if (context->IsKilled()) { + errCode = -E_OBJ_IS_KILLED; + return nullptr; + } + } else { + if (IsKilled()) { + errCode = -E_OBJ_IS_KILLED; + return nullptr; + } + context = GetSyncTaskContext(targetDev, errCode); + if (context == nullptr) { + return nullptr; + } + } + // IncRef for context to make sure context is valid, when task run another thread + RefObject::IncObjRef(context); + } + return context; +} + +int SyncEngine::ScheduleDealMsg(ISyncTaskContext *context, Message *inMsg) +{ + if (inMsg == nullptr) { + LOGE("[SyncEngine] MessageReciveCallback inMsg is null!"); + DecExecTaskCount(); + return E_OK; + } + RefObject::IncObjRef(communicatorProxy_); + int errCode = E_OK; + // deal remote local data changed message + if (inMsg->GetMessageId() == LOCAL_DATA_CHANGED) { + RemoteDataChangedTask(context, communicatorProxy_, inMsg); + } else { + errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind(&SyncEngine::MessageReciveCallbackTask, + this, context, communicatorProxy_, inMsg)); + } + + if (errCode != E_OK) { + LOGE("[SyncEngine] MessageReciveCallbackTask Schedule failed err %d", errCode); + RefObject::DecObjRef(communicatorProxy_); + } + return errCode; +} + +void SyncEngine::MessageReciveCallback(const std::string &targetDev, Message *inMsg) +{ + IncExecTaskCount(); + int errCode = MessageReciveCallbackInner(targetDev, inMsg); + if (errCode != E_OK) { + delete inMsg; + inMsg = nullptr; + DecExecTaskCount(); + LOGE("[SyncEngine] MessageReciveCallback failed!"); + } +} + +int SyncEngine::MessageReciveCallbackInner(const std::string &targetDev, Message *inMsg) +{ + if (targetDev.empty() || inMsg == nullptr) { + LOGE("[SyncEngine][MessageReciveCallback] from a invalid device or inMsg is null "); + return -E_INVALID_ARGS; + } + if (!isActive_) { + LOGE("[SyncEngine] engine is closing, ignore msg"); + return -E_BUSY; + } + int msgSize = 0; + if (!IsSkipCalculateLen(inMsg)) { + msgSize = GetMsgSize(inMsg); + if (msgSize <= 0) { + LOGE("[SyncEngine] GetMsgSize makes a mistake"); + return -E_NOT_SUPPORT; + } + } + + { + std::lock_guard lock(queueLock_); + if ((queueCacheSize_ + msgSize) > maxQueueCacheSize_) { + LOGE("[SyncEngine] The size of message queue is beyond maximum"); + discardMsgNum_++; + return -E_BUSY; + } + + if (execTaskCount_ > MAX_EXEC_NUM) { + PutMsgIntoQueue(targetDev, inMsg, msgSize); + // task dont exec here + DecExecTaskCount(); + return E_OK; + } + } + + int errCode = E_OK; + ISyncTaskContext *nextContext = GetConextForMsg(targetDev, errCode); + if (errCode != E_OK) { + return errCode; + } + LOGD("[SyncEngine] MessageReciveCallback MSG ID = %d", inMsg->GetMessageId()); + return ScheduleDealMsg(nextContext, inMsg); +} + +void SyncEngine::PutMsgIntoQueue(const std::string &targetDev, Message *inMsg, int msgSize) +{ + if (inMsg->GetMessageId() == LOCAL_DATA_CHANGED) { + auto iter = std::find_if(msgQueue_.begin(), msgQueue_.end(), + [&targetDev](const Message *msg) { + return targetDev == msg->GetTarget() && msg->GetMessageId() == LOCAL_DATA_CHANGED; + }); + if (iter != msgQueue_.end()) { + delete inMsg; + inMsg = nullptr; + return; + } + } + inMsg->SetTarget(targetDev); + msgQueue_.push_back(inMsg); + queueCacheSize_ += msgSize; + LOGE("[SyncEngine] The quantity of executing threads is beyond maximum. msgQueueSize = %zu", msgQueue_.size()); +} + +int SyncEngine::GetMsgSize(const Message *inMsg) const +{ + switch (inMsg->GetMessageId()) { + case TIME_SYNC_MESSAGE: + return TimeSync::CalculateLen(inMsg); + case ABILITY_SYNC_MESSAGE: + return AbilitySync::CalculateLen(inMsg); + case DATA_SYNC_MESSAGE: + case QUERY_SYNC_MESSAGE: + case CONTROL_SYNC_MESSAGE: + return SingleVerSerializeManager::CalculateLen(inMsg); +#ifndef OMIT_MULTI_VER + case COMMIT_HISTORY_SYNC_MESSAGE: + return CommitHistorySync::CalculateLen(inMsg); + case MULTI_VER_DATA_SYNC_MESSAGE: + return MultiVerDataSync::CalculateLen(inMsg); + case VALUE_SLICE_SYNC_MESSAGE: + return ValueSliceSync::CalculateLen(inMsg); +#endif + case LOCAL_DATA_CHANGED: + return DeviceManager::CalculateLen(); + default: + LOGE("[SyncEngine] GetMsgSize not support msgId:%u", inMsg->GetMessageId()); + return -E_NOT_SUPPORT; + } +} + +ISyncTaskContext *SyncEngine::FindSyncTaskContext(const std::string &deviceId) +{ + auto iter = syncTaskContextMap_.find(deviceId); + if (iter != syncTaskContextMap_.end()) { + ISyncTaskContext *context = iter->second; + return context; + } + return nullptr; +} + +ISyncTaskContext *SyncEngine::GetSyncTaskContextAndInc(const std::string &deviceId) +{ + ISyncTaskContext *context = nullptr; + std::lock_guard lock(contextMapLock_); + context = FindSyncTaskContext(deviceId); + if (context == nullptr) { + LOGI("[SyncEngine] dev=%s, context is null, no need to clear sync operation", STR_MASK(deviceId)); + return nullptr; + } + if (context->IsKilled()) { + LOGI("[SyncEngine] context is killing"); + return nullptr; + } + RefObject::IncObjRef(context); + return context; +} + +ISyncTaskContext *SyncEngine::GetSyncTaskContext(const std::string &deviceId, int &errCode) +{ + ISyncTaskContext *context = CreateSyncTaskContext(); + if (context == nullptr) { + errCode = -E_OUT_OF_MEMORY; + LOGE("[SyncEngine] SyncTaskContext alloc failed, may be no memory available!"); + return nullptr; + } + errCode = context->Initialize(deviceId, syncInterface_, metadata_, communicatorProxy_); + if (errCode != E_OK) { + LOGE("[SyncEngine] context init failed err %d, dev %s", errCode, STR_MASK(deviceId)); + RefObject::DecObjRef(context); + context = nullptr; + return nullptr; + } + syncTaskContextMap_.insert(std::pair(deviceId, context)); + // IncRef for SyncEngine to make sure SyncEngine is valid when context access + RefObject::IncObjRef(this); + context->OnLastRef([this, deviceId]() { + LOGD("[SyncEngine] SyncTaskContext for id %s finalized", STR_MASK(deviceId)); + RefObject::DecObjRef(this); + }); + context->RegOnSyncTask(std::bind(&SyncEngine::ExecSyncTask, this, context)); + return context; +} + +int SyncEngine::ExecSyncTask(ISyncTaskContext *context) +{ + if (IsKilled()) { + return -E_OBJ_IS_KILLED; + } + + AutoLock lockGuard(context); + int status = context->GetTaskExecStatus(); + if ((status == SyncTaskContext::RUNNING) || context->IsKilled()) { + return -E_NOT_SUPPORT; + } + context->SetTaskExecStatus(ISyncTaskContext::RUNNING); + if (!context->IsTargetQueueEmpty()) { + context->MoveToNextTarget(); + int checkErrCode = E_OK; + // rdb don't support PermissionCheck + if (syncInterface_->GetInterfaceType() != ISyncInterface::SYNC_RELATION) { + checkErrCode = RunPermissionCheck(context->GetDeviceId(), + GetPermissionCheckFlag(context->IsAutoSync(), context->GetMode())); + } + if (checkErrCode != E_OK) { + context->SetOperationStatus(SyncOperation::OP_PERMISSION_CHECK_FAILED); + context->SetTaskExecStatus(ISyncTaskContext::FINISHED); + return checkErrCode; + } + context->UnlockObj(); + int errCode = context->StartStateMachine(); + context->LockObj(); + if (errCode != E_OK) { + LOGE("[SyncEngine] machine StartSync failed"); + context->SetOperationStatus(SyncOperation::OP_FAILED); + return errCode; + } + } else { + LOGD("[SyncEngine] ExecSyncTask finished"); + context->SetTaskExecStatus(ISyncTaskContext::FINISHED); + } + return E_OK; +} + +int SyncEngine::GetQueueCacheSize() const +{ + return queueCacheSize_; +} + +unsigned int SyncEngine::GetDiscardMsgNum() const +{ + return discardMsgNum_; +} + +unsigned int SyncEngine::GetMaxExecNum() const +{ + return MAX_EXEC_NUM; +} + +void SyncEngine::SetMaxQueueCacheSize(int value) +{ + maxQueueCacheSize_ = value; +} + +uint8_t SyncEngine::GetPermissionCheckFlag(bool isAutoSync, int syncMode) +{ + uint8_t flag = 0; + int mode = SyncOperation::TransferSyncMode(syncMode); + if (mode == SyncModeType::PUSH || mode == SyncModeType::RESPONSE_PULL) { + flag = CHECK_FLAG_SEND; + } else if (mode == SyncModeType::PULL) { + flag = CHECK_FLAG_RECEIVE; + } else if (mode == SyncModeType::PUSH_AND_PULL) { + flag = CHECK_FLAG_SEND | CHECK_FLAG_RECEIVE; + } + if (isAutoSync) { + flag = flag | CHECK_FLAG_AUTOSYNC; + } + if (mode != SyncModeType::RESPONSE_PULL) { + // it means this sync is started by local + flag = flag | CHECK_FLAG_SPONSOR; + } + return flag; +} + +int SyncEngine::RunPermissionCheck(const std::string &deviceId, uint8_t flag) const +{ + std::string appId = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::APP_ID, ""); + std::string userId = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::USER_ID, ""); + std::string storeId = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::STORE_ID, ""); + int errCode = RuntimeContext::GetInstance()->RunPermissionCheck(userId, appId, storeId, deviceId, flag); + if (errCode != E_OK) { + LOGE("[SyncEngine] RunPermissionCheck not pass errCode:%d, flag:%d, %s Label=%s", + errCode, flag, STR_MASK(deviceId), label_.c_str()); + } + return errCode; +} + +std::string SyncEngine::GetLabel() const +{ + return label_; +} + +bool SyncEngine::GetSyncRetry() const +{ + return isSyncRetry_; +} + +void SyncEngine::SetSyncRetry(bool isRetry) +{ + if (isSyncRetry_ == isRetry) { + LOGI("sync retry is equal, syncTry=%d, no need to set.", isRetry); + return; + } + isSyncRetry_ = isRetry; + LOGI("[SyncEngine] SetSyncRetry:%d ok", isRetry); + std::lock_guard lock(contextMapLock_); + for (auto &iter : syncTaskContextMap_) { + ISyncTaskContext *context = iter.second; + if (context != nullptr) { + context->SetSyncRetry(isRetry); + } + } +} + +int SyncEngine::SetEqualIdentifier(const std::string &identifier, const std::vector &targets) +{ + if (!isActive_) { + LOGI("[SyncEngine] engine is closed, just put into map"); + return E_OK; + } + ICommunicator *communicator = nullptr; + { + std::lock_guard lock(equalCommunicatorsLock_); + if (equalCommunicators_.count(identifier) != 0) { + communicator = equalCommunicators_[identifier]; + } else { + int errCode = E_OK; + communicator = AllocCommunicator(identifier, errCode); + if (communicator == nullptr) { + return errCode; + } + equalCommunicators_[identifier] = communicator; + } + } + std::string targetDevices; + for (const auto &dev : targets) { + targetDevices += DBCommon::StringMasking(dev) + ","; + } + LOGI("[SyncEngine] set equal identifier=%s, original=%s, targetDevices=%s", + DBCommon::TransferStringToHex(identifier).c_str(), label_.c_str(), + targetDevices.substr(0, targetDevices.size() - 1).c_str()); + communicatorProxy_->SetEqualCommunicator(communicator, identifier, targets); + communicator->Activate(); + return E_OK; +} + +void SyncEngine::SetEqualIdentifier() +{ + std::map> equalIdentifier; // key: equalIdentifier value: devices + for (auto &item : equalIdentifierMap_) { + if (equalIdentifier.find(item.second) == equalIdentifier.end()) { + equalIdentifier[item.second] = {item.first}; + } else { + equalIdentifier[item.second].push_back(item.first); + } + } + for (auto &item : equalIdentifier) { + SetEqualIdentifier(item.first, item.second); + } +} + +void SyncEngine::SetEqualIdentifierMap(const std::string &identifier, const std::vector &targets) +{ + for (auto iter = equalIdentifierMap_.begin(); iter != equalIdentifierMap_.end();) { + if (identifier == iter->second) { + iter = equalIdentifierMap_.erase(iter); + continue; + } + iter++; + } + for (auto &device : targets) { + equalIdentifierMap_[device] = identifier; + } +} + +void SyncEngine::OfflineHandleByDevice(const std::string &deviceId) +{ + if (communicatorProxy_ == nullptr) { + return; + } + // db closed or device is offline + // clear remote subscribe and trigger + std::vector remoteQueryId; + subManager_->GetRemoteSubscribeQueryIds(deviceId, remoteQueryId); + subManager_->ClearRemoteSubscribeQuery(deviceId); + static_cast(syncInterface_)->RemoveSubscribe(remoteQueryId); + // get context and Inc context if context is not nullprt + ISyncTaskContext *context = GetSyncTaskContextAndInc(deviceId); + if (context != nullptr) { + context->SetIsNeedResetAbilitySync(true); + } + if (communicatorProxy_->IsDeviceOnline(deviceId)) { + LOGI("[SyncEngine] target dev=%s is online, no need to clear task.", STR_MASK(deviceId)); + RefObject::DecObjRef(context); + return; + } + // means device is offline, clear local subscribe + subManager_->ClearLocalSubscribeQuery(deviceId); + // clear sync task + if (context != nullptr) { + context->ClearAllSyncTask(); + RefObject::DecObjRef(context); + } +} + +void SyncEngine::GetLocalSubscribeQueries(const std::string &device, std::vector &subscribeQueries) +{ + subManager_->GetLocalSubscribeQueries(device, subscribeQueries); +} + +void SyncEngine::GetRemoteSubscribeQueryIds(const std::string &device, std::vector &subscribeQueryIds) +{ + subManager_->GetRemoteSubscribeQueryIds(device, subscribeQueryIds); +} + +void SyncEngine::GetRemoteSubscribeQueries(const std::string &device, std::vector &subscribeQueries) +{ + subManager_->GetRemoteSubscribeQueries(device, subscribeQueries); +} + +void SyncEngine::PutUnfiniedSubQueries(const std::string &device, std::vector &subscribeQueries) +{ + subManager_->PutLocalUnFiniedSubQueries(device, subscribeQueries); +} + +void SyncEngine::GetAllUnFinishSubQueries(std::map> &allSyncQueries) +{ + subManager_->GetAllUnFinishSubQueries(allSyncQueries); +} + +ICommunicator *SyncEngine::AllocCommunicator(const std::string &identifier, int &errCode) +{ + ICommunicatorAggregator *communicatorAggregator = nullptr; + errCode = RuntimeContext::GetInstance()->GetCommunicatorAggregator(communicatorAggregator); + if (communicatorAggregator == nullptr) { + LOGE("[SyncEngine] Get ICommunicatorAggregator error when SetEqualIdentifier err = %d", errCode); + return nullptr; + } + std::vector identifierVect(identifier.begin(), identifier.end()); + auto communicator = communicatorAggregator->AllocCommunicator(identifierVect, errCode); + if (communicator == nullptr) { + LOGE("[SyncEngine] AllocCommunicator error when SetEqualIdentifier! err = %d", errCode); + return communicator; + } + + errCode = communicator->RegOnMessageCallback( + std::bind(&SyncEngine::MessageReciveCallback, this, std::placeholders::_1, std::placeholders::_2), + []() {}); + if (errCode != E_OK) { + LOGE("[SyncEngine] SyncRequestCallback register failed in SetEqualIdentifier! err = %d", errCode); + communicatorAggregator->ReleaseCommunicator(communicator); + return nullptr; + } + + errCode = communicator->RegOnConnectCallback( + std::bind(&DeviceManager::OnDeviceConnectCallback, deviceManager_, + std::placeholders::_1, std::placeholders::_2), nullptr); + if (errCode != E_OK) { + LOGE("[SyncEngine][RegConnCB] register failed in SetEqualIdentifier! err %d", errCode); + communicator->RegOnMessageCallback(nullptr, nullptr); + communicatorAggregator->ReleaseCommunicator(communicator); + return nullptr; + } + + return communicator; +} + +void SyncEngine::UnRegCommunicatorsCallback() +{ + if (communicator_ != nullptr) { + communicator_->RegOnMessageCallback(nullptr, nullptr); + communicator_->RegOnConnectCallback(nullptr, nullptr); + communicator_->RegOnSendableCallback(nullptr, nullptr); + } + std::lock_guard lock(equalCommunicatorsLock_); + for (const auto &iter : equalCommunicators_) { + iter.second->RegOnMessageCallback(nullptr, nullptr); + iter.second->RegOnConnectCallback(nullptr, nullptr); + iter.second->RegOnSendableCallback(nullptr, nullptr); + } +} + +void SyncEngine::ReleaseCommunicators() +{ + RefObject::KillAndDecObjRef(communicatorProxy_); + communicatorProxy_ = nullptr; + ICommunicatorAggregator *communicatorAggregator = nullptr; + int errCode = RuntimeContext::GetInstance()->GetCommunicatorAggregator(communicatorAggregator); + if (communicatorAggregator == nullptr) { + LOGF("[SyncEngine] ICommunicatorAggregator get failed when fialize SyncEngine err %d", errCode); + return; + } + + if (communicator_ != nullptr) { + communicatorAggregator->ReleaseCommunicator(communicator_); + communicator_ = nullptr; + } + + std::lock_guard lock(equalCommunicatorsLock_); + for (auto &iter : equalCommunicators_) { + communicatorAggregator->ReleaseCommunicator(iter.second); + } + equalCommunicators_.clear(); +} + +bool SyncEngine::IsSkipCalculateLen(const Message *inMsg) +{ + if (inMsg->IsFeedbackError()) { + LOGE("[SyncEngine] Feedback Message with errorNo=%u.", inMsg->GetErrorNo()); + return true; + } + return false; +} + +void SyncEngine::GetSubscribeSyncParam(const std::string &device, const QuerySyncObject &query, + InternalSyncParma &outParam) +{ + outParam.devices = { device }; + outParam.mode = SyncModeType::AUTO_SUBSCRIBE_QUERY; + outParam.isQuerySync = true; + outParam.syncQuery = query; +} + +void SyncEngine::GetQueryAutoSyncParam(const std::string &device, const QuerySyncObject &query, + InternalSyncParma &outParam) +{ + outParam.devices = { device }; + outParam.mode = SyncModeType::AUTO_PUSH; + outParam.isQuerySync = true; + outParam.syncQuery = query; +} + +int SyncEngine::StartAutoSubscribeTimer() +{ + return E_OK; +} + +void SyncEngine::StopAutoSubscribeTimer() +{ +} + +int SyncEngine::InitTimeChangedListener() +{ + int errCode = E_OK; + timeChangedListener_ = RuntimeContext::GetInstance()->RegisterTimeChangedLister( + [this](void *changedOffset) { + if (changedOffset == nullptr) { + return; + } + TimeOffset changedTimeOffset = *(reinterpret_cast(changedOffset)) * + static_cast(TimeHelper::TO_100_NS); + TimeOffset orgOffset = this->metadata_->GetLocalTimeOffset() - changedTimeOffset; + Timestamp currentSysTime = TimeHelper::GetSysCurrentTime(); + Timestamp maxItemTime = 0; + this->syncInterface_->GetMaxTimestamp(maxItemTime); + if ((currentSysTime + static_cast(orgOffset)) <= maxItemTime) { + orgOffset = static_cast(maxItemTime - currentSysTime + TimeHelper::MS_TO_100_NS); // 1ms + } + this->metadata_->SaveLocalTimeOffset(orgOffset); + }, errCode); + if (timeChangedListener_ == nullptr) { + LOGE("[SyncEngine] Init RegisterTimeChangedLister failed"); + return errCode; + } + return E_OK; +} + +int SyncEngine::SubscribeLimitCheck(const std::vector &devices, QuerySyncObject &query) const +{ + return subManager_->LocalSubscribeLimitCheck(devices, query); +} + + +void SyncEngine::ClearInnerResource() +{ + if (timeChangedListener_ != nullptr) { + timeChangedListener_->Drop(true); + timeChangedListener_ = nullptr; + } + if (syncInterface_ != nullptr) { + syncInterface_->DecRefCount(); + syncInterface_ = nullptr; + } + if (deviceManager_ != nullptr) { + delete deviceManager_; + deviceManager_ = nullptr; + } + communicator_ = nullptr; + metadata_ = nullptr; + onRemoteDataChanged_ = nullptr; + offlineChanged_ = nullptr; + queryAutoSyncCallback_ = nullptr; +} + +bool SyncEngine::IsEngineActive() const +{ + return isActive_; +} + +void SyncEngine::SchemaChange() +{ + std::lock_guard lock(contextMapLock_); + for (auto &entry : syncTaskContextMap_) { + auto context = entry.second; + if (context->IsKilled()) { + continue; + } + // IncRef for SyncEngine to make sure context is valid, to avoid a big lock + context->SchemaChange(); + } +} + +void SyncEngine::IncExecTaskCount() +{ + std::lock_guard incLock(execTaskCountLock_); + execTaskCount_++; +} + +void SyncEngine::DecExecTaskCount() +{ + { + std::lock_guard decLock(execTaskCountLock_); + execTaskCount_--; + } + execTaskCv_.notify_all(); +} + +void SyncEngine::Dump(int fd) +{ + std::string communicatorLabel; + if (communicatorProxy_ != nullptr) { + communicatorProxy_->GetLocalIdentity(communicatorLabel); + } + DBDumpHelper::Dump(fd, "\tcommunicator label = %s, equalIdentify Info [\n", communicatorLabel.c_str()); + if (communicatorProxy_ != nullptr) { + communicatorProxy_->GetLocalIdentity(communicatorLabel); + communicatorProxy_->Dump(fd); + } + DBDumpHelper::Dump(fd, "\t]\n\tcontext info [\n"); + // dump context info + std::lock_guard autoLock(contextMapLock_); + for (const auto &entry : syncTaskContextMap_) { + entry.second->Dump(fd); + } + DBDumpHelper::Dump(fd, "\t]\n\n"); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/sync_engine.h b/mock/distributeddb/syncer/src/sync_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..950faa0b51ee1b61a496aac2455b29c5453c77fa --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_engine.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNC_ENGINE_H +#define SYNC_ENGINE_H + +#include +#include +#include + +#include "communicator_proxy.h" +#include "device_manager.h" +#include "isync_engine.h" +#include "isync_task_context.h" +#include "subscribe_manager.h" +#include "task_pool.h" + +namespace DistributedDB { +constexpr uint16_t NEW_SEND_TASK = 1; + +class SyncEngine : public ISyncEngine { +public: + SyncEngine(); + ~SyncEngine() override; + + // Do some init things + int Initialize(ISyncInterface *syncInterface, std::shared_ptr &metadata, + const std::function &onRemoteDataChanged, + const std::function &offlineChanged, + const std::function &queryAutoSyncCallback) override; + + // Do some things, when db close. + int Close() override; + + // Alloc and Add sync SyncTarget + // return E_OK if operator success. + int AddSyncOperation(SyncOperation *operation) override; + + // Clear the SyncTarget matched the syncId. + void RemoveSyncOperation(int syncId) override; + + // notify other devices data has changed + void BroadCastDataChanged() const override; + + // Get Online devices + void GetOnlineDevices(std::vector &devices) const override; + + // Register the device connect callback, this function must be called after Engine inited + void RegConnectCallback() override; + + // Get the queue cache memory size + int GetQueueCacheSize() const; + + // Get the number of message which is discarded + unsigned int GetDiscardMsgNum() const; + + // Get the maximum of executing message number + unsigned int GetMaxExecNum() const; + + // Set the maximum of queue cache memory size + void SetMaxQueueCacheSize(int value); + + std::string GetLabel() const override; + + bool GetSyncRetry() const; + void SetSyncRetry(bool isRetry) override; + + // Set an equal identifier for this database, After this called, send msg to the target will use this identifier + int SetEqualIdentifier(const std::string &identifier, const std::vector &targets) override; + + void SetEqualIdentifier() override; + + void SetEqualIdentifierMap(const std::string &identifier, const std::vector &targets) override; + + void OfflineHandleByDevice(const std::string &deviceId); + + void GetLocalSubscribeQueries(const std::string &device, std::vector &subscribeQueries); + + // subscribeQueries item is queryId + void GetRemoteSubscribeQueryIds(const std::string &device, std::vector &subscribeQueryIds); + + void GetRemoteSubscribeQueries(const std::string &device, std::vector &subscribeQueries); + + void PutUnfiniedSubQueries(const std::string &device, std::vector &subscribeQueries); + + void GetAllUnFinishSubQueries(std::map> &allSyncQueries); + + // used by SingleVerSyncer when db online + int StartAutoSubscribeTimer() override; + + // used by SingleVerSyncer when remote/local db closed + void StopAutoSubscribeTimer() override; + + int SubscribeLimitCheck(const std::vector &devices, QuerySyncObject &query) const override; + + bool IsEngineActive() const override; + + void SchemaChange() override; + + void Dump(int fd) override; + +protected: + // Create a context + virtual ISyncTaskContext *CreateSyncTaskContext() = 0; + + // Find SyncTaskContext from the map + ISyncTaskContext *FindSyncTaskContext(const std::string &deviceId); + ISyncTaskContext *GetSyncTaskContextAndInc(const std::string &deviceId); + void GetQueryAutoSyncParam(const std::string &device, const QuerySyncObject &query, InternalSyncParma &outParam); + void GetSubscribeSyncParam(const std::string &device, const QuerySyncObject &query, InternalSyncParma &outParam); + + ISyncInterface *syncInterface_; + // Used to store all send sync task infos (such as pull sync response, and push sync request) + std::map syncTaskContextMap_; + std::mutex contextMapLock_; + std::shared_ptr subManager_; + std::function queryAutoSyncCallback_; + +private: + + // Init DeviceManager set callback + int InitDeviceManager(const std::function &onRemoteDataChanged, + const std::function &offlineChanged); + + int InitTimeChangedListener(); + + ISyncTaskContext *GetSyncTaskContext(const std::string &deviceId, int &errCode); + + // Init Comunicator, register callbacks + int InitComunicator(const ISyncInterface *syncInterface); + + // Add the sync task info to the map. + int AddSyncOperForContext(const std::string &deviceId, SyncOperation *operation); + + // Sync Request CallbackTask run at a sub thread. + void MessageReciveCallbackTask(ISyncTaskContext *context, const ICommunicator *communicator, Message *inMsg); + + void RemoteDataChangedTask(ISyncTaskContext *context, const ICommunicator *communicator, Message *inMsg); + + void ScheduleTaskOut(ISyncTaskContext *context, const ICommunicator *communicator); + + // wrapper of MessageReciveCallbackTask + void MessageReciveCallback(const std::string &targetDev, Message *inMsg); + + // Sync Request Callback + int MessageReciveCallbackInner(const std::string &targetDev, Message *inMsg); + + // Exec the given SyncTarget. and callback onComplete. + int ExecSyncTask(ISyncTaskContext *context); + + // Anti-DOS attack + void PutMsgIntoQueue(const std::string &targetDev, Message *inMsg, int msgSize); + + // Get message size + int GetMsgSize(const Message *inMsg) const; + + // Do not run MessageReceiveCallbackTask until msgQueue is empty + int DealMsgUtilQueueEmpty(); + + // Handle message in order. + int ScheduleDealMsg(ISyncTaskContext *context, Message *inMsg); + + // Schedule Sync Task + void ScheduleSyncTask(ISyncTaskContext *context); + + ISyncTaskContext *GetConextForMsg(const std::string &targetDev, int &errCode); + + int RunPermissionCheck(const std::string &deviceId, uint8_t flag) const; + + ICommunicator *AllocCommunicator(const std::string &identifier, int &errCode); + + void UnRegCommunicatorsCallback(); + + void ReleaseCommunicators(); + + bool IsSkipCalculateLen(const Message *inMsg); + + void ClearInnerResource(); + + static uint8_t GetPermissionCheckFlag(bool isAutoSync, int syncMode); + + void IncExecTaskCount(); + + void DecExecTaskCount(); + + ICommunicator *communicator_; + DeviceManager *deviceManager_; + std::function onRemoteDataChanged_; + std::function offlineChanged_; + std::shared_ptr metadata_; + std::deque msgQueue_; + NotificationChain::Listener *timeChangedListener_; + uint32_t execTaskCount_; + std::string label_; + bool isSyncRetry_; + CommunicatorProxy *communicatorProxy_; + std::mutex equalCommunicatorsLock_; + std::map equalCommunicators_; + + static int queueCacheSize_; + static int maxQueueCacheSize_; + static unsigned int discardMsgNum_; + static const unsigned int MAX_EXEC_NUM = 7; // Set the maximum of threads as 6 < 7 + static constexpr int DEFAULT_CACHE_SIZE = 160 * 1024 * 1024; // Initial the default cache size of queue as 160MB + static std::mutex queueLock_; + std::atomic isActive_; + + // key: device value: equalIdentifier + std::map equalIdentifierMap_; + std::mutex execTaskCountLock_; + std::condition_variable execTaskCv_; +}; +} // namespace DistributedDB + +#endif // SYNC_ENGINE_H diff --git a/mock/distributeddb/syncer/src/sync_operation.cpp b/mock/distributeddb/syncer/src/sync_operation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..48c7fc7e19c8fdd35530e996578d51c275f45c16 --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_operation.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sync_operation.h" +#include "db_errno.h" +#include "log_print.h" +#include "performance_analysis.h" + +namespace DistributedDB { +SyncOperation::SyncOperation(uint32_t syncId, const std::vector &devices, + int mode, const UserCallback &userCallback, bool isBlockSync) + : devices_(devices), + syncId_(syncId), + mode_(mode), + userCallback_(userCallback), + isBlockSync_(isBlockSync), + isAutoSync_(false), + isFinished_(false), + semaphore_(nullptr), + query_(QuerySyncObject()), + isQuerySync_(false), + isAutoSubscribe_(false) +{ +} + +SyncOperation::~SyncOperation() +{ + LOGD("SyncOperation::~SyncOperation()"); + Finalize(); +} + +int SyncOperation::Initialize() +{ + LOGD("[SyncOperation] Init SyncOperation id:%d.", syncId_); + AutoLock lockGuard(this); + for (const std::string &deviceId : devices_) { + statuses_.insert(std::pair(deviceId, OP_WAITING)); + } + + if (mode_ == AUTO_PUSH) { + mode_ = PUSH; + isAutoSync_ = true; + } else if (mode_ == AUTO_PULL) { + mode_ = PULL; + isAutoSync_ = true; + } else if (mode_ == AUTO_SUBSCRIBE_QUERY) { + mode_ = SUBSCRIBE_QUERY; + isAutoSubscribe_ = true; + } + if (isBlockSync_) { + semaphore_ = std::make_unique(0); + } + + return E_OK; +} + +void SyncOperation::SetOnSyncFinalize(const OnSyncFinalize &callback) +{ + onFinalize_ = callback; +} + +void SyncOperation::SetOnSyncFinished(const OnSyncFinished &callback) +{ + onFinished_ = callback; +} + +void SyncOperation::SetStatus(const std::string &deviceId, int status) +{ + LOGD("[SyncOperation] SetStatus dev %s{private} status %d", deviceId.c_str(), status); + AutoLock lockGuard(this); + if (IsKilled()) { + LOGE("[SyncOperation] SetStatus failed, the SyncOperation has been killed!"); + return; + } + if (isFinished_) { + LOGI("[SyncOperation] SetStatus already finished"); + return; + } + + auto iter = statuses_.find(deviceId); + if (iter != statuses_.end()) { + if (iter->second >= OP_FINISHED_ALL) { + return; + } + iter->second = status; + return; + } +} + +void SyncOperation::SetUnfinishedDevStatus(int status) +{ + LOGD("[SyncOperation] SetUnfinishedDevStatus status %d", status); + AutoLock lockGuard(this); + if (IsKilled()) { + LOGE("[SyncOperation] SetUnfinishedDevStatus failed, the SyncOperation has been killed!"); + return; + } + if (isFinished_) { + LOGI("[SyncOperation] SetUnfinishedDevStatus already finished"); + return; + } + for (auto &item : statuses_) { + if (item.second >= OP_FINISHED_ALL) { + continue; + } + item.second = status; + } +} + +int SyncOperation::GetStatus(const std::string &deviceId) const +{ + AutoLock lockGuard(this); + auto iter = statuses_.find(deviceId); + if (iter != statuses_.end()) { + return iter->second; + } + return -E_INVALID_ARGS; +} + +uint32_t SyncOperation::GetSyncId() const +{ + return syncId_; +} + +int SyncOperation::GetMode() const +{ + return mode_; +} + +void SyncOperation::Finished() +{ + std::map tmpStatus; + { + AutoLock lockGuard(this); + if (IsKilled() || isFinished_) { + return; + } + isFinished_ = true; + tmpStatus = statuses_; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_ACK_RECV_TO_USER_CALL_BACK); + } + if (userCallback_) { + LOGI("[SyncOperation] Sync %d finished call onComplete.", syncId_); + if (IsBlockSync()) { + userCallback_(tmpStatus); + } else { + RefObject::IncObjRef(this); + int errCode = RuntimeContext::GetInstance()->ScheduleQueuedTask(identifier_, [this, tmpStatus] { + userCallback_(tmpStatus); + RefObject::DecObjRef(this); + }); + if (errCode != E_OK) { + LOGE("[Finished] SyncOperation Finished userCallback_ retCode:%d", errCode); + RefObject::DecObjRef(this); + } + } + } + if (onFinished_) { + LOGD("[SyncOperation] Sync %d finished call onFinished.", syncId_); + onFinished_(syncId_); + } +} + +const std::vector &SyncOperation::GetDevices() const +{ + return devices_; +} + +void SyncOperation::WaitIfNeed() +{ + if (isBlockSync_ && (semaphore_ != nullptr)) { + LOGD("[SyncOperation] Wait."); + semaphore_->WaitSemaphore(); + } +} + +void SyncOperation::NotifyIfNeed() +{ + if (isBlockSync_ && (semaphore_ != nullptr)) { + LOGD("[SyncOperation] Notify."); + semaphore_->SendSemaphore(); + } +} + +bool SyncOperation::IsAutoSync() const +{ + return isAutoSync_; +} + +bool SyncOperation::IsBlockSync() const +{ + return isBlockSync_; +} + +bool SyncOperation::IsAutoControlCmd() const +{ + return isAutoSubscribe_; +} + +bool SyncOperation::CheckIsAllFinished() const +{ + AutoLock lockGuard(this); + for (const auto &iter : statuses_) { + if (iter.second < OP_FINISHED_ALL) { + return false; + } + } + return true; +} + +void SyncOperation::Finalize() +{ + if ((syncId_ > 0) && onFinalize_) { + LOGD("[SyncOperation] Callback SyncOperation onFinalize."); + onFinalize_(); + } +} + +void SyncOperation::SetQuery(const QuerySyncObject &query) +{ + query_ = query; + isQuerySync_ = true; + if (mode_ != SyncModeType::SUBSCRIBE_QUERY && mode_ != SyncModeType::UNSUBSCRIBE_QUERY) { + mode_ += QUERY_SYNC_MODE_BASE; + } +} + +QuerySyncObject SyncOperation::GetQuery() const +{ + return query_; +} + +bool SyncOperation::IsQuerySync() const +{ + return isQuerySync_; +} + +void SyncOperation::SetIdentifier(const std::vector &identifier) +{ + identifier_.assign(identifier.begin(), identifier.end()); +} + +SyncType SyncOperation::GetSyncType(int mode) +{ + static const std::map syncTypeMap = { + {SyncModeType::PUSH, SyncType::MANUAL_FULL_SYNC_TYPE}, + {SyncModeType::PULL, SyncType::MANUAL_FULL_SYNC_TYPE}, + {SyncModeType::PUSH_AND_PULL, SyncType::MANUAL_FULL_SYNC_TYPE}, + {SyncModeType::RESPONSE_PULL, SyncType::MANUAL_FULL_SYNC_TYPE}, + {SyncModeType::AUTO_PULL, SyncType::AUTO_SYNC_TYPE}, + {SyncModeType::AUTO_PUSH, SyncType::AUTO_SYNC_TYPE}, + {SyncModeType::QUERY_PUSH, SyncType::QUERY_SYNC_TYPE}, + {SyncModeType::QUERY_PULL, SyncType::QUERY_SYNC_TYPE}, + {SyncModeType::QUERY_PUSH_PULL, SyncType::QUERY_SYNC_TYPE}, + }; + auto iter = syncTypeMap.find(mode); + if (iter != syncTypeMap.end()) { + return iter->second; + } + return SyncType::INVALID_SYNC_TYPE; +} + +int SyncOperation::TransferSyncMode(int mode) +{ + // AUTO_PUSH and AUTO_PULL mode is used before sync, RESPONSE_PULL is regarded as push or query push mode. + // so for the three mode, it is no need to transferred. + if (mode >= SyncModeType::QUERY_PUSH && mode <= SyncModeType::QUERY_PUSH_PULL) { + return (mode - QUERY_SYNC_MODE_BASE); + } + return mode; +} + +std::string SyncOperation::GetQueryId() const +{ + return query_.GetIdentify(); +} + +const std::map &SyncOperation::DBStatusTransMap() +{ + static const std::map transMap = { + { static_cast(OP_FINISHED_ALL), OK }, + { static_cast(OP_TIMEOUT), TIME_OUT }, + { static_cast(OP_PERMISSION_CHECK_FAILED), PERMISSION_CHECK_FORBID_SYNC }, + { static_cast(OP_COMM_ABNORMAL), COMM_FAILURE }, + { static_cast(OP_SECURITY_OPTION_CHECK_FAILURE), SECURITY_OPTION_CHECK_ERROR }, + { static_cast(OP_EKEYREVOKED_FAILURE), EKEYREVOKED_ERROR }, + { static_cast(OP_SCHEMA_INCOMPATIBLE), SCHEMA_MISMATCH }, + { static_cast(OP_BUSY_FAILURE), BUSY }, + { static_cast(OP_QUERY_FORMAT_FAILURE), INVALID_QUERY_FORMAT }, + { static_cast(OP_QUERY_FIELD_FAILURE), INVALID_QUERY_FIELD }, + { static_cast(OP_NOT_SUPPORT), NOT_SUPPORT }, + { static_cast(OP_INTERCEPT_DATA_FAIL), INTERCEPT_DATA_FAIL }, + { static_cast(OP_MAX_LIMITS), OVER_MAX_LIMITS }, + { static_cast(OP_SCHEMA_CHANGED), DISTRIBUTED_SCHEMA_CHANGED }, + { static_cast(OP_INVALID_ARGS), INVALID_ARGS }, + { static_cast(OP_USER_CHANGED), USER_CHANGED}, + }; + return transMap; +} +DEFINE_OBJECT_TAG_FACILITIES(SyncOperation) +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/sync_operation.h b/mock/distributeddb/syncer/src/sync_operation.h new file mode 100644 index 0000000000000000000000000000000000000000..229c8320b9c557d00ae4935f52154c185d63ffa8 --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_operation.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNC_OPERATION_H +#define SYNC_OPERATION_H + +#include +#include +#include +#include +#include + +#include "ikvdb_sync_interface.h" +#include "notification_chain.h" +#include "query_sync_object.h" +#include "ref_object.h" +#include "runtime_context.h" +#include "semaphore_utils.h" +#include "sync_types.h" + +namespace DistributedDB { +class SyncOperation : public RefObject { +public: + enum Status { + OP_WAITING = 0, + OP_SYNCING, + OP_SEND_FINISHED, + OP_RECV_FINISHED, + OP_FINISHED_ALL, // status >= OP_FINISHED_ALL is final status. + OP_FAILED, + OP_TIMEOUT, + OP_PERMISSION_CHECK_FAILED, + OP_COMM_ABNORMAL, + OP_SECURITY_OPTION_CHECK_FAILURE, // remote device's SecurityOption not equal to local + OP_EKEYREVOKED_FAILURE, // EKEYREVOKED error + OP_BUSY_FAILURE, + OP_SCHEMA_INCOMPATIBLE, + OP_QUERY_FORMAT_FAILURE, + OP_QUERY_FIELD_FAILURE, + OP_NOT_SUPPORT, + OP_INTERCEPT_DATA_FAIL, + OP_MAX_LIMITS, + OP_SCHEMA_CHANGED, + OP_INVALID_ARGS, + OP_USER_CHANGED + }; + + using UserCallback = std::function)>; + using OnSyncFinished = std::function; + using OnSyncFinalize = std::function; + + SyncOperation(uint32_t syncId, const std::vector &devices, int mode, + const UserCallback &userCallback, bool isBlockSync); + + DISABLE_COPY_ASSIGN_MOVE(SyncOperation); + + // Init the status for callback + int Initialize(); + + // Set the OnSyncFinalize callback + void SetOnSyncFinalize(const OnSyncFinalize &callback); + + // Set the OnSyncFinished callback, it will be called either success or failed. + void SetOnSyncFinished(const OnSyncFinished &callback); + + // Set the sync status, running or finished + void SetStatus(const std::string &deviceId, int status); + + // Set the unfinished devices sync status, running or finished + void SetUnfinishedDevStatus(int status); + + // Set the identifier, used in SyncOperation::Finished + void SetIdentifier(const std::vector &identifier); + + // Get the sync status, running or finished + int GetStatus(const std::string &deviceId) const; + + // Get the sync id. + uint32_t GetSyncId() const; + + // Get the sync mode + int GetMode() const; + + // Used to call the onFinished and caller's on complete + void Finished(); + + // Get the deviceId of this sync status + const std::vector &GetDevices() const; + + // Wait if it's a block sync + void WaitIfNeed(); + + // Notify if it's a block sync + void NotifyIfNeed(); + + // Return if this sync is auto sync + bool IsAutoSync() const; + + // Return if this sync is block sync + bool IsBlockSync() const; + + // Return if this sync is AUTO_SUBSCRIBE_QUERY + bool IsAutoControlCmd() const; + + // Check if All devices sync finished. + bool CheckIsAllFinished() const; + + // For query sync + void SetQuery(const QuerySyncObject &query); + QuerySyncObject GetQuery() const; + bool IsQuerySync() const; + std::string GetQueryId() const; + static SyncType GetSyncType(int mode); + static int TransferSyncMode(int mode); + + static const std::map &DBStatusTransMap(); + +protected: + virtual ~SyncOperation(); + +private: + DECLARE_OBJECT_TAG(SyncOperation); + + // called by destruction + void Finalize(); + + // Transfer sync mode from interface to inner + void TransferQuerySyncMode(); + + // The device list + const std::vector devices_; + + // The Syncid + uint32_t syncId_; + + // The sync mode_ see SyncMode + int mode_; + + // The callback caller registered + UserCallback userCallback_; + + // The callback caller registered, when sync timeout, call + OnSyncFinished onFinished_; + + // The callback caller registered, will be called when destruction. + OnSyncFinalize onFinalize_; + + // The device id we sync with + std::map statuses_; + + // Is this operation is a block sync + bool isBlockSync_; + + // Is this operation is an auto sync + bool isAutoSync_; + + // Is this operation has finished + bool isFinished_; + + // Used for block sync + std::unique_ptr semaphore_; + + QuerySyncObject query_; + bool isQuerySync_; + + bool isAutoSubscribe_; + + // record identifier used to call ScheduleQueuedTask in SyncOperation::Finished + std::string identifier_; +}; +} // namespace DistributedDB + +#endif // SYNC_OPERATION_H diff --git a/mock/distributeddb/syncer/src/sync_state_machine.cpp b/mock/distributeddb/syncer/src/sync_state_machine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5fae36822f9bbe4e34720f3a1a3fadebbeab19d2 --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_state_machine.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sync_state_machine.h" + +#include + +#include "log_print.h" +#include "version.h" + +namespace DistributedDB { +SyncStateMachine::SyncStateMachine() + : syncContext_(nullptr), + storageInterface_(nullptr), + communicator_(nullptr), + metadata_(nullptr), + currentState_(0), + watchDogStarted_(false), + currentSyncProctolVersion_(SINGLE_VER_SYNC_PROCTOL_V3), + saveDataNotifyTimerId_(0), + saveDataNotifyCount_(0) +{ +} + +SyncStateMachine::~SyncStateMachine() +{ + syncContext_ = nullptr; + storageInterface_ = nullptr; + watchDogStarted_ = false; + metadata_ = nullptr; + if (communicator_ != nullptr) { + RefObject::DecObjRef(communicator_); + communicator_ = nullptr; + } +} + +int SyncStateMachine::Initialize(ISyncTaskContext *context, ISyncInterface *syncInterface, + std::shared_ptr &metadata, ICommunicator *communicator) +{ + if ((context == nullptr) || (syncInterface == nullptr) || (metadata == nullptr) || (communicator == nullptr)) { + return -E_INVALID_ARGS; + } + syncContext_ = context; + storageInterface_ = syncInterface; + metadata_ = metadata; + RefObject::IncObjRef(communicator); + communicator_ = communicator; + return E_OK; +} + +int SyncStateMachine::StartSync() +{ + int errCode = syncContext_->IncUsedCount(); + if (errCode != E_OK) { + return errCode; + } + std::lock_guard lock(stateMachineLock_); + errCode = StartSyncInner(); + syncContext_->SafeExit(); + return errCode; +} + +int SyncStateMachine::TimeoutCallback(TimerId timerId) +{ + RefObject::AutoLock lock(syncContext_); + if (syncContext_->IsKilled()) { + return -E_OBJ_IS_KILLED; + } + TimerId timer = syncContext_->GetTimerId(); + if (timer != timerId) { + return -E_UNEXPECTED_DATA; + } + + int retryTime = syncContext_->GetRetryTime(); + if (retryTime >= syncContext_->GetSyncRetryTimes() || !syncContext_->IsSyncTaskNeedRetry()) { + LOGI("[SyncStateMachine][Timeout] TimeoutCallback retryTime:%d", retryTime); + syncContext_->UnlockObj(); + StepToTimeout(timerId); + syncContext_->LockObj(); + return E_OK; + } + retryTime++; + syncContext_->SetRetryTime(retryTime); + // the sequenceid will be managed by dataSync slide windows. + syncContext_->SetRetryStatus(SyncTaskContext::NEED_RETRY); + int timeoutTime = syncContext_->GetSyncRetryTimeout(retryTime); + syncContext_->ModifyTimer(timeoutTime); + LOGI("[SyncStateMachine][Timeout] Schedule task, timeoutTime = %d, retryTime = %d", timeoutTime, retryTime); + SyncStep(); + return E_OK; +} + +void SyncStateMachine::Abort() +{ + RefObject::IncObjRef(syncContext_); + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this]() { + { + std::lock_guard lock(this->stateMachineLock_); + this->AbortInner(); + StopWatchDog(); + currentState_ = 0; + } + RefObject::DecObjRef(this->syncContext_); + }); + if (errCode != E_OK) { + LOGE("[SyncStateMachine][Abort] Abort failed, errCode %d", errCode); + RefObject::DecObjRef(syncContext_); + } +} + +int SyncStateMachine::SwitchMachineState(uint8_t event) +{ + const std::vector &tables = GetStateSwitchTables(); + auto tableIter = std::find_if(tables.begin(), tables.end(), + [this](const StateSwitchTable &table) { + return table.version <= currentSyncProctolVersion_; + }); + if (tableIter == tables.end()) { + LOGE("[SyncStateMachine][SwitchState] Can't find a compatible version by version %u", + currentSyncProctolVersion_); + return -E_NOT_FOUND; + } + + const std::map &table = (*tableIter).switchTable; + auto eventToStateIter = table.find(currentState_); + if (eventToStateIter == table.end()) { + LOGE("[SyncStateMachine][SwitchState] tableVer:%d, Can't find EventToState with currentSate %u", + (*tableIter).version, currentState_); + SetCurStateErrStatus(); + return E_OK; + } + + const EventToState &eventToState = eventToStateIter->second; + auto stateIter = eventToState.find(event); + if (stateIter == eventToState.end()) { + LOGD("[SyncStateMachine][SwitchState] tableVer:%d, Can't find event %u int currentSate %u ignore", + (*tableIter).version, event, currentState_); + return -E_NOT_FOUND; + } + + currentState_ = stateIter->second; + LOGD("[SyncStateMachine][SwitchState] tableVer:%d, from state %u move to state %u with event %u dev %s{private}", + (*tableIter).version, eventToStateIter->first, currentState_, event, syncContext_->GetDeviceId().c_str()); + return E_OK; +} + +void SyncStateMachine::SwitchStateAndStep(uint8_t event) +{ + if (SwitchMachineState(event) == E_OK) { + SyncStepInner(); + } +} + +int SyncStateMachine::ExecNextTask() +{ + while (!syncContext_->IsTargetQueueEmpty()) { + syncContext_->MoveToNextTarget(); + if (syncContext_->IsCurrentSyncTaskCanBeSkipped()) { + syncContext_->SetOperationStatus(SyncOperation::OP_FINISHED_ALL); + continue; + } + int errCode = PrepareNextSyncTask(); + if (errCode != E_OK) { + LOGE("[SyncStateMachine] PrepareSync failed"); + syncContext_->SetOperationStatus(SyncOperation::OP_FAILED); + } + return errCode; + } + // no task left + syncContext_->SetTaskExecStatus(ISyncTaskContext::FINISHED); + syncContext_->Clear(); + LOGD("[SyncStateMachine] All sync task finished!"); + return -E_NO_SYNC_TASK; +} + +int SyncStateMachine::StartWatchDog() +{ + int errCode = syncContext_->StartTimer(); + if (errCode == E_OK) { + watchDogStarted_ = true; + } + return errCode; +} + +int SyncStateMachine::ResetWatchDog() +{ + if (!watchDogStarted_) { + return E_OK; + } + LOGD("[SyncStateMachine][WatchDog] ResetWatchDog."); + syncContext_->StopTimer(); + syncContext_->SetRetryTime(0); + return syncContext_->StartTimer(); +} + +void SyncStateMachine::StopWatchDog() +{ + watchDogStarted_ = false; + LOGD("[SyncStateMachine][WatchDog] StopWatchDog."); + syncContext_->StopTimer(); +} + +bool SyncStateMachine::StartSaveDataNotify(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) +{ + std::lock_guard lockGuard(saveDataNotifyLock_); + if (saveDataNotifyTimerId_ > 0) { + saveDataNotifyCount_ = 0; + LOGW("[SyncStateMachine][SaveDataNotify] timer has been started!"); + return false; + } + + // Incref to make sure context still alive before timer stopped. + RefObject::IncObjRef(syncContext_); + int errCode = RuntimeContext::GetInstance()->SetTimer( + SAVE_DATA_NOTIFY_INTERVAL, + [this, sessionId, sequenceId, inMsgId](TimerId timerId) { + RefObject::IncObjRef(syncContext_); + int ret = RuntimeContext::GetInstance()->ScheduleTask([this, sessionId, sequenceId, inMsgId]() { + DoSaveDataNotify(sessionId, sequenceId, inMsgId); + RefObject::DecObjRef(syncContext_); + }); + if (ret != E_OK) { + LOGE("[SyncStateMachine] [DoSaveDataNotify] ScheduleTask failed errCode %d", ret); + RefObject::DecObjRef(syncContext_); + } + return ret; + }, + [this]() { RefObject::DecObjRef(syncContext_); }, + saveDataNotifyTimerId_); + if (errCode != E_OK) { + LOGW("[SyncStateMachine][SaveDataNotify] start timer failed err %d !", errCode); + return false; + } + return true; +} + +void SyncStateMachine::StopSaveDataNotify() +{ + std::lock_guard lockGuard(saveDataNotifyLock_); + StopSaveDataNotifyNoLock(); +} + +void SyncStateMachine::StopSaveDataNotifyNoLock() +{ + if (saveDataNotifyTimerId_ == 0) { + LOGI("[SyncStateMachine][SaveDataNotify] timer is not started!"); + return; + } + RuntimeContext::GetInstance()->RemoveTimer(saveDataNotifyTimerId_); + saveDataNotifyTimerId_ = 0; + saveDataNotifyCount_ = 0; +} + +bool SyncStateMachine::StartFeedDogForSync(uint32_t time, SyncDirectionFlag flag) +{ + if (flag != SyncDirectionFlag::SEND && flag != SyncDirectionFlag::RECEIVE) { + LOGE("[SyncStateMachine][feedDog] start wrong flag:%d", flag); + return false; + } + + uint8_t cnt = GetFeedDogTimeout(time / SAVE_DATA_NOTIFY_INTERVAL); + LOGI("[SyncStateMachine][feedDog] start cnt:%d, flag:%d", cnt, flag); + + std::lock_guard lockGuard(feedDogLock_[flag]); + watchDogController_[flag].refCount++; + LOGD("af incr refCount = %d", watchDogController_[flag].refCount); + + if (watchDogController_[flag].feedDogTimerId > 0) { + // update the upperLimit, if the new cnt is bigger then last upperLimit + if (cnt > watchDogController_[flag].feedDogUpperLimit) { + LOGD("update feedDogUpperLimit = %d", cnt); + watchDogController_[flag].feedDogUpperLimit = cnt; + } + watchDogController_[flag].feedDogCnt = 0u; + LOGW("[SyncStateMachine][feedDog] timer has been started!, flag:%d", flag); + return false; + } + + // Incref to make sure context still alive before timer stopped. + RefObject::IncObjRef(syncContext_); + watchDogController_[flag].feedDogUpperLimit = cnt; + int errCode = RuntimeContext::GetInstance()->SetTimer( + SAVE_DATA_NOTIFY_INTERVAL, + [this, flag](TimerId timerId) { + RefObject::IncObjRef(syncContext_); + int ret = RuntimeContext::GetInstance()->ScheduleTask([this, flag]() { + DoFeedDogForSync(flag); + RefObject::DecObjRef(syncContext_); + }); + if (ret != E_OK) { + LOGE("[SyncStateMachine] [DoFeedDogForSync] ScheduleTask failed errCode %d", ret); + RefObject::DecObjRef(syncContext_); + } + return ret; + }, + [this]() { RefObject::DecObjRef(syncContext_); }, + watchDogController_[flag].feedDogTimerId); + if (errCode != E_OK) { + LOGW("[SyncStateMachine][feedDog] start timer failed err %d !", errCode); + return false; + } + return true; +} + +uint8_t SyncStateMachine::GetFeedDogTimeout(int timeoutCount) const +{ + if (timeoutCount > UINT8_MAX) { + return UINT8_MAX; + } + return timeoutCount; +} + +void SyncStateMachine::StopFeedDogForSync(SyncDirectionFlag flag) +{ + if (flag != SyncDirectionFlag::SEND && flag != SyncDirectionFlag::RECEIVE) { + LOGE("[SyncStateMachine][feedDog] stop wrong flag:%d", flag); + return; + } + std::lock_guard lockGuard(feedDogLock_[flag]); + StopFeedDogForSyncNoLock(flag); +} + +void SyncStateMachine::StopFeedDogForSyncNoLock(SyncDirectionFlag flag) +{ + if (flag != SyncDirectionFlag::SEND && flag != SyncDirectionFlag::RECEIVE) { + LOGE("[SyncStateMachine][feedDog] stop wrong flag:%d", flag); + return; + } + if (watchDogController_[flag].feedDogTimerId == 0) { + return; + } + LOGI("[SyncStateMachine][feedDog] stop flag:%d", flag); + RuntimeContext::GetInstance()->RemoveTimer(watchDogController_[flag].feedDogTimerId); + watchDogController_[flag].feedDogTimerId = 0; + watchDogController_[flag].feedDogCnt = 0; + watchDogController_[flag].refCount = 0; +} + +void SyncStateMachine::SetCurStateErrStatus() +{ +} + +void SyncStateMachine::DecRefCountOfFeedDogTimer(SyncDirectionFlag flag) +{ + std::lock_guard lockGuard(feedDogLock_[flag]); + if (watchDogController_[flag].feedDogTimerId == 0) { + return; + } + if (--watchDogController_[flag].refCount <= 0) { + LOGD("stop feed dog timer, refcount = %d", watchDogController_[flag].refCount); + StopFeedDogForSyncNoLock(flag); + } + LOGD("af dec refcount = %d", watchDogController_[flag].refCount); +} + +void SyncStateMachine::DoSaveDataNotify(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) +{ + { + std::lock_guard lock(stateMachineLock_); + (void)ResetWatchDog(); + } + std::lock_guard innerLock(saveDataNotifyLock_); + if (saveDataNotifyCount_ >= MAXT_SAVE_DATA_NOTIFY_COUNT) { + StopSaveDataNotifyNoLock(); + return; + } + SendSaveDataNotifyPacket(sessionId, sequenceId, inMsgId); + saveDataNotifyCount_++; + return; +} + +void SyncStateMachine::DoFeedDogForSync(SyncDirectionFlag flag) +{ + { + std::lock_guard lock(stateMachineLock_); + (void)ResetWatchDog(); + } + std::lock_guard innerLock(feedDogLock_[flag]); + if (watchDogController_[flag].feedDogCnt >= watchDogController_[flag].feedDogUpperLimit) { + StopFeedDogForSyncNoLock(flag); + return; + } + watchDogController_[flag].feedDogCnt++; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/sync_state_machine.h b/mock/distributeddb/syncer/src/sync_state_machine.h new file mode 100644 index 0000000000000000000000000000000000000000..1e633b9b625d701c4ac3cb22cdf5aa4f9434a6a5 --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_state_machine.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNC_STATE_MACHINE_H +#define SYNC_STATE_MACHINE_H + +#include + +#include "isync_interface.h" +#include "isync_state_machine.h" + +namespace DistributedDB { +// the 1st uint8_t is event, the 2nd uint8_t is out state +using EventToState = std::map; + +// The StateSwitchTable with the SyncProctolVersion +struct StateSwitchTable { + uint32_t version = 0; + std::map switchTable; // the 1st uint8_t is current state +}; + +struct WatchDogController { + TimerId feedDogTimerId = 0; + uint8_t feedDogCnt = 0; + uint8_t feedDogUpperLimit = 0; + /* this variable will +1 when call StartFeedDogForSync, -1 when recv one ack, + when it become <= 0, we stop the watch dog. */ + int refCount = 0; +}; + +class SyncStateMachine : public ISyncStateMachine { +public: + SyncStateMachine(); + ~SyncStateMachine() override; + + // Init the SingleVerSyncStateMachine + int Initialize(ISyncTaskContext *context, ISyncInterface *syncInterface, std::shared_ptr &metadata, + ICommunicator *communicator) override; + + // start a sync step + int StartSync() override; + + // call when timeout + int TimeoutCallback(TimerId timerId) override; + + // Force stop the state machine + void Abort() override; + + // start a timer to ResetWatchDog when sync data one (key,value) size bigger than mtu + bool StartFeedDogForSync(uint32_t time, SyncDirectionFlag flag) override; + + uint8_t GetFeedDogTimeout(int timeoutCount) const; + + // stop timer to ResetWatchDog when sync data one (key,value) size bigger than mtu + void StopFeedDogForSync(SyncDirectionFlag flag) override; +protected: + + // SyncOperation is timeout, step to timeout state + virtual void StepToTimeout(TimerId timerId) = 0; + + // Step the SingleVerSyncStateMachine + virtual void SyncStep() = 0; + + // Called by SyncStep, Sub class should realize this function to do machine step + virtual void SyncStepInnerLocked() = 0; + + // Do state machine step with no lock, for inner use + virtual void SyncStepInner() = 0; + + // Called by StartSync, Sub class should realize this function to start statemachine + virtual int StartSyncInner() = 0; + + // Called by Abort, Sub class should realize this function to force abort statemachine + virtual void AbortInner() = 0; + + // while currentstate could not be found, should called, Sub class should realize this function. + virtual void SetCurStateErrStatus(); + + // Used to get instance class' stateSwitchTables + virtual const std::vector &GetStateSwitchTables() const = 0; + + // Called by ExecNextTask, Sub class should realize this function to do some thing for run next sync task + virtual int PrepareNextSyncTask() = 0; + + // Called by StartSaveDataNotifyTimer, Sub class should realize this function to send a heartbeet packet + virtual void SendSaveDataNotifyPacket(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) = 0; + + // Used to parse state table to switch machine state, this function must be called in stateMachineLock + int SwitchMachineState(uint8_t event); + + // Do state switch with the event, and do syncstep + virtual void SwitchStateAndStep(uint8_t event); + + // To Exec next sync task in context targetQueue + int ExecNextTask(); + + // Start a watchdog used for manual sync, when begin a manual sync + int StartWatchDog(); + + // Reset the watchdog used for manual sync + int ResetWatchDog(); + + // stop a watchdog used for manual sync, call when sync finished, + void StopWatchDog(); + + // Start a timer to send data notify packet to keep remote device not timeout + bool StartSaveDataNotify(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId); + + // Stop send save data notify + void StopSaveDataNotify(); + + // Stop send save data notify without lock + void StopSaveDataNotifyNoLock(); + + // stop a timer to ResetWatchDog when sync data bigger than mtu without lock + void StopFeedDogForSyncNoLock(SyncDirectionFlag flag); + + void DecRefCountOfFeedDogTimer(SyncDirectionFlag flag); + + virtual void DoSaveDataNotify(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId); + + void DoFeedDogForSync(SyncDirectionFlag flag); + + DISABLE_COPY_ASSIGN_MOVE(SyncStateMachine); + + ISyncTaskContext *syncContext_; + ISyncInterface *storageInterface_; + ICommunicator *communicator_; + std::shared_ptr metadata_; + std::mutex stateMachineLock_; + uint8_t currentState_; + bool watchDogStarted_; + uint32_t currentSyncProctolVersion_; + + // For save data notify + static const int SAVE_DATA_NOTIFY_INTERVAL = 2000; // 2s for save data notify + static const int MAXT_SAVE_DATA_NOTIFY_COUNT = 15; // only notify 15 times + static const int SYNC_DIRECTION_NUM = 2; // send receive + std::mutex saveDataNotifyLock_; + TimerId saveDataNotifyTimerId_; + uint8_t saveDataNotifyCount_; + + // used for one (key,value) bigger than mtu size, in this case, send packet need more longger time + std::mutex feedDogLock_[SYNC_DIRECTION_NUM]; + WatchDogController watchDogController_[SYNC_DIRECTION_NUM] = {{0}, {0}}; +}; +} // namespace DistributedDB +#endif // SYNC_STATE_MACHINE_H diff --git a/mock/distributeddb/syncer/src/sync_target.cpp b/mock/distributeddb/syncer/src/sync_target.cpp new file mode 100644 index 0000000000000000000000000000000000000000..68343ba16ecbc5975548b59224b67e92dfb7e7c4 --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_target.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sync_target.h" + +#include "db_errno.h" +#include "sync_operation.h" +#include "log_print.h" + +namespace DistributedDB { +SyncTarget::~SyncTarget() +{ + operation_ = nullptr; +} + +int SyncTarget::GetSyncId() const +{ + if (operation_ == nullptr) { + return 0; + } + return operation_->GetSyncId(); +} + +void SyncTarget::SetTaskType(int taskType) +{ + taskType_ = taskType; +} + +int SyncTarget::GetTaskType() const +{ + return taskType_; +} + +void SyncTarget::SetMode(int mode) +{ + mode_ = mode; +} + +int SyncTarget::GetMode() const +{ + return mode_; +} + +void SyncTarget::SetSyncOperation(SyncOperation *operation) +{ + if ((operation != nullptr) && !operation->IsKilled()) { + operation_ = operation; + mode_ = operation->GetMode(); + taskType_ = REQUEST; + } + operation_ = operation; +} + +void SyncTarget::GetSyncOperation(SyncOperation *&operation) const +{ + if (operation_ == nullptr) { + LOGD("GetSyncOperation is nullptr"); + } + operation = operation_; +} + +bool SyncTarget::IsAutoSync() const +{ + if (operation_ == nullptr) { + return false; + } + return operation_->IsAutoSync(); +} + +uint32_t SyncTarget::GetResponseSessionId() const +{ + return 0; +} +} // namespace DistributedDB + diff --git a/mock/distributeddb/syncer/src/sync_target.h b/mock/distributeddb/syncer/src/sync_target.h new file mode 100644 index 0000000000000000000000000000000000000000..11df7813e03996f62ceb7a890f6b68a5d5d8f41c --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_target.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNC_TARGET_H +#define SYNC_TARGET_H + +#include "isync_target.h" + +namespace DistributedDB { +class SyncTarget : public ISyncTarget { +public: + SyncTarget() : operation_(nullptr), taskType_(0), mode_(0) {}; + ~SyncTarget() override; + + // Get the Sync Id of this task + int GetSyncId() const override; + + // Set the type of this task request or response + void SetTaskType(int taskType) override; + + // Get the type of this task request or response + int GetTaskType() const override; + + // Set the mode of this task request or response + void SetMode(int mode) override; + + // Get the mode of this task request or response + int GetMode() const override; + + // Set a SyncOperation + void SetSyncOperation(SyncOperation *operation) override; + + // Get a SyncOperation + void GetSyncOperation(SyncOperation *&operation) const override; + + // Is this target is an auto sync + bool IsAutoSync() const override; + + uint32_t GetResponseSessionId() const override; + +protected: + SyncOperation *operation_; + int taskType_; // sync task or response task; + int mode_; +}; +} // namespace DistributedDB + +#endif // SYNC_TARGET_H diff --git a/mock/distributeddb/syncer/src/sync_task_context.cpp b/mock/distributeddb/syncer/src/sync_task_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c59ae5aba79a841d8e18f9e709342e88c599195d --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_task_context.cpp @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sync_task_context.h" + +#include +#include + +#include "db_constant.h" +#include "db_dump_helper.h" +#include "db_dfx_adapter.h" +#include "db_errno.h" +#include "hash.h" +#include "isync_state_machine.h" +#include "log_print.h" +#include "time_helper.h" + +namespace DistributedDB { +std::mutex SyncTaskContext::synTaskContextSetLock_; +std::set SyncTaskContext::synTaskContextSet_; + +namespace { + const int NEGOTIATION_LIMIT = 2; +} + +SyncTaskContext::SyncTaskContext() + : syncOperation_(nullptr), + syncId_(0), + mode_(0), + isAutoSync_(false), + status_(0), + taskExecStatus_(0), + syncInterface_(nullptr), + communicator_(nullptr), + stateMachine_(nullptr), + requestSessionId_(0), + lastRequestSessionId_(0), + timeHelper_(nullptr), + remoteSoftwareVersion_(0), + remoteSoftwareVersionId_(0), + isCommNormal_(true), + taskErrCode_(E_OK), + syncTaskRetryStatus_(false), + isSyncRetry_(false), + negotiationCount_(0), + isAutoSubscribe_(false), + isNeedResetAbilitySync_(false) +{ +} + +SyncTaskContext::~SyncTaskContext() +{ + if (stateMachine_ != nullptr) { + delete stateMachine_; + stateMachine_ = nullptr; + } + ClearSyncOperation(); + ClearSyncTarget(); + syncInterface_ = nullptr; + communicator_ = nullptr; +} + +int SyncTaskContext::AddSyncTarget(ISyncTarget *target) +{ + if (target == nullptr) { + return -E_INVALID_ARGS; + } + int targetMode = target->GetMode(); + { + std::lock_guard lock(targetQueueLock_); + if (target->GetTaskType() == ISyncTarget::REQUEST) { + requestTargetQueue_.push_back(target); + } else if (target->GetTaskType() == ISyncTarget::RESPONSE) { + responseTargetQueue_.push_back(target); + } else { + return -E_INVALID_ARGS; + } + } + CancelCurrentSyncRetryIfNeed(targetMode); + if (taskExecStatus_ == RUNNING) { + return E_OK; + } + if (onSyncTaskAdd_) { + RefObject::IncObjRef(this); + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this]() { + onSyncTaskAdd_(); + RefObject::DecObjRef(this); + }); + if (errCode != E_OK) { + RefObject::DecObjRef(this); + } + } + return E_OK; +} + +void SyncTaskContext::SetOperationStatus(int status) +{ + std::lock_guard lock(operationLock_); + if (syncOperation_ == nullptr) { + LOGD("[SyncTaskContext][SetStatus] syncOperation is null"); + return; + } + int finalStatus = status; + + int operationStatus = syncOperation_->GetStatus(deviceId_); + if (status == SyncOperation::OP_SEND_FINISHED && operationStatus == SyncOperation::OP_RECV_FINISHED) { + if (GetTaskErrCode() == -E_EKEYREVOKED) { + finalStatus = SyncOperation::OP_EKEYREVOKED_FAILURE; + } else { + finalStatus = SyncOperation::OP_FINISHED_ALL; + } + } else if (status == SyncOperation::OP_RECV_FINISHED && operationStatus == SyncOperation::OP_SEND_FINISHED) { + if (GetTaskErrCode() == -E_EKEYREVOKED) { + finalStatus = SyncOperation::OP_EKEYREVOKED_FAILURE; + } else { + finalStatus = SyncOperation::OP_FINISHED_ALL; + } + } + syncOperation_->SetStatus(deviceId_, finalStatus); + if (finalStatus >= SyncOperation::OP_FINISHED_ALL) { + SaveLastPushTaskExecStatus(finalStatus); + } + if (syncOperation_->CheckIsAllFinished()) { + syncOperation_->Finished(); + } +} + +void SyncTaskContext::SaveLastPushTaskExecStatus(int finalStatus) +{ + (void)finalStatus; +} + +void SyncTaskContext::Clear() +{ + StopTimer(); + retryTime_ = 0; + sequenceId_ = 1; + syncId_ = 0; + isAutoSync_ = false; + requestSessionId_ = 0; + isNeedRetry_ = NO_NEED_RETRY; + mode_ = SyncModeType::INVALID_MODE; + status_ = SyncOperation::OP_WAITING; + taskErrCode_ = E_OK; + packetId_ = 0; + isAutoSubscribe_ = false; +} + +int SyncTaskContext::RemoveSyncOperation(int syncId) +{ + std::lock_guard lock(targetQueueLock_); + auto iter = std::find_if(requestTargetQueue_.begin(), requestTargetQueue_.end(), + [syncId](const ISyncTarget *target) { + if (target == nullptr) { + return false; + } + return target->GetSyncId() == syncId; + }); + if (iter != requestTargetQueue_.end()) { + if (*iter != nullptr) { + delete *iter; + *iter = nullptr; + } + requestTargetQueue_.erase(iter); + return E_OK; + } + return -E_INVALID_ARGS; +} + +void SyncTaskContext::ClearSyncTarget() +{ + std::lock_guard lock(targetQueueLock_); + for (auto &requestTarget : requestTargetQueue_) { + if (requestTarget != nullptr) { + delete requestTarget; + requestTarget = nullptr; + } + } + requestTargetQueue_.clear(); + + for (auto &responseTarget : responseTargetQueue_) { + if (responseTarget != nullptr) { + delete responseTarget; + responseTarget = nullptr; + } + } + responseTargetQueue_.clear(); +} + +bool SyncTaskContext::IsTargetQueueEmpty() const +{ + std::lock_guard lock(targetQueueLock_); + return requestTargetQueue_.empty() && responseTargetQueue_.empty(); +} + +int SyncTaskContext::GetOperationStatus() const +{ + std::lock_guard lock(operationLock_); + if (syncOperation_ == nullptr) { + return SyncOperation::OP_FINISHED_ALL; + } + return syncOperation_->GetStatus(deviceId_); +} + +void SyncTaskContext::SetMode(int mode) +{ + mode_ = mode; +} + +int SyncTaskContext::GetMode() const +{ + return mode_; +} + +void SyncTaskContext::MoveToNextTarget() +{ + ClearSyncOperation(); + TaskParam param; + // call other system api without lock + param.timeout = communicator_->GetTimeout(deviceId_); + std::lock_guard lock(targetQueueLock_); + while (!requestTargetQueue_.empty() || !responseTargetQueue_.empty()) { + ISyncTarget *tmpTarget = nullptr; + if (!requestTargetQueue_.empty()) { + tmpTarget = requestTargetQueue_.front(); + requestTargetQueue_.pop_front(); + } else { + tmpTarget = responseTargetQueue_.front(); + responseTargetQueue_.pop_front(); + } + if (tmpTarget == nullptr) { + LOGE("[SyncTaskContext][MoveToNextTarget] currentTarget is null skip!"); + continue; + } + SyncOperation *tmpOperation = nullptr; + tmpTarget->GetSyncOperation(tmpOperation); + if ((tmpOperation != nullptr) && tmpOperation->IsKilled()) { + // if killed skip this syncOperation_. + delete tmpTarget; + tmpTarget = nullptr; + continue; + } + CopyTargetData(tmpTarget, param); + delete tmpTarget; + tmpTarget = nullptr; + break; + } +} + +uint32_t SyncTaskContext::GetSyncId() const +{ + return syncId_; +} + +// Get the current task deviceId. +std::string SyncTaskContext::GetDeviceId() const +{ + return deviceId_; +} + +void SyncTaskContext::SetTaskExecStatus(int status) +{ + taskExecStatus_ = status; +} + +int SyncTaskContext::GetTaskExecStatus() const +{ + return taskExecStatus_; +} + +bool SyncTaskContext::IsAutoSync() const +{ + return isAutoSync_; +} + +int SyncTaskContext::StartTimer() +{ + std::lock_guard lockGuard(timerLock_); + if (timerId_ > 0) { + return -E_UNEXPECTED_DATA; + } + TimerId timerId = 0; + RefObject::IncObjRef(this); + TimerAction timeOutCallback = std::bind(&SyncTaskContext::TimeOut, this, std::placeholders::_1); + int errCode = RuntimeContext::GetInstance()->SetTimer(timeout_, timeOutCallback, + [this]() { + int ret = RuntimeContext::GetInstance()->ScheduleTask([this](){ RefObject::DecObjRef(this); }); + if (ret != E_OK) { + LOGE("[SyncTaskContext] timer finalizer ScheduleTask, errCode %d", ret); + } + }, timerId); + if (errCode != E_OK) { + RefObject::DecObjRef(this); + return errCode; + } + timerId_ = timerId; + return errCode; +} + +void SyncTaskContext::StopTimer() +{ + TimerId timerId; + { + std::lock_guard lockGuard(timerLock_); + timerId = timerId_; + if (timerId_ == 0) { + return; + } + timerId_ = 0; + } + RuntimeContext::GetInstance()->RemoveTimer(timerId); +} + +int SyncTaskContext::ModifyTimer(int milliSeconds) +{ + std::lock_guard lockGuard(timerLock_); + if (timerId_ == 0) { + return -E_UNEXPECTED_DATA; + } + return RuntimeContext::GetInstance()->ModifyTimer(timerId_, milliSeconds); +} + +void SyncTaskContext::SetRetryTime(int retryTime) +{ + retryTime_ = retryTime; +} + +int SyncTaskContext::GetRetryTime() const +{ + return retryTime_; +} + +void SyncTaskContext::SetRetryStatus(int isNeedRetry) +{ + isNeedRetry_ = isNeedRetry; +} + +int SyncTaskContext::GetRetryStatus() const +{ + return isNeedRetry_; +} + +TimerId SyncTaskContext::GetTimerId() const +{ + return timerId_; +} + +uint32_t SyncTaskContext::GetRequestSessionId() const +{ + return requestSessionId_; +} + +void SyncTaskContext::IncSequenceId() +{ + sequenceId_++; +} + +uint32_t SyncTaskContext::GetSequenceId() const +{ + return sequenceId_; +} + +void SyncTaskContext::ReSetSequenceId() +{ + sequenceId_ = 1; +} + +void SyncTaskContext::IncPacketId() +{ + packetId_++; +} + +uint64_t SyncTaskContext::GetPacketId() const +{ + return packetId_; +} + +int SyncTaskContext::GetTimeoutTime() const +{ + return timeout_; +} + +void SyncTaskContext::SetTimeoutCallback(const TimerAction &timeOutCallback) +{ + timeOutCallback_ = timeOutCallback; +} + +void SyncTaskContext::SetTimeOffset(TimeOffset offset) +{ + timeOffset_ = offset; +} + +TimeOffset SyncTaskContext::GetTimeOffset() const +{ + return timeOffset_; +} + +int SyncTaskContext::StartStateMachine() +{ + return stateMachine_->StartSync(); +} + +int SyncTaskContext::ReceiveMessageCallback(Message *inMsg) +{ + int errCode = E_OK; + if (IncUsedCount() == E_OK) { + errCode = stateMachine_->ReceiveMessageCallback(inMsg); + SafeExit(); + } + return errCode; +} + +void SyncTaskContext::RegOnSyncTask(const std::function &callback) +{ + onSyncTaskAdd_ = callback; +} + +int SyncTaskContext::IncUsedCount() +{ + AutoLock lock(this); + if (IsKilled()) { + LOGI("[SyncTaskContext] IncUsedCount isKilled"); + return -E_OBJ_IS_KILLED; + } + usedCount_++; + return E_OK; +} + +void SyncTaskContext::SafeExit() +{ + AutoLock lock(this); + usedCount_--; + if (usedCount_ < 1) { + safeKill_.notify_one(); + } +} + +Timestamp SyncTaskContext::GetCurrentLocalTime() const +{ + if (timeHelper_ == nullptr) { + return TimeHelper::INVALID_TIMESTAMP; + } + return timeHelper_->GetTime(); +} + +void SyncTaskContext::Abort(int status) +{ + (void)status; + Clear(); +} + +void SyncTaskContext::CommErrHandlerFunc(int errCode, ISyncTaskContext *context, int32_t sessionId) +{ + { + std::lock_guard lock(synTaskContextSetLock_); + if (synTaskContextSet_.count(context) == 0) { + LOGI("[SyncTaskContext][CommErrHandle] context has been killed"); + return; + } + + // IncObjRef to maker sure context not been killed. after the lock_guard + RefObject::IncObjRef(context); + } + + static_cast(context)->CommErrHandlerFuncInner(errCode, static_cast(sessionId)); + RefObject::DecObjRef(context); +} + +void SyncTaskContext::SetRemoteSoftwareVersion(uint32_t version) +{ + std::lock_guard lock(remoteSoftwareVersionLock_); + remoteSoftwareVersion_ = version; + remoteSoftwareVersionId_++; +} + +uint32_t SyncTaskContext::GetRemoteSoftwareVersion() const +{ + std::lock_guard lock(remoteSoftwareVersionLock_); + return remoteSoftwareVersion_; +} + +uint64_t SyncTaskContext::GetRemoteSoftwareVersionId() const +{ + std::lock_guard lock(remoteSoftwareVersionLock_); + return remoteSoftwareVersionId_; +} + +bool SyncTaskContext::IsCommNormal() const +{ + return isCommNormal_; +} + +void SyncTaskContext::CommErrHandlerFuncInner(int errCode, uint32_t sessionId) +{ + { + RefObject::AutoLock lock(this); + if (sessionId != requestSessionId_) { + return; + } + + if (errCode == E_OK) { + // when communicator sent message failed, the state machine will get the error and exit this sync task + // it seems unnecessary to change isCommNormal_ value, so just return here + return; + } + + isCommNormal_ = false; + } + LOGE("[SyncTaskContext][CommErr] errCode %d", errCode); + stateMachine_->CommErrAbort(); +} + +int SyncTaskContext::TimeOut(TimerId id) +{ + int errCode = E_OK; + if (!timeOutCallback_) { + return errCode; + } + if (IncUsedCount() == E_OK) { + errCode = timeOutCallback_(id); + SafeExit(); + } + return errCode; +} + +void SyncTaskContext::CopyTargetData(const ISyncTarget *target, const TaskParam &taskParam) +{ + mode_ = target->GetMode(); + status_ = SyncOperation::OP_SYNCING; + isNeedRetry_ = SyncTaskContext::NO_NEED_RETRY; + taskErrCode_ = E_OK; + packetId_ = 0; + isCommNormal_ = true; // reset comm status here + syncTaskRetryStatus_ = isSyncRetry_; + timeout_ = static_cast(taskParam.timeout); + negotiationCount_ = 0; + target->GetSyncOperation(syncOperation_); + ReSetSequenceId(); + + if (syncOperation_ != nullptr) { + // IncRef for syncOperation_ to make sure syncOperation_ is valid, when setStatus + RefObject::IncObjRef(syncOperation_); + syncId_ = syncOperation_->GetSyncId(); + isAutoSync_ = syncOperation_->IsAutoSync(); + isAutoSubscribe_ = syncOperation_->IsAutoControlCmd(); + if (isAutoSync_ || mode_ == SUBSCRIBE_QUERY || mode_ == UNSUBSCRIBE_QUERY) { + syncTaskRetryStatus_ = true; + } + requestSessionId_ = Hash::Hash32Func(deviceId_ + std::to_string(syncId_) + + std::to_string(TimeHelper::GetSysCurrentTime())); + if (lastRequestSessionId_ == requestSessionId_) { + // Hash32Func max is 0x7fffffff and UINT32_MAX is 0xffffffff + requestSessionId_++; + LOGI("last sessionId is equal to this sessionId!"); + } + LOGI("[SyncTaskContext][copyTarget] mode=%d,syncId=%d,isAutoSync=%d,isRetry=%d,dev=%s{private}", + mode_, syncId_, isAutoSync_, syncTaskRetryStatus_, deviceId_.c_str()); + lastRequestSessionId_ = requestSessionId_; + DBDfxAdapter::StartAsyncTrace(syncActionName_, static_cast(syncId_)); + } else { + isAutoSync_ = false; + LOGI("[SyncTaskContext][copyTarget] for response data dev %s{private},isRetry=%d", deviceId_.c_str(), + syncTaskRetryStatus_); + } +} + +void SyncTaskContext::KillWait() +{ + StopTimer(); + stateMachine_->Abort(); + LOGW("[SyncTaskContext] Try to kill a context, now wait."); + bool noDeadLock = WaitLockedUntil( + safeKill_, + [this]() { + if (usedCount_ < 1) { + return true; + } + return false; + }, + KILL_WAIT_SECONDS); + if (!noDeadLock) { + LOGE("[SyncTaskContext] Dead lock may happen, we stop waiting the task exit."); + } else { + LOGW("[SyncTaskContext] Wait the task exit ok."); + } + std::lock_guard lock(synTaskContextSetLock_); + synTaskContextSet_.erase(this); +} + +void SyncTaskContext::ClearSyncOperation() +{ + std::lock_guard lock(operationLock_); + if (syncOperation_ != nullptr) { + DBDfxAdapter::FinishAsyncTrace(syncActionName_, static_cast(syncId_)); + RefObject::DecObjRef(syncOperation_); + syncOperation_ = nullptr; + } +} + +void SyncTaskContext::CancelCurrentSyncRetryIfNeed(int newTargetMode) +{ + AutoLock(this); + if (!isAutoSync_) { + return; + } + int mode = SyncOperation::TransferSyncMode(newTargetMode); + if (newTargetMode == mode_ || mode == SyncModeType::PUSH_AND_PULL) { + SetRetryTime(AUTO_RETRY_TIMES); + ModifyTimer(timeout_); + } +} + +int SyncTaskContext::GetTaskErrCode() const +{ + return taskErrCode_; +} + +void SyncTaskContext::SetTaskErrCode(int errCode) +{ + taskErrCode_ = errCode; +} + +bool SyncTaskContext::IsSyncTaskNeedRetry() const +{ + return syncTaskRetryStatus_; +} + +void SyncTaskContext::SetSyncRetry(bool isRetry) +{ + isSyncRetry_ = isRetry; +} + +int SyncTaskContext::GetSyncRetryTimes() const +{ + if (IsAutoSync() || mode_ == SUBSCRIBE_QUERY || mode_ == UNSUBSCRIBE_QUERY) { + return AUTO_RETRY_TIMES; + } + return MANUAL_RETRY_TIMES; +} + +int SyncTaskContext::GetSyncRetryTimeout(int retryTime) const +{ + int timeoutTime = GetTimeoutTime(); + if (IsAutoSync()) { + // set the new timeout value with 2 raised to the power of retryTime. + return timeoutTime * static_cast(pow(2, retryTime)); + } + return timeoutTime; +} + +void SyncTaskContext::ClearAllSyncTask() +{ +} + +bool SyncTaskContext::IsAutoLiftWaterMark() const +{ + return negotiationCount_ < NEGOTIATION_LIMIT; +} + +void SyncTaskContext::IncNegotiationCount() +{ + negotiationCount_++; +} + +bool SyncTaskContext::IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query) +{ + return stateMachine_->IsNeedTriggerQueryAutoSync(inMsg, query); +} + +bool SyncTaskContext::IsAutoSubscribe() const +{ + return isAutoSubscribe_; +} + +bool SyncTaskContext::GetIsNeedResetAbilitySync() const +{ + return isNeedResetAbilitySync_; +} + +void SyncTaskContext::SetIsNeedResetAbilitySync(bool isNeedReset) +{ + isNeedResetAbilitySync_ = isNeedReset; +} + +bool SyncTaskContext::IsCurrentSyncTaskCanBeSkipped() const +{ + return false; +} + +void SyncTaskContext::ResetLastPushTaskStatus() +{ +} + +void SyncTaskContext::SchemaChange() +{ + SetIsNeedResetAbilitySync(true); +} + +void SyncTaskContext::Dump(int fd) +{ + size_t totalSyncTaskCount = 0u; + size_t autoSyncTaskCount = 0u; + size_t reponseTaskCount = 0u; + { + std::lock_guard lock(targetQueueLock_); + totalSyncTaskCount = requestTargetQueue_.size() + responseTargetQueue_.size(); + for (const auto &target : requestTargetQueue_) { + if (target->IsAutoSync()) { + autoSyncTaskCount++; + } + } + reponseTaskCount = responseTargetQueue_.size(); + } + DBDumpHelper::Dump(fd, "\t\ttarget = %s, total sync task count = %zu, auto sync task count = %zu," + " response task count = %zu\n", + deviceId_.c_str(), totalSyncTaskCount, autoSyncTaskCount, reponseTaskCount); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/sync_task_context.h b/mock/distributeddb/syncer/src/sync_task_context.h new file mode 100644 index 0000000000000000000000000000000000000000..c7fffc87dff9635b0203fa53165fac90d7848803 --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_task_context.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNC_TASK_CONTEXT_H +#define SYNC_TASK_CONTEXT_H + +#include +#include + +#include "icommunicator.h" +#include "ikvdb_sync_interface.h" +#include "isync_task_context.h" +#include "meta_data.h" +#include "runtime_context.h" +#include "semaphore_utils.h" +#include "sync_operation.h" +#include "sync_target.h" +#include "time_helper.h" + +namespace DistributedDB { +enum SyncDirectionFlag { + SEND = 0, + RECEIVE = 1, +}; +struct TaskParam { + uint32_t timeout = 0; +}; +class ISyncStateMachine; + +class SyncTaskContext : public ISyncTaskContext { +public: + SyncTaskContext(); + + // Add a sync task target to the queue + int AddSyncTarget(ISyncTarget *target) override; + + // Set the status of this task + void SetOperationStatus(int status) override; + + // Clear context data + void Clear() override; + + // remove a sync target by syncId + int RemoveSyncOperation(int syncId) override; + + // If the requestTargetQueue is empty + bool IsTargetQueueEmpty() const override; + + // Get the status of this task + int GetOperationStatus() const override; + + // Set the mode of this task + void SetMode(int mode) override; + + // Get the mode of this task + int GetMode() const override; + + // Move to next target to sync + void MoveToNextTarget() override; + + // Get the current task syncId + uint32_t GetSyncId() const override; + + // Get the current task deviceId. + std::string GetDeviceId() const override; + + // Set the sync task queue exec status + void SetTaskExecStatus(int status) override; + + // Get the sync task queue exec status + int GetTaskExecStatus() const override; + + // Return if now is doing auto sync + bool IsAutoSync() const override; + + // Set a Timer used for timeout + int StartTimer() override; + + // delete timer + void StopTimer() override; + + // modify timer + int ModifyTimer(int milliSeconds) override; + + // Set a RetryTime for the sync task + void SetRetryTime(int retryTime) override; + + // Get a RetryTime for the sync task + int GetRetryTime() const override; + + // Set Retry status for the sync task + void SetRetryStatus(int isNeedRetry) override; + + // Get Retry status for the sync task + int GetRetryStatus() const override; + + TimerId GetTimerId() const override; + + // Inc the current message sequenceId + void IncSequenceId() override; + + // Get the current initiactive sync session id + uint32_t GetRequestSessionId() const override; + + // Get the current message sequence id + uint32_t GetSequenceId() const override; + + void ReSetSequenceId() override; + + void IncPacketId(); + + uint64_t GetPacketId() const; + + // Get the current watch timeout time + int GetTimeoutTime() const override; + + void SetTimeoutCallback(const TimerAction &timeOutCallback) override; + + // Start the sync state machine + int StartStateMachine() override; + + // Set the timeoffset with the remote device + void SetTimeOffset(TimeOffset offset) override; + + // Get the timeoffset with the remote device + TimeOffset GetTimeOffset() const override; + + // Used for sync message callback + int ReceiveMessageCallback(Message *inMsg) override; + + // used to register a callback, called when new SyncTarget added + void RegOnSyncTask(const std::function &callback) override; + + // When schedule a new task, should call this function to inc usedcount + int IncUsedCount() override; + + // When schedule task exit, should call this function to dec usedcount + void SafeExit() override; + + // Get current local time from TimeHelper + Timestamp GetCurrentLocalTime() const override; + + // Set the remount software version num + void SetRemoteSoftwareVersion(uint32_t version) override; + + // Get the remount software version num + uint32_t GetRemoteSoftwareVersion() const override; + + // Get the remount software version id, when called GetRemoteSoftwareVersion this id will be increase. + // Used to check if the version num is is overdue + uint64_t GetRemoteSoftwareVersionId() const override; + + // Judge if the communicator is normal + bool IsCommNormal() const override; + + // If ability sync request set version, need call this function. + // Should be called with ObjLock + virtual void Abort(int status); + + // Used in send msg, as execution is asynchronous, should use this function to handle result. + static void CommErrHandlerFunc(int errCode, ISyncTaskContext *context, int32_t sessionId); + + int GetTaskErrCode() const override; + + void SetTaskErrCode(int errCode) override; + + bool IsSyncTaskNeedRetry() const override; + + void SetSyncRetry(bool isRetry) override; + + int GetSyncRetryTimes() const override; + + int GetSyncRetryTimeout(int retryTime) const override; + + void ClearAllSyncTask() override; + + bool IsAutoLiftWaterMark() const override; + + void IncNegotiationCount() override; + + // check if need trigger query auto sync and get query from inMsg + bool IsNeedTriggerQueryAutoSync(Message *inMsg, QuerySyncObject &query) override; + + bool IsAutoSubscribe() const override; + + bool IsCurrentSyncTaskCanBeSkipped() const override; + + virtual void ResetLastPushTaskStatus(); + + bool GetIsNeedResetAbilitySync() const; + + void SetIsNeedResetAbilitySync(bool isNeedReset) override; + + void SchemaChange() override; + + void Dump(int fd) override; + +protected: + const static int KILL_WAIT_SECONDS = INT32_MAX; + + ~SyncTaskContext() override; + + virtual int TimeOut(TimerId id); + + virtual void CopyTargetData(const ISyncTarget *target, const TaskParam &taskParam); + + void CommErrHandlerFuncInner(int errCode, uint32_t sessionId); + + void KillWait(); + + void ClearSyncOperation(); + + void ClearSyncTarget(); + + void CancelCurrentSyncRetryIfNeed(int newTargetMode); + + virtual void SaveLastPushTaskExecStatus(int finalStatus); + + mutable std::mutex targetQueueLock_; + std::list requestTargetQueue_; + std::list responseTargetQueue_; + SyncOperation *syncOperation_; + mutable std::mutex operationLock_; + uint32_t syncId_; + int mode_; + bool isAutoSync_; + int status_; + int taskExecStatus_; + std::string deviceId_; + std::string syncActionName_; + ISyncInterface *syncInterface_; + ICommunicator *communicator_; + ISyncStateMachine *stateMachine_; + TimeOffset timeOffset_ = 0; + int retryTime_ = 0; + int isNeedRetry_ = SyncTaskContext::NO_NEED_RETRY; + uint32_t requestSessionId_ = 0; + uint32_t lastRequestSessionId_ = 0; + uint32_t sequenceId_ = 1; + std::function onSyncTaskAdd_; + + // for safe exit + std::condition_variable safeKill_; + int usedCount_ = 0; + + // for timeout callback + std::mutex timerLock_; + TimerId timerId_ = 0; + int timeout_ = 1000; // 1000ms + TimerAction timeOutCallback_; + std::unique_ptr timeHelper_; + + // for version sync + mutable std::mutex remoteSoftwareVersionLock_; + uint32_t remoteSoftwareVersion_; + uint64_t remoteSoftwareVersionId_; // Check if the remoteSoftwareVersion_ is is overdue + + bool isCommNormal_; + int taskErrCode_; + uint64_t packetId_ = 0; // used for assignment to reSendMap_.ReSendInfo.packetId in 103 version or above + bool syncTaskRetryStatus_; + bool isSyncRetry_; + uint32_t negotiationCount_; + bool isAutoSubscribe_; + // syncFinished_ need to set false if isNeedResetSyncFinished_ is true when start do abilitySync interface + std::atomic isNeedResetAbilitySync_; + + // For global ISyncTaskContext Set, used by CommErrCallback. + static std::mutex synTaskContextSetLock_; + static std::set synTaskContextSet_; +}; +} // namespace DistributedDB + +#endif // SYNC_TASK_CONTEXT_H diff --git a/mock/distributeddb/syncer/src/sync_types.h b/mock/distributeddb/syncer/src/sync_types.h new file mode 100644 index 0000000000000000000000000000000000000000..3af5663de665b1c554c9e938a222068acdac03bf --- /dev/null +++ b/mock/distributeddb/syncer/src/sync_types.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNC_TYPES_H +#define SYNC_TYPES_H + +#include +#include "query_sync_object.h" +#include "sync_config.h" + +namespace DistributedDB { +enum MessageId { + TIME_SYNC_MESSAGE = 1, + DATA_SYNC_MESSAGE, + COMMIT_HISTORY_SYNC_MESSAGE, + MULTI_VER_DATA_SYNC_MESSAGE, + VALUE_SLICE_SYNC_MESSAGE, + LOCAL_DATA_CHANGED, + ABILITY_SYNC_MESSAGE, + QUERY_SYNC_MESSAGE, + CONTROL_SYNC_MESSAGE, + UNKNOW_MESSAGE, +}; + +enum SyncModeType { + PUSH, + PULL, + PUSH_AND_PULL, + AUTO_PUSH, + AUTO_PULL, + RESPONSE_PULL, + QUERY_PUSH, + QUERY_PULL, + QUERY_PUSH_PULL, + SUBSCRIBE_QUERY, + UNSUBSCRIBE_QUERY, + AUTO_SUBSCRIBE_QUERY, + INVALID_MODE +}; + +enum class SyncType { + MANUAL_FULL_SYNC_TYPE = 1, + AUTO_SYNC_TYPE, + QUERY_SYNC_TYPE, + INVALID_SYNC_TYPE, +}; + +enum ControlCmdType { + SUBSCRIBE_QUERY_CMD, + UNSUBSCRIBE_QUERY_CMD, + INVALID_CONTROL_CMD, +}; + +struct UpdateWaterMark { + bool normalUpdateMark = false; + bool deleteUpdateMark = false; +}; + +struct InternalSyncParma { + std::vector devices; + int mode = 0; + bool isQuerySync = false; + QuerySyncObject syncQuery; +}; + +constexpr int NOT_SURPPORT_SEC_CLASSIFICATION = 0xff; +constexpr uint8_t QUERY_SYNC_MODE_BASE = SyncModeType::QUERY_PUSH; +constexpr int AUTO_RETRY_TIMES = 3; +constexpr int MANUAL_RETRY_TIMES = 1; +constexpr int TIME_SYNC_WAIT_TIME = 5000; // 5s +constexpr uint64_t MAX_PACKETID = 10000000000; // max packetId +constexpr int NOTIFY_MIN_MTU_SIZE = 30 * 1024; // 30k + +constexpr int MAX_SUBSCRIBE_NUM_PER_DEV = 4; +constexpr int MAX_SUBSCRIBE_NUM_PER_DB = 8; +constexpr size_t MAX_DEVICES_NUM = 32; + +// index 0 for packetId in data request +// if ack reserve size is 1, reserve is {localWaterMark} +// if ack reserve size is above 2, reserve is {localWaterMark, packetId, deletedWaterMark...} +constexpr uint32_t REQUEST_PACKET_RESERVED_INDEX_PACKETID = 0; +constexpr uint32_t ACK_PACKET_RESERVED_INDEX_PACKETID = 1; // index 1 for packetId +constexpr uint32_t ACK_PACKET_RESERVED_INDEX_LOCAL_WATER_MARK = 0; // index 0 for localWaterMark +constexpr uint32_t ACK_PACKET_RESERVED_INDEX_DELETE_WATER_MARK = 2; // index 2 for deleteDataWaterMark +constexpr uint64_t MAX_TIMESTAMP = INT64_MAX; +constexpr uint8_t REMOVE_DEVICE_DATA_MARK = 1; +constexpr uint8_t SUPPORT_MARK = 1; // used for set is support one ability +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/syncer_factory.cpp b/mock/distributeddb/syncer/src/syncer_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b0c8d9d4989db497217b63ce012e8a942a721202 --- /dev/null +++ b/mock/distributeddb/syncer/src/syncer_factory.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "syncer_factory.h" + +#include "ikvdb_sync_interface.h" +#include "multi_ver_syncer.h" +#include "single_ver_kv_syncer.h" +#ifdef RELATIONAL_STORE +#include "single_ver_relational_syncer.h" +#endif + +namespace DistributedDB { +std::shared_ptr SyncerFactory::GetSyncer(int type) +{ + if (type == ISyncInterface::SYNC_SVD) { + std::shared_ptr singleVerSyncer(std::make_shared()); + return singleVerSyncer; +#ifndef OMIT_MULTI_VER + } else if (type == ISyncInterface::SYNC_MVD) { + std::shared_ptr multiVerSyncer(std::make_shared()); + return multiVerSyncer; +#endif +#ifdef RELATIONAL_STORE + } else if (type == ISyncInterface::SYNC_RELATION) { + std::shared_ptr relationalSyncer(std::make_shared()); + return relationalSyncer; +#endif + } else { + return nullptr; + } +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/syncer_factory.h b/mock/distributeddb/syncer/src/syncer_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..20e1c45e2bd086d1750515fe912fb00e85c785e5 --- /dev/null +++ b/mock/distributeddb/syncer/src/syncer_factory.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNCER_FACTORY_H +#define SYNCER_FACTORY_H + +#include + +#include "isyncer.h" + +namespace DistributedDB { +class SyncerFactory final { +public: + // Product a ISyncer for the given type + // type can be : IKvDBSyncInterface::SYNC_SVD + // IKvDBSyncInterface::SYNC_MVD + static std::shared_ptr GetSyncer(int type); +}; +} // namespace DistributedDB + +#endif // SYNCER_FACTORY_H diff --git a/mock/distributeddb/syncer/src/syncer_proxy.cpp b/mock/distributeddb/syncer/src/syncer_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e28076b8746c0bb327e2e39a8bdabca24c8728a --- /dev/null +++ b/mock/distributeddb/syncer/src/syncer_proxy.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "syncer_proxy.h" + +#include "syncer_factory.h" +#include "db_errno.h" +#include "log_print.h" + +namespace DistributedDB { +SyncerProxy::SyncerProxy() + : syncer_(nullptr) +{ +} + +int SyncerProxy::Initialize(ISyncInterface *syncInterface, bool isNeedActive) +{ + if (syncInterface == nullptr) { + return -E_INVALID_ARGS; + } + + int interfaceType = syncInterface->GetInterfaceType(); + { + std::lock_guard lock(syncerLock_); + if (syncer_ == nullptr) { + syncer_ = SyncerFactory::GetSyncer(interfaceType); + } + } + if (syncer_ == nullptr) { + LOGF("syncer create failed! invalid interface type %d", interfaceType); + return -E_OUT_OF_MEMORY; + } + + return syncer_->Initialize(syncInterface, isNeedActive); +} + +int SyncerProxy::Close(bool isClosedOperation) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->Close(isClosedOperation); +} + +int SyncerProxy::Sync(const std::vector &devices, int mode, + const std::function &)> &onComplete, + const std::function &onFinalize, bool wait) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->Sync(devices, mode, onComplete, onFinalize, wait); +} + +int SyncerProxy::Sync(const SyncParma &parma, uint64_t connectionId) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->Sync(parma, connectionId); +} + +int SyncerProxy::RemoveSyncOperation(int syncId) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->RemoveSyncOperation(syncId); +} + +int SyncerProxy::StopSync(uint64_t connectionId) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->StopSync(connectionId); +} + +uint64_t SyncerProxy::GetTimestamp() +{ + if (syncer_ == nullptr) { + return SyncerFactory::GetSyncer(ISyncInterface::SYNC_SVD)->GetTimestamp(); + } + return syncer_->GetTimestamp(); +} + +void SyncerProxy::EnableAutoSync(bool enable) +{ + if (syncer_ == nullptr) { + return; + } + syncer_->EnableAutoSync(enable); +} + +int SyncerProxy::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) +{ + return syncer_->EraseDeviceWaterMark(deviceId, isNeedHash, ""); +} + +int SyncerProxy::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash, + const std::string &tableName) +{ + if (syncer_ == nullptr) { + LOGE("[SyncerProxy] Syncer no init, unknown rule to erase waterMark!"); + return -E_NOT_INIT; + } + return syncer_->EraseDeviceWaterMark(deviceId, isNeedHash, tableName); +} + +void SyncerProxy::LocalDataChanged(int notifyEvent) +{ + if (syncer_ == nullptr) { + return; + } + syncer_->LocalDataChanged(notifyEvent); +} + +int SyncerProxy::GetQueuedSyncSize(int *queuedSyncSize) const +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->GetQueuedSyncSize(queuedSyncSize); +} + +int SyncerProxy::SetQueuedSyncLimit(const int *queuedSyncLimit) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->SetQueuedSyncLimit(queuedSyncLimit); +} + +int SyncerProxy::GetQueuedSyncLimit(int *queuedSyncLimit) const +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->GetQueuedSyncLimit(queuedSyncLimit); +} + +int SyncerProxy::DisableManualSync(void) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->DisableManualSync(); +} + +int SyncerProxy::EnableManualSync(void) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->EnableManualSync(); +} + +int SyncerProxy::GetLocalIdentity(std::string &outTarget) const +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->GetLocalIdentity(outTarget); +} + +int SyncerProxy::SetStaleDataWipePolicy(WipePolicy policy) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->SetStaleDataWipePolicy(policy); +} + +int SyncerProxy::SetSyncRetry(bool isRetry) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->SetSyncRetry(isRetry); +} + +int SyncerProxy::SetEqualIdentifier(const std::string &identifier, const std::vector &targets) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->SetEqualIdentifier(identifier, targets); +} + +void SyncerProxy::Dump(int fd) +{ + if (syncer_ == nullptr) { + return; + } + return syncer_->Dump(fd); +} + +SyncerBasicInfo SyncerProxy::DumpSyncerBasicInfo() +{ + if (syncer_ == nullptr) { + return SyncerBasicInfo { false, false, false }; + } + return syncer_->DumpSyncerBasicInfo(); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/time_helper.cpp b/mock/distributeddb/syncer/src/time_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ea1f4f225cc7907347ebaa37ed407096b18254f --- /dev/null +++ b/mock/distributeddb/syncer/src/time_helper.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "time_helper.h" + +#include "db_errno.h" +#include "log_print.h" +#include "platform_specific.h" + +namespace DistributedDB { +std::mutex TimeHelper::systemTimeLock_; +Timestamp TimeHelper::lastSystemTimeUs_ = 0; +Timestamp TimeHelper::currentIncCount_ = 0; + +Timestamp TimeHelper::GetSysCurrentTime() +{ + uint64_t curTime = 0; + std::lock_guard lock(systemTimeLock_); + int errCode = OS::GetCurrentSysTimeInMicrosecond(curTime); + if (errCode != E_OK) { + return INVALID_TIMESTAMP; + } + + // If GetSysCurrentTime in 1us, we need increase the currentIncCount_ + if (curTime == lastSystemTimeUs_) { + // if the currentIncCount_ has been increased MAX_INC_COUNT, keep the currentIncCount_ + if (currentIncCount_ < MAX_INC_COUNT) { + currentIncCount_++; + } + } else { + lastSystemTimeUs_ = curTime; + currentIncCount_ = 0; + } + return (curTime * TO_100_NS) + currentIncCount_; // Currently Timestamp is uint64_t +} + +TimeHelper::TimeHelper() + : storage_(nullptr), + metadata_(nullptr) +{ +} + +TimeHelper::~TimeHelper() +{ + metadata_ = nullptr; + storage_ = nullptr; +} + +int TimeHelper::Initialize(const ISyncInterface *inStorage, std::shared_ptr &inMetadata) +{ + if ((inStorage == nullptr) || (inMetadata == nullptr)) { + return -E_INVALID_ARGS; + } + metadata_ = inMetadata; + storage_ = inStorage; + Timestamp currentSysTime = GetSysCurrentTime(); + TimeOffset localTimeOffset = GetLocalTimeOffset(); + Timestamp maxItemTime = GetMaxDataItemTime(); + if (currentSysTime > MAX_VALID_TIME || localTimeOffset > MAX_VALID_TIME || maxItemTime > MAX_VALID_TIME) { + return -E_INVALID_TIME; + } + if ((currentSysTime + localTimeOffset) <= maxItemTime) { + localTimeOffset = static_cast(maxItemTime - currentSysTime + MS_TO_100_NS); // 1ms + int errCode = SaveLocalTimeOffset(localTimeOffset); + if (errCode != E_OK) { + LOGE("[TimeHelper] save local time offset failed,err=%d", errCode); + return errCode; + } + } + metadata_->SetLastLocalTime(currentSysTime + static_cast(localTimeOffset)); + return E_OK; +} + +Timestamp TimeHelper::GetTime() +{ + Timestamp currentSysTime = GetSysCurrentTime(); + TimeOffset localTimeOffset = GetLocalTimeOffset(); + Timestamp currentLocalTime = currentSysTime + localTimeOffset; + Timestamp lastLocalTime = metadata_->GetLastLocalTime(); + if (currentLocalTime <= lastLocalTime || currentLocalTime > MAX_VALID_TIME) { + lastLocalTime++; + currentLocalTime = lastLocalTime; + metadata_->SetLastLocalTime(lastLocalTime); + } else { + metadata_->SetLastLocalTime(currentLocalTime); + } + return currentLocalTime; +} + +Timestamp TimeHelper::GetMaxDataItemTime() +{ + Timestamp timestamp = 0; + storage_->GetMaxTimestamp(timestamp); + return timestamp; +} + +TimeOffset TimeHelper::GetLocalTimeOffset() const +{ + return metadata_->GetLocalTimeOffset(); +} + +int TimeHelper::SaveLocalTimeOffset(TimeOffset offset) +{ + return metadata_->SaveLocalTimeOffset(offset); +} + +void TimeHelper::SetSendConfig(const std::string &dstTarget, bool nonBlock, uint32_t timeout, SendConfig &sendConf) +{ + SetSendConfigParam(storage_->GetDbProperties(), dstTarget, false, SEND_TIME_OUT, sendConf); +} +} // namespace DistributedDB diff --git a/mock/distributeddb/syncer/src/time_helper.h b/mock/distributeddb/syncer/src/time_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..d19d00bd8ab8b5b60bf89d3ad7577c910e8f822a --- /dev/null +++ b/mock/distributeddb/syncer/src/time_helper.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TIME_HELPER_H +#define TIME_HELPER_H + +#include + +#include "icommunicator.h" +#include "meta_data.h" +#include "runtime_context.h" + +namespace DistributedDB { +class TimeHelper { +public: + constexpr static int64_t BASE_OFFSET = 10000LL * 365LL * 24LL * 3600LL * 1000LL * 1000LL * 10L; // 10000 year 100ns + + constexpr static int64_t MAX_VALID_TIME = BASE_OFFSET * 2; // 20000 year 100ns + + static const uint64_t TO_100_NS = 10; // 1us to 100ns + + static const uint64_t MS_TO_100_NS = 10000; // 1ms to 100ns + + static const Timestamp INVALID_TIMESTAMP = 0; + + // Get current system time + static Timestamp GetSysCurrentTime(); + + TimeHelper(); + ~TimeHelper(); + + // Init the TimeHelper + int Initialize(const ISyncInterface *inStorage, std::shared_ptr &inMetadata); + + // Get Timestamp when write data into db, export interface; + Timestamp GetTime(); + + // Get max data time from db + Timestamp GetMaxDataItemTime(); + + // Get local time offset + TimeOffset GetLocalTimeOffset() const; + + // Get local time + int SaveLocalTimeOffset(TimeOffset offset); + + void SetSendConfig(const std::string &dstTarget, bool nonBlock, uint32_t timeout, SendConfig &sendConf); + +private: + static std::mutex systemTimeLock_; + static Timestamp lastSystemTimeUs_; + static Timestamp currentIncCount_; + static const uint64_t MAX_INC_COUNT = 9; // last bit from 0-9 + const ISyncInterface *storage_; + std::shared_ptr metadata_; +}; +} // namespace DistributedDB + +#endif // TIME_HELPER_H diff --git a/mock/distributeddb/syncer/src/time_sync.cpp b/mock/distributeddb/syncer/src/time_sync.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a7a40178829f2e9d54aa8dae3f67f01b872606e8 --- /dev/null +++ b/mock/distributeddb/syncer/src/time_sync.cpp @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "time_sync.h" + +#include "parcel.h" +#include "log_print.h" +#include "sync_types.h" +#include "message_transform.h" +#include "version.h" +#include "isync_task_context.h" + +namespace DistributedDB { +std::mutex TimeSync::timeSyncSetLock_; +std::set TimeSync::timeSyncSet_; +namespace { + constexpr uint64_t TIME_SYNC_INTERVAL = 24 * 60 * 60 * 1000; // 24h + constexpr int TRIP_DIV_HALF = 2; + constexpr int64_t MAX_TIME_OFFSET_NOISE = 1 * 1000 * 10000; // 1s for 100ns +} + +// Class TimeSyncPacket +TimeSyncPacket::TimeSyncPacket() + : sourceTimeBegin_(0), + sourceTimeEnd_(0), + targetTimeBegin_(0), + targetTimeEnd_(0), + version_(TIME_SYNC_VERSION_V1) +{ +} + +TimeSyncPacket::~TimeSyncPacket() +{ +} + +void TimeSyncPacket::SetSourceTimeBegin(Timestamp sourceTimeBegin) +{ + sourceTimeBegin_ = sourceTimeBegin; +} + +Timestamp TimeSyncPacket::GetSourceTimeBegin() const +{ + return sourceTimeBegin_; +} + +void TimeSyncPacket::SetSourceTimeEnd(Timestamp sourceTimeEnd) +{ + sourceTimeEnd_ = sourceTimeEnd; +} + +Timestamp TimeSyncPacket::GetSourceTimeEnd() const +{ + return sourceTimeEnd_; +} + +void TimeSyncPacket::SetTargetTimeBegin(Timestamp targetTimeBegin) +{ + targetTimeBegin_ = targetTimeBegin; +} + +Timestamp TimeSyncPacket::GetTargetTimeBegin() const +{ + return targetTimeBegin_; +} + +void TimeSyncPacket::SetTargetTimeEnd(Timestamp targetTimeEnd) +{ + targetTimeEnd_ = targetTimeEnd; +} + +Timestamp TimeSyncPacket::GetTargetTimeEnd() const +{ + return targetTimeEnd_; +} + +void TimeSyncPacket::SetVersion(uint32_t version) +{ + version_ = version; +} + +uint32_t TimeSyncPacket::GetVersion() const +{ + return version_; +} + +uint32_t TimeSyncPacket::CalculateLen() +{ + uint32_t len = Parcel::GetUInt32Len(); + len += Parcel::GetUInt64Len(); + len += Parcel::GetUInt64Len(); + len += Parcel::GetUInt64Len(); + len += Parcel::GetUInt64Len(); + len = Parcel::GetEightByteAlign(len); + return len; +} + +// Class TimeSync +TimeSync::TimeSync() + : communicateHandle_(nullptr), + metadata_(nullptr), + timeHelper_(nullptr), + retryTime_(0), + driverTimerId_(0), + isSynced_(false), + isAckReceived_(false), + timeChangedListener_(nullptr), + timeDriverLockCount_(0), + isOnline_(true) +{ +} + +TimeSync::~TimeSync() +{ + Finalize(); + driverTimerId_ = 0; + + if (timeChangedListener_ != nullptr) { + timeChangedListener_->Drop(true); + timeChangedListener_ = nullptr; + } + timeHelper_ = nullptr; + communicateHandle_ = nullptr; + metadata_ = nullptr; + + std::lock_guard lock(timeSyncSetLock_); + timeSyncSet_.erase(this); +} + +int TimeSync::RegisterTransformFunc() +{ + TransformFunc func; + func.computeFunc = std::bind(&TimeSync::CalculateLen, std::placeholders::_1); + func.serializeFunc = std::bind(&TimeSync::Serialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + func.deserializeFunc = std::bind(&TimeSync::DeSerialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + return MessageTransform::RegTransformFunction(TIME_SYNC_MESSAGE, func); +} + +int TimeSync::Initialize(ICommunicator *communicator, std::shared_ptr &metadata, + const ISyncInterface *storage, const DeviceID &deviceId) +{ + if ((communicator == nullptr) || (storage == nullptr) || (metadata == nullptr)) { + return -E_INVALID_ARGS; + } + { + std::lock_guard lock(timeSyncSetLock_); + timeSyncSet_.insert(this); + } + communicateHandle_ = communicator; + metadata_ = metadata; + deviceId_ = deviceId; + timeHelper_ = std::make_unique(); + + int errCode = timeHelper_->Initialize(storage, metadata_); + if (errCode != E_OK) { + timeHelper_ = nullptr; + LOGE("[TimeSync] timeHelper Init failed, err %d.", errCode); + return errCode; + } + + driverCallback_ = std::bind(&TimeSync::TimeSyncDriver, this, std::placeholders::_1); + errCode = RuntimeContext::GetInstance()->SetTimer(TIME_SYNC_INTERVAL, driverCallback_, nullptr, driverTimerId_); + if (errCode != E_OK) { + return errCode; + } + return errCode; +} + +void TimeSync::Finalize() +{ + // Stop the timer + LOGD("[TimeSync] Finalize enter!"); + RuntimeContext *runtimeContext = RuntimeContext::GetInstance(); + std::unique_lock lock(timeDriverLock_); + runtimeContext->RemoveTimer(driverTimerId_, true); + timeDriverCond_.wait(lock, [this](){ return this->timeDriverLockCount_ == 0; }); + LOGD("[TimeSync] Finalized!"); +} + +int TimeSync::SyncStart(const CommErrHandler &handler, uint32_t sessionId) +{ + isOnline_ = true; + TimeSyncPacket packet; + Timestamp startTime = timeHelper_->GetTime(); + packet.SetSourceTimeBegin(startTime); + // send timeSync request + LOGD("[TimeSync] startTime = %" PRIu64 ", dev = %s{private}", startTime, deviceId_.c_str()); + + Message *message = new (std::nothrow) Message(TIME_SYNC_MESSAGE); + if (message == nullptr) { + return -E_OUT_OF_MEMORY; + } + message->SetSessionId(sessionId); + message->SetMessageType(TYPE_REQUEST); + message->SetPriority(Priority::NORMAL); + int errCode = message->SetCopiedObject<>(packet); + if (errCode != E_OK) { + delete message; + message = nullptr; + return errCode; + } + + errCode = SendPacket(deviceId_, message, handler); + if (errCode != E_OK) { + delete message; + message = nullptr; + } + return errCode; +} + +uint32_t TimeSync::CalculateLen(const Message *inMsg) +{ + if (!(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return 0; + } + + const TimeSyncPacket *packet = const_cast(inMsg->GetObject()); + if (packet == nullptr) { + return 0; + } + + return TimeSyncPacket::CalculateLen(); +} + +int TimeSync::Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return -E_INVALID_ARGS; + } + const TimeSyncPacket *packet = inMsg->GetObject(); + if ((packet == nullptr) || (length != TimeSyncPacket::CalculateLen())) { + return -E_INVALID_ARGS; + } + + Parcel parcel(buffer, length); + Timestamp srcBegin = packet->GetSourceTimeBegin(); + Timestamp srcEnd = packet->GetSourceTimeEnd(); + Timestamp targetBegin = packet->GetTargetTimeBegin(); + Timestamp targetEnd = packet->GetTargetTimeEnd(); + + int errCode = parcel.WriteUInt32(TIME_SYNC_VERSION_V1); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + errCode = parcel.WriteUInt64(srcBegin); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + errCode = parcel.WriteUInt64(srcEnd); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + errCode = parcel.WriteUInt64(targetBegin); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + errCode = parcel.WriteUInt64(targetEnd); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + parcel.EightByteAlign(); + + return errCode; +} + +int TimeSync::DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return -E_INVALID_ARGS; + } + TimeSyncPacket packet; + Parcel parcel(const_cast(buffer), length); + Timestamp srcBegin; + Timestamp srcEnd; + Timestamp targetBegin; + Timestamp targetEnd; + + uint32_t version = 0; + parcel.ReadUInt32(version); + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + if (version > TIME_SYNC_VERSION_V1) { + packet.SetVersion(version); + return inMsg->SetCopiedObject<>(packet); + } + parcel.ReadUInt64(srcBegin); + parcel.ReadUInt64(srcEnd); + parcel.ReadUInt64(targetBegin); + parcel.ReadUInt64(targetEnd); + if (parcel.IsError()) { + return -E_INVALID_ARGS; + } + packet.SetSourceTimeBegin(srcBegin); + packet.SetSourceTimeEnd(srcEnd); + packet.SetTargetTimeBegin(targetBegin); + packet.SetTargetTimeEnd(targetEnd); + + return inMsg->SetCopiedObject<>(packet); +} + +int TimeSync::AckRecv(const Message *message, uint32_t targetSessionId) +{ + // only check when sessionId is not 0, because old version timesync sessionId is 0. + if (message != nullptr && message->GetSessionId() != 0 && + message->GetErrorNo() == E_FEEDBACK_COMMUNICATOR_NOT_FOUND && message->GetSessionId() == targetSessionId) { + LOGE("[AbilitySync][AckMsgCheck] Remote db is closed"); + return -E_FEEDBACK_COMMUNICATOR_NOT_FOUND; + } + if (!IsPacketValid(message, TYPE_RESPONSE)) { + return -E_INVALID_ARGS; + } + const TimeSyncPacket *packet = message->GetObject(); + if (packet == nullptr) { + LOGE("[TimeSync] AckRecv packet is null"); + return -E_INVALID_ARGS; + } + + TimeSyncPacket packetData = TimeSyncPacket(*packet); + Timestamp sourceTimeEnd = timeHelper_->GetTime(); + packetData.SetSourceTimeEnd(sourceTimeEnd); + if (packetData.GetSourceTimeBegin() > packetData.GetSourceTimeEnd() || + packetData.GetTargetTimeBegin() > packetData.GetTargetTimeEnd() || + packetData.GetSourceTimeEnd() > TimeHelper::MAX_VALID_TIME || + packetData.GetTargetTimeEnd() > TimeHelper::MAX_VALID_TIME) { + LOGD("[TimeSync][AckRecv] Time valid check failed."); + return -E_INVALID_TIME; + } + // calculate timeoffset of two devices + TimeOffset offset = CalculateTimeOffset(packetData); + LOGD("TimeSync::AckRecv, dev = %s{private}, sEnd = %" PRIu64 ", tEnd = %" PRIu64 ", sBegin = %" PRIu64 + ", tBegin = %" PRIu64 ", offset = %" PRId64, + deviceId_.c_str(), + packetData.GetSourceTimeEnd(), + packetData.GetTargetTimeEnd(), + packetData.GetSourceTimeBegin(), + packetData.GetTargetTimeBegin(), + offset); + + // save timeoffset into metadata, maybe a block action + int errCode = SaveTimeOffset(deviceId_, offset); + isSynced_ = true; + { + std::lock_guard lock(cvLock_); + isAckReceived_ = true; + } + conditionVar_.notify_all(); + ResetTimer(); + return errCode; +} + +int TimeSync::RequestRecv(const Message *message) +{ + if (!IsPacketValid(message, TYPE_REQUEST)) { + return -E_INVALID_ARGS; + } + Timestamp targetTimeBegin = timeHelper_->GetTime(); + + const TimeSyncPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + // build timeSync ack packet + TimeSyncPacket ackPacket = TimeSyncPacket(*packet); + ackPacket.SetTargetTimeBegin(targetTimeBegin); + Timestamp targetTimeEnd = timeHelper_->GetTime(); + ackPacket.SetTargetTimeEnd(targetTimeEnd); + LOGD("TimeSync::RequestRecv, dev = %s{private}, sTimeEnd = %" PRIu64 ", tTimeEnd = %" PRIu64 ", sbegin = %" PRIu64 + ", tbegin = %" PRIu64, deviceId_.c_str(), ackPacket.GetSourceTimeEnd(), ackPacket.GetTargetTimeEnd(), + ackPacket.GetSourceTimeBegin(), ackPacket.GetTargetTimeBegin()); + if (ackPacket.GetSourceTimeBegin() > TimeHelper::MAX_VALID_TIME) { + LOGD("[TimeSync][RequestRecv] Time valid check failed."); + return -E_INVALID_TIME; + } + + TimeOffset timeoffsetIgnoreRtt = static_cast(ackPacket.GetSourceTimeBegin() - targetTimeBegin); + TimeOffset metadataTimeoffset; + metadata_->GetTimeOffset(deviceId_, metadataTimeoffset); + + // 2 is half of INT64_MAX + if ((std::abs(metadataTimeoffset) >= INT64_MAX / 2) || (std::abs(timeoffsetIgnoreRtt) >= INT64_MAX / 2) || + (std::abs(metadataTimeoffset - timeoffsetIgnoreRtt) > MAX_TIME_OFFSET_NOISE)) { + LOGI("[TimeSync][RequestRecv] timeoffSet invalid, should do time sync"); + isSynced_ = false; + } + + Message *ackMessage = new (std::nothrow) Message(TIME_SYNC_MESSAGE); + if (ackMessage == nullptr) { + return -E_OUT_OF_MEMORY; + } + ackMessage->SetSessionId(message->GetSessionId()); + ackMessage->SetPriority(Priority::NORMAL); + ackMessage->SetMessageType(TYPE_RESPONSE); + ackMessage->SetTarget(deviceId_); + int errCode = ackMessage->SetCopiedObject<>(ackPacket); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + return errCode; + } + + errCode = SendPacket(deviceId_, ackMessage); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + } + return errCode; +} + +int TimeSync::SaveTimeOffset(const DeviceID &deviceID, TimeOffset timeOffset) +{ + return metadata_->SaveTimeOffset(deviceID, timeOffset); +} + +TimeOffset TimeSync::CalculateTimeOffset(const TimeSyncPacket &timeSyncInfo) +{ + TimeOffset roundTrip = static_cast((timeSyncInfo.GetSourceTimeEnd() - + timeSyncInfo.GetSourceTimeBegin()) - (timeSyncInfo.GetTargetTimeEnd() - timeSyncInfo.GetTargetTimeBegin())); + TimeOffset offset1 = static_cast(timeSyncInfo.GetTargetTimeBegin() - + timeSyncInfo.GetSourceTimeBegin() - (roundTrip / TRIP_DIV_HALF)); + TimeOffset offset2 = static_cast(timeSyncInfo.GetTargetTimeEnd() + (roundTrip / TRIP_DIV_HALF) - + timeSyncInfo.GetSourceTimeEnd()); + TimeOffset offset = (offset1 / TRIP_DIV_HALF) + (offset2 / TRIP_DIV_HALF); + LOGD("TimeSync::CalculateTimeOffset roundTrip= %" PRId64 ", offset1 = %" PRId64 ", offset2 = %" PRId64 + ", offset = %" PRId64, roundTrip, offset1, offset2, offset); + return offset; +} + +bool TimeSync::IsPacketValid(const Message *inMsg, uint16_t messageType) +{ + if (inMsg == nullptr) { + return false; + } + if (inMsg->GetMessageId() != TIME_SYNC_MESSAGE) { + LOGD("message Id = %d", inMsg->GetMessageId()); + return false; + } + if (messageType != inMsg->GetMessageType()) { + LOGD("input Type = %hu, inMsg type = %hu", messageType, inMsg->GetMessageType()); + return false; + } + return true; +} + +int TimeSync::SendPacket(const DeviceID &deviceId, const Message *message, const CommErrHandler &handler) +{ + SendConfig conf; + timeHelper_->SetSendConfig(deviceId, false, SEND_TIME_OUT, conf); + int errCode = communicateHandle_->SendMessage(deviceId, message, conf, handler); + if (errCode != E_OK) { + LOGE("[TimeSync] SendPacket failed, err %d", errCode); + } + return errCode; +} + +int TimeSync::TimeSyncDriver(TimerId timerId) +{ + if (timerId != driverTimerId_) { + return -E_INTERNAL_ERROR; + } + if (!isOnline_) { + return E_OK; + } + std::lock_guard lock(timeDriverLock_); + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this]() { + CommErrHandler handler = std::bind(&TimeSync::CommErrHandlerFunc, std::placeholders::_1, this); + (void)this->SyncStart(handler); + std::lock_guard innerLock(this->timeDriverLock_); + this->timeDriverLockCount_--; + this->timeDriverCond_.notify_all(); + }); + if (errCode != E_OK) { + LOGE("[TimeSync][TimerSyncDriver] ScheduleTask failed err %d", errCode); + return errCode; + } + timeDriverLockCount_++; + return E_OK; +} + +int TimeSync::GetTimeOffset(TimeOffset &outOffset, uint32_t timeout, uint32_t sessionId) +{ + if (!isSynced_) { + { + std::lock_guard lock(cvLock_); + isAckReceived_ = false; + } + CommErrHandler handler = std::bind(&TimeSync::CommErrHandlerFunc, std::placeholders::_1, this); + int errCode = SyncStart(handler, sessionId); + LOGD("TimeSync::GetTimeOffset start, current time = %" PRIu64 ", errCode = %d, timeout = %" PRIu32 " ms", + TimeHelper::GetSysCurrentTime(), errCode, timeout); + std::unique_lock lock(cvLock_); + if (errCode != E_OK || !conditionVar_.wait_for(lock, std::chrono::milliseconds(timeout), + [this](){ return this->isAckReceived_ == true; })) { + LOGD("TimeSync::GetTimeOffset, retryTime_ = %d", retryTime_); + retryTime_++; + if (retryTime_ < MAX_RETRY_TIME) { + lock.unlock(); + LOGI("TimeSync::GetTimeOffset timeout, try again"); + return GetTimeOffset(outOffset, timeout); + } + retryTime_ = 0; + return -E_TIMEOUT; + } + } + retryTime_ = 0; + metadata_->GetTimeOffset(deviceId_, outOffset); + return E_OK; +} + +bool TimeSync::IsNeedSync() const +{ + return !isSynced_; +} + +void TimeSync::SetOnline(bool isOnline) +{ + isOnline_ = isOnline; +} + +void TimeSync::CommErrHandlerFunc(int errCode, TimeSync *timeSync) +{ + LOGD("[TimeSync][CommErrHandle] errCode:%d", errCode); + std::lock_guard lock(timeSyncSetLock_); + if (timeSyncSet_.count(timeSync) == 0) { + LOGI("[TimeSync][CommErrHandle] timeSync has been killed"); + return; + } + if (timeSync == nullptr) { + LOGI("[TimeSync][CommErrHandle] timeSync is nullptr"); + return; + } + if (errCode != E_OK) { + timeSync->SetOnline(false); + } else { + timeSync->SetOnline(true); + } +} + +void TimeSync::ResetTimer() +{ + std::lock_guard lock(timeDriverLock_); + RuntimeContext::GetInstance()->RemoveTimer(driverTimerId_, true); + int errCode = RuntimeContext::GetInstance()->SetTimer( + TIME_SYNC_INTERVAL, driverCallback_, nullptr, driverTimerId_); + if (errCode != E_OK) { + LOGW("[TimeSync] Reset TimeSync timer failed err :%d", errCode); + } +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/syncer/src/time_sync.h b/mock/distributeddb/syncer/src/time_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..5aaa8a115c37585fceb3eb20cfc3e24f1c350271 --- /dev/null +++ b/mock/distributeddb/syncer/src/time_sync.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TIME_SYNC_H +#define TIME_SYNC_H + +#include "icommunicator.h" +#include "meta_data.h" +#include "sync_task_context.h" +#include "time_helper.h" + +namespace DistributedDB { +class TimeSyncPacket { +public: + TimeSyncPacket(); + ~TimeSyncPacket(); + + void SetSourceTimeBegin(Timestamp sourceTimeBegin); + + Timestamp GetSourceTimeBegin() const; + + void SetSourceTimeEnd(Timestamp sourceTimeEnd); + + Timestamp GetSourceTimeEnd() const; + + void SetTargetTimeBegin(Timestamp targetTimeBegin); + + Timestamp GetTargetTimeBegin() const; + + void SetTargetTimeEnd(Timestamp targetTimeEnd); + + Timestamp GetTargetTimeEnd() const; + + void SetVersion(uint32_t version); + + uint32_t GetVersion() const; + + static uint32_t CalculateLen(); +private: + Timestamp sourceTimeBegin_; // start point time on peer + Timestamp sourceTimeEnd_; // end point time on local + Timestamp targetTimeBegin_; // start point time on peer + Timestamp targetTimeEnd_; // end point time on peer + uint32_t version_; +}; + +class TimeSync { +public: + TimeSync(); + ~TimeSync(); + + DISABLE_COPY_ASSIGN_MOVE(TimeSync); + + static int RegisterTransformFunc(); + + static uint32_t CalculateLen(const Message *inMsg); + + static int Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg); // register to communicator + + static int DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); // register to communicator + + int Initialize(ICommunicator *communicator, std::shared_ptr &metadata, + const ISyncInterface *storage, const DeviceID &deviceId); + + int SyncStart(const CommErrHandler &handler = nullptr, uint32_t sessionId = 0); // send timesync request + + int AckRecv(const Message *message, uint32_t targetSessionId = 0); + + int RequestRecv(const Message *message); + + // Get timeoffset from metadata + int GetTimeOffset(TimeOffset &outOffset, uint32_t timeout, uint32_t sessionId = 0); + + bool IsNeedSync() const; + + void SetOnline(bool isOnline); + + // Used in send msg, as execution is asynchronous, should use this function to handle result. + static void CommErrHandlerFunc(int errCode, TimeSync *timeSync); + +private: + static const int MAX_RETRY_TIME = 1; + + static TimeOffset CalculateTimeOffset(const TimeSyncPacket &timeSyncInfo); + + static bool IsPacketValid(const Message *inMsg, uint16_t messageType); + + void Finalize(); + + int SaveTimeOffset(const DeviceID &deviceID, TimeOffset timeOffset); + + int SendPacket(const DeviceID &deviceId, const Message *message, const CommErrHandler &handler = nullptr); + + int TimeSyncDriver(TimerId timerId); + + void ResetTimer(); + + ICommunicator *communicateHandle_; + std::shared_ptr metadata_; + std::unique_ptr timeHelper_; + DeviceID deviceId_; + int retryTime_; + TimerId driverTimerId_; + TimerAction driverCallback_; + bool isSynced_; + bool isAckReceived_; + std::condition_variable conditionVar_; + std::mutex cvLock_; + NotificationChain::Listener *timeChangedListener_; + std::condition_variable timeDriverCond_; + std::mutex timeDriverLock_; + int timeDriverLockCount_; + bool isOnline_; + static std::mutex timeSyncSetLock_; + static std::set timeSyncSet_; +}; +} // namespace DistributedDB + +#endif // TIME_SYNC_H diff --git a/mock/distributeddb/syncer/src/value_slice_sync.cpp b/mock/distributeddb/syncer/src/value_slice_sync.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b19fa333dc4f18d8a635ed3dfe9786cee98a9a4 --- /dev/null +++ b/mock/distributeddb/syncer/src/value_slice_sync.cpp @@ -0,0 +1,631 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_MULTI_VER +#include "value_slice_sync.h" + +#include "parcel.h" +#include "log_print.h" +#include "sync_types.h" +#include "message_transform.h" +#include "performance_analysis.h" +#include "db_constant.h" + +namespace DistributedDB { +const int ValueSliceSync::MAX_VALUE_NODE_SIZE = 100000; + +// Class ValueSliceHashPacket +uint32_t ValueSliceHashPacket::CalculateLen() const +{ + uint64_t len = Parcel::GetIntLen(); + len = Parcel::GetEightByteAlign(len); + len += Parcel::GetVectorCharLen(valueSliceHash_); + if (len > INT32_MAX) { + return 0; + } + return len; +} + +void ValueSliceHashPacket::SetValueSliceHash(ValueSliceHash &hash) +{ + valueSliceHash_ = std::move(hash); +} + +void ValueSliceHashPacket::GetValueSliceHash(ValueSliceHash &hash) const +{ + hash = valueSliceHash_; +} + +void ValueSliceHashPacket::SetErrCode(int32_t errCode) +{ + errCode_ = errCode; +} + +int32_t ValueSliceHashPacket::GetErrCode() const +{ + return errCode_; +} + +// Class ValueSlicePacket +uint32_t ValueSlicePacket::CalculateLen() const +{ + uint64_t len = Parcel::GetIntLen(); + len = Parcel::GetEightByteAlign(len); + len += Parcel::GetVectorCharLen(valueSlice_); + if (len > INT32_MAX) { + return 0; + } + return len; +} + +void ValueSlicePacket::SetData(const ValueSlice &data) +{ + valueSlice_ = std::move(data); +} + +void ValueSlicePacket::GetData(ValueSlice &data) const +{ + data = valueSlice_; +} + +void ValueSlicePacket::SetErrorCode(int32_t errCode) +{ + errorCode_ = errCode; +} + +void ValueSlicePacket::GetErrorCode(int32_t &errCode) const +{ + errCode = errorCode_; +} + +// Class ValueSliceSync +ValueSliceSync::~ValueSliceSync() +{ + storagePtr_ = nullptr; + communicateHandle_ = nullptr; +} + +int ValueSliceSync::Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return -E_INVALID_ARGS; + } + + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return RequestPacketSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckPacketSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +int ValueSliceSync::DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return -E_INVALID_ARGS; + } + + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + return RequestPacketDeSerialization(buffer, length, inMsg); + case TYPE_RESPONSE: + return AckPacketDeSerialization(buffer, length, inMsg); + default: + return -E_MESSAGE_TYPE_ERROR; + } +} + +uint32_t ValueSliceSync::CalculateLen(const Message *inMsg) +{ + if (!(IsPacketValid(inMsg, TYPE_RESPONSE) || IsPacketValid(inMsg, TYPE_REQUEST))) { + return 0; + } + + uint32_t len = 0; + int errCode = E_OK; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + errCode = RequestPacketCalculateLen(inMsg, len); + break; + case TYPE_RESPONSE: + errCode = AckPacketCalculateLen(inMsg, len); + break; + default: + return 0; + } + if (errCode != E_OK) { + return 0; + } + return len; +} + +int ValueSliceSync::RegisterTransformFunc() +{ + TransformFunc func; + func.computeFunc = std::bind(&ValueSliceSync::CalculateLen, std::placeholders::_1); + func.serializeFunc = std::bind(&ValueSliceSync::Serialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + func.deserializeFunc = std::bind(&ValueSliceSync::DeSerialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + return MessageTransform::RegTransformFunction(VALUE_SLICE_SYNC_MESSAGE, func); +} + +int ValueSliceSync::Initialize(MultiVerKvDBSyncInterface *storagePtr, ICommunicator *communicateHandle) +{ + if ((storagePtr == nullptr) || (communicateHandle == nullptr)) { + return -E_INVALID_ARGS; + } + storagePtr_ = storagePtr; + communicateHandle_ = communicateHandle; + return E_OK; +} + +int ValueSliceSync::SyncStart(MultiVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + int entriesIndex = context->GetEntriesIndex(); + int entriesSize = context->GetEntriesSize(); + if (entriesSize > DBConstant::MAX_ENTRIES_SIZE) { + LOGE("ValueSliceSync::entriesSize too large %d", entriesSize); + return -E_INVALID_ARGS; + } + while (entriesIndex < entriesSize) { + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_GET_VALUE_SLICE_NODE); + } + ValueSliceHash valueSliceHashNode; + int errCode = GetValidValueSliceHashNode(context, valueSliceHashNode); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_GET_VALUE_SLICE_NODE); + } + LOGD("ValueSliceSync::SyncStart begin errCode = %d", errCode); + if (errCode == E_OK) { + errCode = SendRequestPacket(context, valueSliceHashNode); + LOGD("ValueSliceSync::SyncStart send request packet dst=%s{private}, errCode = %d", + context->GetDeviceId().c_str(), errCode); + return errCode; + } + // move to next entry + MultiVerKvEntry *entry = nullptr; + std::vector valueHashes; + entriesIndex++; + if (entriesIndex < entriesSize) { + LOGD("ValueSliceSync::SyncStart begin entriesIndex = %d, entriesSize = %d", entriesIndex, entriesSize); + context->SetEntriesIndex(entriesIndex); + context->GetEntry(entriesIndex, entry); + errCode = entry->GetValueHash(valueHashes); + if (errCode != E_OK) { + LOGE("ValueSliceSync::entry->GetValueHash %d", errCode); + return errCode; + } + context->SetValueSliceHashNodes(valueHashes); + context->SetValueSlicesIndex(0); + context->SetValueSlicesSize(static_cast(valueHashes.size())); + } else { + // all entries are received, move to next commit + return -E_NOT_FOUND; + } + } + return -E_NOT_FOUND; +} + +int ValueSliceSync::RequestRecvCallback(const MultiVerSyncTaskContext *context, const Message *message) +{ + if (!IsPacketValid(message, TYPE_REQUEST) || context == nullptr) { + return -E_INVALID_ARGS; + } + + const ValueSliceHashPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + ValueSliceHash valueSliceHashNode; + packet->GetValueSliceHash(valueSliceHashNode); + if ((packet->GetErrCode() == -E_LAST_SYNC_FRAME) && valueSliceHashNode.empty()) { + return -E_LAST_SYNC_FRAME; + } + ValueSlice valueSlice; + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_READ_VALUE_SLICE); + } + int errCode = GetValueSlice(valueSliceHashNode, valueSlice); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_READ_VALUE_SLICE); + } + if (errCode != E_OK) { + LOGE("ValueSliceSync::RequestRecvCallback : GetValueSlice ERR, errno = %d", errCode); + } + errCode = SendAckPacket(context, valueSlice, errCode, message); + LOGD("ValueSliceSync::RequestRecvCallback : SendAckPacket, errno = %d, dst = %s{private}", errCode, + context->GetDeviceId().c_str()); + if (packet->GetErrCode() == -E_LAST_SYNC_FRAME) { + return -E_LAST_SYNC_FRAME; + } + return errCode; +} + +int ValueSliceSync::AckRecvCallback(const MultiVerSyncTaskContext *context, const Message *message) +{ + if (!IsPacketValid(message, TYPE_RESPONSE) || (context == nullptr)) { + return -E_INVALID_ARGS; + } + + const ValueSlicePacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = E_OK; + packet->GetErrorCode(errCode); + ValueSlice valueSlice; + packet->GetData(valueSlice); + if (errCode != E_OK) { + return errCode; + } + int index = context->GetValueSlicesIndex(); + ValueSliceHash hashValue; + context->GetValueSliceHashNode(index, hashValue); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_SAVE_VALUE_SLICE); + } + errCode = PutValueSlice(hashValue, valueSlice); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_SAVE_VALUE_SLICE); + } + LOGD("ValueSliceSync::AckRecvCallback PutValueSlice finished, src=%s{private}, errCode = %d", + context->GetDeviceId().c_str(), errCode); + if (errCode != E_OK) { + return errCode; + } + return errCode; +} + +void ValueSliceSync::SendFinishedRequest(const MultiVerSyncTaskContext *context) +{ + if (context == nullptr) { + return; + } + + ValueSliceHashPacket *packet = new (std::nothrow) ValueSliceHashPacket(); + if (packet == nullptr) { + return; + } + + packet->SetErrCode(-E_LAST_SYNC_FRAME); + Message *message = new (std::nothrow) Message(VALUE_SLICE_SYNC_MESSAGE); + if (message == nullptr) { + delete packet; + packet = nullptr; + return; + } + + int errCode = message->SetExternalObject(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete message; + message = nullptr; + return; + } + + message->SetMessageType(TYPE_REQUEST); + message->SetTarget(context->GetDeviceId()); + message->SetSessionId(context->GetRequestSessionId()); + message->SetSequenceId(context->GetSequenceId()); + errCode = Send(message->GetTarget(), message); + if (errCode != E_OK) { + delete message; + message = nullptr; + LOGE("[ValueSliceSync][SendRequestPacket] SendRequestPacket failed, err %d", errCode); + } + LOGI("[ValueSliceSync][SendRequestPacket] SendRequestPacket dst=%s{private}", context->GetDeviceId().c_str()); +} + +int ValueSliceSync::RequestPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + const ValueSliceHashPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + len = packet->CalculateLen(); + return E_OK; +} + +int ValueSliceSync::RequestPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + const ValueSliceHashPacket *packet = inMsg->GetObject(); + if ((packet == nullptr) || (length != packet->CalculateLen())) { + return -E_INVALID_ARGS; + } + + Parcel parcel(buffer, length); + ValueSliceHash valueSliceHash; + packet->GetValueSliceHash(valueSliceHash); + int32_t ackCode = packet->GetErrCode(); + // errCode Serialization + int32_t errCode = parcel.WriteInt(ackCode); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + parcel.EightByteAlign(); + // commitMap Serialization + errCode = parcel.WriteVectorChar(valueSliceHash); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + + return errCode; +} + +int ValueSliceSync::RequestPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + Parcel parcel(const_cast(buffer), length); + + int ackCode = 0; + // errCode DeSerialization + uint32_t packLen = parcel.ReadInt(ackCode); + parcel.EightByteAlign(); + packLen = Parcel::GetEightByteAlign(packLen); + + ValueSliceHash valueSliceHash; + // commit DeSerialization + packLen += parcel.ReadVectorChar(valueSliceHash); + if (packLen != length || parcel.IsError()) { + return -E_INVALID_ARGS; + } + ValueSliceHashPacket *packet = new (std::nothrow) ValueSliceHashPacket(); + if (packet == nullptr) { + LOGE("ValueSliceSync::AckPacketDeSerialization : new packet error"); + return -E_OUT_OF_MEMORY; + } + + packet->SetValueSliceHash(valueSliceHash); + int errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + } + return errCode; +} + +int ValueSliceSync::AckPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + const ValueSlicePacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + len = packet->CalculateLen(); + return E_OK; +} + +int ValueSliceSync::AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || !IsPacketValid(inMsg, TYPE_RESPONSE)) { + return -E_INVALID_ARGS; + } + const ValueSlicePacket *packet = inMsg->GetObject(); + if ((packet == nullptr) || (length != packet->CalculateLen())) { + return -E_INVALID_ARGS; + } + + Parcel parcel(buffer, length); + ValueSlice valueSlice; + packet->GetData(valueSlice); + int32_t ackCode = 0; + packet->GetErrorCode(ackCode); + // errCode Serialization + int32_t errCode = parcel.WriteInt(ackCode); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + parcel.EightByteAlign(); + + // commits vector Serialization + errCode = parcel.WriteVectorChar(valueSlice); + if (errCode != E_OK) { + return -E_SECUREC_ERROR; + } + + return errCode; +} + +int ValueSliceSync::AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + Parcel parcel(const_cast(buffer), length); + int32_t ackCode = 0; + uint32_t packLen = 0; + ValueSlice valueSlice; + + // errCode DeSerialization + packLen += parcel.ReadInt(ackCode); + parcel.EightByteAlign(); + packLen = Parcel::GetEightByteAlign(packLen); + // valueSlice DeSerialization + packLen += parcel.ReadVectorChar(valueSlice); + if (packLen != length || parcel.IsError()) { + LOGE("ValueSliceSync::AckPacketSerialization data error, packLen = %" PRIu32 ", length = %" PRIu32, + packLen, length); + return -E_INVALID_ARGS; + } + ValueSlicePacket *packet = new (std::nothrow) ValueSlicePacket(); + if (packet == nullptr) { + LOGE("ValueSliceSync::AckPacketDeSerialization : new packet error"); + return -E_OUT_OF_MEMORY; + } + packet->SetData(valueSlice); + packet->SetErrorCode(ackCode); + int errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + } + return errCode; +} + +bool ValueSliceSync::IsPacketValid(const Message *inMsg, uint16_t messageType) +{ + if ((inMsg == nullptr) || (inMsg->GetMessageId() != VALUE_SLICE_SYNC_MESSAGE)) { + return false; + } + if (messageType != inMsg->GetMessageType()) { + return false; + } + return true; +} + +int ValueSliceSync::GetValidValueSliceHashNode(MultiVerSyncTaskContext *context, ValueSliceHash &valueHashNode) +{ + int index = context->GetValueSlicesIndex(); + int valueNodesSize = context->GetValueSlicesSize(); + if (valueNodesSize > MAX_VALUE_NODE_SIZE) { + LOGD("ValueSliceSync::GetValidValueSliceHashNode failed, too large!"); + return -E_LENGTH_ERROR; + } + LOGD("ValueSliceSync::GetValidValueSliceHashNode ValueSlicesSize = %d", valueNodesSize); + if (context->GetRetryStatus() == SyncTaskContext::NEED_RETRY) { + context->SetRetryStatus(SyncTaskContext::NO_NEED_RETRY); + index--; + } + std::vector valueSliceHashNodes; + context->GetValueSliceHashNodes(valueSliceHashNodes); + index = (index < 0) ? 0 : index; + while (index < valueNodesSize) { + if (IsValueSliceExisted(valueSliceHashNodes[index])) { + index++; + context->SetValueSlicesIndex(index); + continue; + } + valueHashNode = valueSliceHashNodes[index]; + return E_OK; + } + return -E_NOT_FOUND; +} + +int ValueSliceSync::Send(const DeviceID &deviceId, const Message *inMsg) +{ + SendConfig conf = {false, false, SEND_TIME_OUT, {}}; + int errCode = communicateHandle_->SendMessage(deviceId, inMsg, conf); + if (errCode != E_OK) { + LOGE("ValueSliceSync::Send ERR! err = %d", errCode); + } + return errCode; +} + +int ValueSliceSync::SendRequestPacket(const MultiVerSyncTaskContext *context, ValueSliceHash &valueSliceHash) +{ + ValueSliceHashPacket *packet = new (std::nothrow) ValueSliceHashPacket(); + if (packet == nullptr) { + LOGE("ValueSliceSync::SendRequestPacket : new packet error"); + return -E_OUT_OF_MEMORY; + } + + packet->SetValueSliceHash(valueSliceHash); + Message *message = new (std::nothrow) Message(VALUE_SLICE_SYNC_MESSAGE); + if (message == nullptr) { + delete packet; + packet = nullptr; + LOGE("ValueSliceSync::SendRequestPacket : new message error"); + return -E_OUT_OF_MEMORY; + } + + int errCode = message->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete message; + message = nullptr; + return errCode; + } + + message->SetMessageType(TYPE_REQUEST); + message->SetTarget(context->GetDeviceId()); + message->SetSessionId(context->GetRequestSessionId()); + message->SetSequenceId(context->GetSequenceId()); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(MV_TEST_RECORDS::RECORD_VALUE_SLICE_SEND_REQUEST_TO_ACK_RECV); + } + errCode = Send(message->GetTarget(), message); + if (errCode != E_OK) { + delete message; + message = nullptr; + } + return errCode; +} + +int ValueSliceSync::SendAckPacket(const MultiVerSyncTaskContext *context, const ValueSlice &value, + int ackCode, const Message *message) +{ + ValueSlicePacket *packet = new (std::nothrow) ValueSlicePacket(); + if (packet == nullptr) { + LOGE("ValueSliceSync::SendAckPacket : packet is nullptr"); + return -E_OUT_OF_MEMORY; + } + + Message *ackMessage = new (std::nothrow) Message(VALUE_SLICE_SYNC_MESSAGE); + if (ackMessage == nullptr) { + delete packet; + packet = nullptr; + LOGE("ValueSliceSync::SendAckPacket : new message error"); + return -E_OUT_OF_MEMORY; + } + + packet->SetData(value); + packet->SetErrorCode(static_cast(ackCode)); + int errCode = ackMessage->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + delete ackMessage; + ackMessage = nullptr; + return errCode; + } + + ackMessage->SetMessageType(TYPE_RESPONSE); + ackMessage->SetTarget(context->GetDeviceId()); + ackMessage->SetSequenceId(message->GetSequenceId()); + ackMessage->SetSessionId(message->GetSessionId()); + errCode = Send(ackMessage->GetTarget(), ackMessage); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + } + + return errCode; +} + +bool ValueSliceSync::IsValueSliceExisted(const ValueSliceHash &value) +{ + return storagePtr_->IsValueSliceExisted(value); +} + +int ValueSliceSync::GetValueSlice(const ValueSliceHash &hashValue, ValueSlice &sliceValue) +{ + return storagePtr_->GetValueSlice(hashValue, sliceValue); +} + +int ValueSliceSync::PutValueSlice(const ValueSliceHash &hashValue, const ValueSlice &sliceValue) +{ + return storagePtr_->PutValueSlice(hashValue, sliceValue); +} +} // namespace DistributedDB +#endif diff --git a/mock/distributeddb/syncer/src/value_slice_sync.h b/mock/distributeddb/syncer/src/value_slice_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..e8ff764f1d5455aadfb0a12baeb4a5c05a0fedc5 --- /dev/null +++ b/mock/distributeddb/syncer/src/value_slice_sync.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VALUE_SLICE_SYNC_H +#define VALUE_SLICE_SYNC_H + +#ifndef OMIT_MULTI_VER +#include + +#include "icommunicator.h" +#include "multi_ver_kvdb_sync_interface.h" +#include "multi_ver_sync_task_context.h" + +namespace DistributedDB { +class ValueSliceHashPacket { +public: + ValueSliceHashPacket() : errCode_(E_OK) {}; + ~ValueSliceHashPacket() {}; + + uint32_t CalculateLen() const; + + void SetValueSliceHash(ValueSliceHash &hash); + + void GetValueSliceHash(ValueSliceHash &hash) const; + + void SetErrCode(int32_t errCode); + + int32_t GetErrCode() const; +private: + ValueSliceHash valueSliceHash_; + int32_t errCode_; +}; + +class ValueSlicePacket { +public: + ValueSlicePacket() : errorCode_(0) {}; + ~ValueSlicePacket() {}; + + uint32_t CalculateLen() const; + + void SetData(const ValueSlice &data); + + void GetData(ValueSlice &data) const; + + void SetErrorCode(int32_t errCode); + + void GetErrorCode(int32_t &errCode) const; +private: + ValueSlice valueSlice_; + int32_t errorCode_; +}; + +class ValueSliceSync { +public: + ValueSliceSync() : storagePtr_(nullptr), communicateHandle_(nullptr) {}; + ~ValueSliceSync(); + DISABLE_COPY_ASSIGN_MOVE(ValueSliceSync); + + static int RegisterTransformFunc(); + + int Initialize(MultiVerKvDBSyncInterface *storagePtr, ICommunicator *communicateHandle); + + static int Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static uint32_t CalculateLen(const Message *inMsg); + + int SyncStart(MultiVerSyncTaskContext *context); + + int RequestRecvCallback(const MultiVerSyncTaskContext *context, const Message *message); + + int AckRecvCallback(const MultiVerSyncTaskContext *context, const Message *message); + + void SendFinishedRequest(const MultiVerSyncTaskContext *context); + +private: + static int RequestPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int RequestPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int RequestPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static int AckPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int AckPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int AckPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + static bool IsPacketValid(const Message *inMsg, uint16_t messageType); + + int GetValidValueSliceHashNode(MultiVerSyncTaskContext *context, ValueSliceHash &valueHashNode); + + int Send(const DeviceID &deviceId, const Message *inMsg); + + int SendRequestPacket(const MultiVerSyncTaskContext *context, ValueSliceHash &valueSliceHash); + + int SendAckPacket(const MultiVerSyncTaskContext *context, const ValueSlice &value, int ackCode, + const Message *message); + + bool IsValueSliceExisted(const ValueSliceHash &value); + + int GetValueSlice(const ValueSliceHash &hashValue, ValueSlice &sliceValue); + + int PutValueSlice(const ValueSliceHash &hashValue, const ValueSlice &sliceValue); + + static const int MAX_VALUE_NODE_SIZE; + MultiVerKvDBSyncInterface *storagePtr_; + ICommunicator *communicateHandle_; +}; +} + +#endif +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/BUILD.gn b/mock/distributeddb/test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..3ecedfeda945f4dc76ca6ac5c4ac16a7c7eae782 --- /dev/null +++ b/mock/distributeddb/test/BUILD.gn @@ -0,0 +1,772 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import("//build/test.gni") + +module_output_path = "distributeddatamgr/distributeddb" + +############################################################################### +config("module_private_config") { + visibility = [ ":*" ] + + include_dirs = [ + "./unittest/common/common", + "./unittest/common/syncer", + "./unittest/common/storage", + "./unittest/common/interfaces", + "../include", + "../interfaces/include", + "../interfaces/include/relational", + "../interfaces/src", + "../interfaces/src/relational", + "../storage/include", + "../storage/src", + "../storage/src/multiver", + "../storage/src/operation", + "../storage/src/sqlite", + "../storage/src/sqlite/relational", + "../storage/src/upgrader", + "../common/include", + "../common/include/relational", + "../common/src", + "../communicator/include", + "../communicator/src", + "../syncer/include", + "../syncer/src", + "//third_party/openssl/include/", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + "USE_DFX_ABILITY", + "TRACE_SQLITE_EXECUTE", + ] +} + +############################################################################### +ohos_source_set("src_file") { + testonly = true + + sources = [ + "../common/src/auto_launch.cpp", + "../common/src/data_compression.cpp", + "../common/src/data_value.cpp", + "../common/src/db_common.cpp", + "../common/src/db_constant.cpp", + "../common/src/db_dfx_adapter.cpp", + "../common/src/db_dump_helper.cpp", + "../common/src/evloop/src/event_impl.cpp", + "../common/src/evloop/src/event_loop_epoll.cpp", + "../common/src/evloop/src/event_loop_impl.cpp", + "../common/src/evloop/src/event_loop_select.cpp", + "../common/src/evloop/src/ievent.cpp", + "../common/src/evloop/src/ievent_loop.cpp", + "../common/src/flatbuffer_schema.cpp", + "../common/src/hash.cpp", + "../common/src/json_object.cpp", + "../common/src/lock_status_observer.cpp", + "../common/src/log_print.cpp", + "../common/src/notification_chain.cpp", + "../common/src/param_check_utils.cpp", + "../common/src/parcel.cpp", + "../common/src/performance_analysis.cpp", + "../common/src/platform_specific.cpp", + "../common/src/query.cpp", + "../common/src/query_expression.cpp", + "../common/src/ref_object.cpp", + "../common/src/relational/relational_schema_object.cpp", + "../common/src/runtime_context.cpp", + "../common/src/runtime_context_impl.cpp", + "../common/src/schema_constant.cpp", + "../common/src/schema_negotiate.cpp", + "../common/src/schema_object.cpp", + "../common/src/schema_utils.cpp", + "../common/src/semaphore_utils.cpp", + "../common/src/task_pool.cpp", + "../common/src/task_pool_impl.cpp", + "../common/src/task_queue.cpp", + "../common/src/time_tick_monitor.cpp", + "../common/src/types_export.cpp", + "../common/src/user_change_monitor.cpp", + "../common/src/value_object.cpp", + "../common/src/zlib_compression.cpp", + "../communicator/src/combine_status.cpp", + "../communicator/src/communicator.cpp", + "../communicator/src/communicator_aggregator.cpp", + "../communicator/src/communicator_linker.cpp", + "../communicator/src/frame_combiner.cpp", + "../communicator/src/frame_retainer.cpp", + "../communicator/src/header_converter.cpp", + "../communicator/src/message_transform.cpp", + "../communicator/src/network_adapter.cpp", + "../communicator/src/protocol_proto.cpp", + "../communicator/src/send_task_scheduler.cpp", + "../communicator/src/serial_buffer.cpp", + "../interfaces/src/intercepted_data_impl.cpp", + "../interfaces/src/kv_store_changed_data_impl.cpp", + "../interfaces/src/kv_store_delegate_impl.cpp", + "../interfaces/src/kv_store_delegate_manager.cpp", + "../interfaces/src/kv_store_errno.cpp", + "../interfaces/src/kv_store_nb_conflict_data_impl.cpp", + "../interfaces/src/kv_store_nb_delegate_impl.cpp", + "../interfaces/src/kv_store_result_set_impl.cpp", + "../interfaces/src/kv_store_snapshot_delegate_impl.cpp", + "../interfaces/src/relational/relational_store_changed_data_impl.cpp", + "../interfaces/src/relational/relational_store_delegate_impl.cpp", + "../interfaces/src/relational/relational_store_manager.cpp", + "../interfaces/src/relational/relational_store_sqlite_ext.cpp", + "../interfaces/src/relational/runtime_config.cpp", + "../storage/src/data_transformer.cpp", + "../storage/src/db_properties.cpp", + "../storage/src/default_factory.cpp", + "../storage/src/generic_kvdb.cpp", + "../storage/src/generic_kvdb_connection.cpp", + "../storage/src/generic_single_ver_kv_entry.cpp", + "../storage/src/iconnection.cpp", + "../storage/src/ikvdb_factory.cpp", + "../storage/src/kvdb_commit_notify_filterable_data.cpp", + "../storage/src/kvdb_manager.cpp", + "../storage/src/kvdb_observer_handle.cpp", + "../storage/src/kvdb_properties.cpp", + "../storage/src/kvdb_utils.cpp", + "../storage/src/kvdb_windowed_result_set.cpp", + "../storage/src/multiver/generic_multi_ver_kv_entry.cpp", + "../storage/src/multiver/multi_ver_commit.cpp", + "../storage/src/multiver/multi_ver_kvdata_storage.cpp", + "../storage/src/multiver/multi_ver_natural_store.cpp", + "../storage/src/multiver/multi_ver_natural_store_commit_notify_data.cpp", + "../storage/src/multiver/multi_ver_natural_store_commit_storage.cpp", + "../storage/src/multiver/multi_ver_natural_store_connection.cpp", + "../storage/src/multiver/multi_ver_natural_store_snapshot.cpp", + "../storage/src/multiver/multi_ver_natural_store_transfer_data.cpp", + "../storage/src/multiver/multi_ver_storage_engine.cpp", + "../storage/src/multiver/multi_ver_storage_executor.cpp", + "../storage/src/multiver/multi_ver_vacuum.cpp", + "../storage/src/multiver/multi_ver_vacuum_executor_impl.cpp", + "../storage/src/multiver/multi_ver_value_object.cpp", + "../storage/src/operation/database_oper.cpp", + "../storage/src/operation/local_database_oper.cpp", + "../storage/src/operation/multi_ver_database_oper.cpp", + "../storage/src/operation/single_ver_database_oper.cpp", + "../storage/src/package_file.cpp", + "../storage/src/relational_store_connection.cpp", + "../storage/src/relational_store_instance.cpp", + "../storage/src/relational_sync_able_storage.cpp", + "../storage/src/relationaldb_properties.cpp", + "../storage/src/result_entries_window.cpp", + "../storage/src/single_ver_natural_store_commit_notify_data.cpp", + "../storage/src/sqlite/query_object.cpp", + "../storage/src/sqlite/query_sync_object.cpp", + "../storage/src/sqlite/relational/sqlite_relational_store.cpp", + "../storage/src/sqlite/relational/sqlite_relational_store_connection.cpp", + "../storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp", + "../storage/src/sqlite/sqlite_local_kvdb.cpp", + "../storage/src/sqlite/sqlite_local_kvdb_connection.cpp", + "../storage/src/sqlite/sqlite_local_kvdb_snapshot.cpp", + "../storage/src/sqlite/sqlite_local_storage_engine.cpp", + "../storage/src/sqlite/sqlite_local_storage_executor.cpp", + "../storage/src/sqlite/sqlite_multi_ver_data_storage.cpp", + "../storage/src/sqlite/sqlite_multi_ver_transaction.cpp", + "../storage/src/sqlite/sqlite_query_helper.cpp", + "../storage/src/sqlite/sqlite_single_ver_continue_token.cpp", + "../storage/src/sqlite/sqlite_single_ver_database_upgrader.cpp", + "../storage/src/sqlite/sqlite_single_ver_forward_cursor.cpp", + "../storage/src/sqlite/sqlite_single_ver_natural_store.cpp", + "../storage/src/sqlite/sqlite_single_ver_natural_store_connection.cpp", + "../storage/src/sqlite/sqlite_single_ver_relational_continue_token.cpp", + "../storage/src/sqlite/sqlite_single_ver_relational_storage_executor.cpp", + "../storage/src/sqlite/sqlite_single_ver_result_set.cpp", + "../storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.cpp", + "../storage/src/sqlite/sqlite_single_ver_storage_engine.cpp", + "../storage/src/sqlite/sqlite_single_ver_storage_executor.cpp", + "../storage/src/sqlite/sqlite_single_ver_storage_executor_cache.cpp", + "../storage/src/sqlite/sqlite_single_ver_storage_executor_subscribe.cpp", + "../storage/src/sqlite/sqlite_storage_engine.cpp", + "../storage/src/sqlite/sqlite_storage_executor.cpp", + "../storage/src/sqlite/sqlite_utils.cpp", + "../storage/src/storage_engine.cpp", + "../storage/src/storage_engine_manager.cpp", + "../storage/src/storage_executor.cpp", + "../storage/src/sync_able_engine.cpp", + "../storage/src/sync_able_kvdb.cpp", + "../storage/src/sync_able_kvdb_connection.cpp", + "../storage/src/upgrader/single_ver_database_upgrader.cpp", + "../storage/src/upgrader/single_ver_schema_database_upgrader.cpp", + "../syncer/src/ability_sync.cpp", + "../syncer/src/commit_history_sync.cpp", + "../syncer/src/communicator_proxy.cpp", + "../syncer/src/db_ability.cpp", + "../syncer/src/device_manager.cpp", + "../syncer/src/generic_syncer.cpp", + "../syncer/src/meta_data.cpp", + "../syncer/src/multi_ver_data_sync.cpp", + "../syncer/src/multi_ver_sync_engine.cpp", + "../syncer/src/multi_ver_sync_state_machine.cpp", + "../syncer/src/multi_ver_sync_task_context.cpp", + "../syncer/src/multi_ver_syncer.cpp", + "../syncer/src/query_sync_water_mark_helper.cpp", + "../syncer/src/single_ver_data_message_schedule.cpp", + "../syncer/src/single_ver_data_packet.cpp", + "../syncer/src/single_ver_data_sync.cpp", + "../syncer/src/single_ver_data_sync_utils.cpp", + "../syncer/src/single_ver_kv_sync_task_context.cpp", + "../syncer/src/single_ver_kv_syncer.cpp", + "../syncer/src/single_ver_relational_sync_task_context.cpp", + "../syncer/src/single_ver_relational_syncer.cpp", + "../syncer/src/single_ver_serialize_manager.cpp", + "../syncer/src/single_ver_sync_engine.cpp", + "../syncer/src/single_ver_sync_state_machine.cpp", + "../syncer/src/single_ver_sync_target.cpp", + "../syncer/src/single_ver_sync_task_context.cpp", + "../syncer/src/single_ver_syncer.cpp", + "../syncer/src/subscribe_manager.cpp", + "../syncer/src/sync_config.cpp", + "../syncer/src/sync_engine.cpp", + "../syncer/src/sync_operation.cpp", + "../syncer/src/sync_state_machine.cpp", + "../syncer/src/sync_target.cpp", + "../syncer/src/sync_task_context.cpp", + "../syncer/src/syncer_factory.cpp", + "../syncer/src/syncer_proxy.cpp", + "../syncer/src/time_helper.cpp", + "../syncer/src/time_sync.cpp", + "../syncer/src/value_slice_sync.cpp", + "unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "unittest/common/common/distributeddb_tools_unit_test.cpp", + "unittest/common/interfaces/process_system_api_adapter_impl.cpp", + "unittest/common/syncer/generic_virtual_device.cpp", + "unittest/common/syncer/kv_virtual_device.cpp", + "unittest/common/syncer/relational_virtual_device.cpp", + "unittest/common/syncer/virtual_communicator.cpp", + "unittest/common/syncer/virtual_communicator_aggregator.cpp", + "unittest/common/syncer/virtual_multi_ver_sync_db_interface.cpp", + "unittest/common/syncer/virtual_single_ver_sync_db_Interface.cpp", + ] + + configs = [ ":module_private_config" ] + + deps = [ + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//third_party/zlib:libz", + "//utils/native/base:utils", + ] + + configs += [ "//third_party/jsoncpp:jsoncpp_config" ] + ldflags = [ "-Wl,--exclude-libs,ALL" ] + deps += [ + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + ] + external_deps = [ + "hisysevent_native:libhisysevent", + "hitrace_native:hitrace_meter", + "hiviewdfx_hilog_native:libhilog", + ] + part_name = "distributeddatamgr" +} + +template("distributeddb_unittest") { + ohos_unittest(target_name) { + forward_variables_from(invoker, "*") + module_out_path = module_output_path + if (!defined(deps)) { + deps = [] + } + if (!defined(external_deps)) { + external_deps = [] + } + configs = [ ":module_private_config" ] + deps += [ + ":src_file", + "//third_party/googletest:gmock_main", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//third_party/zlib:libz", + "//utils/native/base:utils", + ] + configs += [ "//third_party/jsoncpp:jsoncpp_config" ] + ldflags = [ "-Wl,--exclude-libs,ALL" ] + deps += [ + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + ] + external_deps = [ + "hisysevent_native:libhisysevent", + "hitrace_native:hitrace_meter", + "hiviewdfx_hilog_native:libhilog", + ] + } +} + +distributeddb_unittest("DistributedDBSchemalTest") { + sources = [ "unittest/common/common/distributeddb_schema_unit_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesDatabaseTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_database_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBInterfacesDataOperationTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_data_operation_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesEncryptDatabaseTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_encrypt_database_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesEncryptDelegateTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_encrypt_delegate_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesImportAndExportTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_import_and_export_test.cpp" ] +} + +distributeddb_unittest("DistributedDBStorageDataOperationTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_data_operation_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBStorageRegisterConflictTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_register_conflict_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBInterfacesTransactionTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_transaction_test.cpp", + "unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.cpp", + ] +} + +distributeddb_unittest("DistributedDBStorageTransactionDataTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_transaction_data_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBStorageTransactionRecordTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_transaction_record_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBNotificationChainTest") { + sources = + [ "unittest/common/common/distributeddb_notification_chain_test.cpp" ] +} + +distributeddb_unittest("DistributedDBStorageCommitStorageTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_commit_storage_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBInterfacesDataOperationSyncDBTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_data_operation_syncdb_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesRegisterSyncDBTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_register_syncdb_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesTransactionSyncDBTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_transaction_syncdb_test.cpp", + "unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.cpp", + ] +} + +distributeddb_unittest("DistributedDBSingleVerP2PSyncTest") { + sources = + [ "unittest/common/syncer/distributeddb_single_ver_p2p_sync_test.cpp" ] +} + +distributeddb_unittest("DistributedDBSingleVerMsgScheduleTest") { + sources = [ + "unittest/common/syncer/distributeddb_single_ver_msg_schedule_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBInterfacesNBDelegateTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_nb_delegate_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBCommonTest") { + sources = [ "unittest/common/common/distributeddb_common_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesNBDelegateLocalBatchTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_nb_delegate_local_batch_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesTransactionOptimizationTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_transaction_optimization_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesQueryDBTest") { + sources = + [ "unittest/common/interfaces/distributeddb_interfaces_query_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesNBDelegateSchemaPutTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_nb_delegate_schema_put_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesNBTransactionTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_nb_transaction_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesNBPublishTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_nb_publish_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBInterfacesNBUnpublishTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_nb_unpublish_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBInterfacesSpaceManagementTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_space_management_test.cpp" ] +} + +distributeddb_unittest("DistributedDBStorageRegisterObserverTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_register_observer_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBCommunicatorTest") { + sources = [ + "unittest/common/communicator/adapter_stub.cpp", + "unittest/common/communicator/distributeddb_communicator_common.cpp", + "unittest/common/communicator/distributeddb_communicator_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBCommunicatorSendReceiveTest") { + sources = [ + "unittest/common/communicator/adapter_stub.cpp", + "unittest/common/communicator/distributeddb_communicator_common.cpp", + "unittest/common/communicator/distributeddb_communicator_send_receive_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBCommunicatorDeepTest") { + sources = [ + "unittest/common/communicator/adapter_stub.cpp", + "unittest/common/communicator/distributeddb_communicator_common.cpp", + "unittest/common/communicator/distributeddb_communicator_deep_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBSyncerDeviceManagerTest") { + sources = + [ "unittest/common/syncer/distributeddb_syncer_device_manager_test.cpp" ] +} + +distributeddb_unittest("DistributedDBMultiVerP2PSyncTest") { + sources = + [ "unittest/common/syncer/distributeddb_multi_ver_p2p_sync_test.cpp" ] +} + +distributeddb_unittest("DistributedDBStorageSQLiteSingleVerNaturalStoreTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.cpp", + "unittest/common/storage/distributeddb_storage_sqlite_single_ver_natural_store_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBStorageMemorySingleVerNaturalStoreTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_memory_single_ver_naturall_store_test.cpp", + "unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.cpp", + ] +} + +distributeddb_unittest("DistributedDBEventLoopTimerTest") { + sources = [ "unittest/common/common/evloop_timer_unit_test.cpp" ] +} + +distributeddb_unittest("DistributedDBTimeSyncTest") { + sources = [ + "unittest/common/syncer/distributeddb_time_sync_test.cpp", + "unittest/common/syncer/virtual_time_sync_communicator.cpp", + ] +} + +distributeddb_unittest("DistributedDBDeviceIdentifierTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_device_identifier_test.cpp" ] +} + +distributeddb_unittest("DistributedDBSingleVersionResultSetTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_single_version_result_set_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesDatabaseCorruptTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_database_corrupt_test.cpp" ] +} + +distributeddb_unittest("DistributedDBFilePackageTest") { + sources = [ "unittest/common/storage/distributeddb_file_package_test.cpp" ] +} + +distributeddb_unittest("DistributedDBMultiVerVacuumTest") { + sources = [ + "unittest/common/storage/distributeddb_multi_ver_vacuum_test.cpp", + "unittest/common/storage/multi_ver_vacuum_executor_stub.cpp", + ] +} + +distributeddb_unittest("DistributedDBParcelTest") { + sources = [ "unittest/common/common/distributeddb_parcel_unit_test.cpp" ] +} + +distributeddb_unittest("DistributedDBAbilitySyncTest") { + sources = [ "unittest/common/syncer/distributeddb_ability_sync_test.cpp" ] +} + +distributeddb_unittest("DistributedDBSchemaObjectTest") { + sources = [ "unittest/common/common/distributeddb_schema_object_test.cpp" ] +} + +distributeddb_unittest("DistributedDBStorageSingleVerUpgradeTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_single_ver_upgrade_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBSqliteRegisterTest") { + sources = [ "unittest\common\storage\distributeddb_sqlite_register_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesAutoLaunchTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_auto_launch_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBInterfacesIndexUnitTest") { + sources = [ + "unittest\common\interfaces\distributeddb_interfaces_index_unit_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBAutoLaunchUnitTest") { + sources = [ "unittest/common/common/distributeddb_auto_launch_test.cpp" ] +} + +distributeddb_unittest("DistributedDBDataCompressionTest") { + sources = [ "unittest/common/common/distributeddb_data_compression_test.cpp" ] +} + +############################################################################### +distributeddb_unittest("DistributedDBJsonPrecheckUnitTest") { + sources = + [ "unittest/common/common/distributeddb_json_precheck_unit_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesNBResultsetPerfTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_resultset_performance.cpp" ] +} + +distributeddb_unittest("DistributedDBStorageResultAndJsonOptimizeTest") { + sources = [ "unittest/common/storage/distributeddb_storage_resultset_and_json_optimize.cpp" ] +} + +distributeddb_unittest("DistributedDBStorageIndexOptimizeTest") { + sources = [ + "unittest/common/storage/distributeddb_storage_index_optimize_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBSingleVerP2PSyncCheckTest") { + sources = [ + "unittest/common/syncer/distributeddb_single_ver_p2p_sync_check_test.cpp", + ] +} + +distributeddb_unittest("RuntimeContextProcessSystemApiAdapterImplTest") { + sources = [ "unittest/common/interfaces/runtime_context_process_system_api_adapter_impl_test.cpp" ] +} + +distributeddb_unittest("DistributedDBInterfacesSchemaDatabaseUpgradeTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_schema_database_upgrade_test.cpp" ] +} + +distributeddb_unittest("DistributedDBStorageQuerySyncTest") { + sources = + [ "unittest/common/storage/distributeddb_storage_query_sync_test.cpp" ] +} + +distributeddb_unittest("DistributedDBSingleVerP2PQuerySyncTest") { + sources = [ + "unittest/common/syncer/distributeddb_single_ver_p2p_query_sync_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBCommunicatorProxyTest") { + sources = + [ "unittest/common/syncer/distributeddb_communicator_proxy_test.cpp" ] +} + +distributeddb_unittest("DistributedDBSingleVerP2PSubscribeSyncTest") { + sources = [ "unittest/common/syncer/distributeddb_single_ver_p2p_subsribe_sync_test.cpp" ] +} + +distributeddb_unittest("DistributedDBMockSyncModuleTest") { + sources = [ "unittest/common/syncer/distributeddb_mock_sync_module_test.cpp" ] +} + +distributeddb_unittest("DistributedInterfacesRelationalTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_relational_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBRelationalSchemaObjectTest") { + sources = [ + "unittest/common/common/distributeddb_relational_schema_object_test.cpp", + ] +} + +distributeddb_unittest("DistributedDBInterfacesRelationalSyncTest") { + sources = [ "unittest/common/interfaces/distributeddb_interfaces_relational_sync_test.cpp" ] +} + +distributeddb_unittest("DistributedDBRelationalGetDataTest") { + sources = + [ "unittest/common/storage/distributeddb_relational_get_data_test.cpp" ] +} + +distributeddb_unittest("DistributedDBSingleVerMultiUserTest") { + sources = + [ "unittest/common/syncer/distributeddb_single_ver_multi_user_test.cpp" ] +} + +############################################################################### +group("unittest") { + testonly = true + deps = [ "//third_party/googletest:gmock" ] + + deps += [ + ":DistributedDBAbilitySyncTest", + ":DistributedDBAutoLaunchUnitTest", + ":DistributedDBCommonTest", + ":DistributedDBCommunicatorDeepTest", + ":DistributedDBCommunicatorProxyTest", + ":DistributedDBCommunicatorSendReceiveTest", + ":DistributedDBCommunicatorTest", + ":DistributedDBDeviceIdentifierTest", + ":DistributedDBEventLoopTimerTest", + ":DistributedDBFilePackageTest", + ":DistributedDBInterfacesAutoLaunchTest", + ":DistributedDBInterfacesDataOperationSyncDBTest", + ":DistributedDBInterfacesDataOperationTest", + ":DistributedDBInterfacesDatabaseCorruptTest", + ":DistributedDBInterfacesDatabaseTest", + ":DistributedDBInterfacesEncryptDatabaseTest", + ":DistributedDBInterfacesEncryptDelegateTest", + ":DistributedDBInterfacesImportAndExportTest", + ":DistributedDBInterfacesIndexUnitTest", + ":DistributedDBInterfacesNBDelegateLocalBatchTest", + ":DistributedDBInterfacesNBDelegateSchemaPutTest", + ":DistributedDBInterfacesNBDelegateTest", + ":DistributedDBInterfacesNBPublishTest", + ":DistributedDBInterfacesNBResultsetPerfTest", + ":DistributedDBInterfacesNBTransactionTest", + ":DistributedDBInterfacesNBUnpublishTest", + ":DistributedDBInterfacesQueryDBTest", + ":DistributedDBInterfacesRegisterSyncDBTest", + ":DistributedDBInterfacesRelationalSyncTest", + ":DistributedDBInterfacesSchemaDatabaseUpgradeTest", + ":DistributedDBInterfacesSpaceManagementTest", + ":DistributedDBInterfacesTransactionOptimizationTest", + ":DistributedDBInterfacesTransactionSyncDBTest", + ":DistributedDBInterfacesTransactionTest", + ":DistributedDBJsonPrecheckUnitTest", + ":DistributedDBMockSyncModuleTest", + ":DistributedDBMultiVerP2PSyncTest", + ":DistributedDBMultiVerVacuumTest", + ":DistributedDBNotificationChainTest", + ":DistributedDBParcelTest", + ":DistributedDBRelationalGetDataTest", + ":DistributedDBRelationalSchemaObjectTest", + ":DistributedDBSchemaObjectTest", + ":DistributedDBSchemalTest", + ":DistributedDBSingleVerMsgScheduleTest", + ":DistributedDBSingleVerMultiUserTest", + ":DistributedDBSingleVerP2PQuerySyncTest", + ":DistributedDBSingleVerP2PSubscribeSyncTest", + ":DistributedDBSingleVerP2PSyncCheckTest", + ":DistributedDBSingleVerP2PSyncTest", + ":DistributedDBSingleVersionResultSetTest", + ":DistributedDBSqliteRegisterTest", + ":DistributedDBStorageCommitStorageTest", + ":DistributedDBStorageDataOperationTest", + ":DistributedDBStorageIndexOptimizeTest", + ":DistributedDBStorageMemorySingleVerNaturalStoreTest", + ":DistributedDBStorageQuerySyncTest", + ":DistributedDBStorageRegisterConflictTest", + ":DistributedDBStorageRegisterObserverTest", + ":DistributedDBStorageResultAndJsonOptimizeTest", + ":DistributedDBStorageSQLiteSingleVerNaturalStoreTest", + ":DistributedDBStorageSingleVerUpgradeTest", + ":DistributedDBStorageTransactionDataTest", + ":DistributedDBStorageTransactionRecordTest", + ":DistributedDBSyncerDeviceManagerTest", + ":DistributedDBTimeSyncTest", + ":DistributedInterfacesRelationalTest", + ":RuntimeContextProcessSystemApiAdapterImplTest", + ] +} + +############################################################################### + +group("distributeddatamgr_fuzztest") { + testonly = true + deps = [] + deps += [ + "fuzztest/delegate_fuzzer:fuzztest", + "fuzztest/fileoper_fuzzer:fuzztest", + "fuzztest/importfile_fuzzer:fuzztest", + "fuzztest/iprocesscommunicator_fuzzer:fuzztest", + "fuzztest/kvstoreresultset_fuzzer:fuzztest", + "fuzztest/nbdelegate_fuzzer:fuzztest", + "fuzztest/parseckeck_fuzzer:fuzztest", + "fuzztest/query_fuzzer:fuzztest", + "fuzztest/rekey_fuzzer:fuzztest", + ] +} +############################################################################### diff --git a/mock/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp b/mock/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..784569361778c0b13a01c4678a34683bc331d603 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "distributeddb_tools_test.h" +#include +#include +#include +#include +#include +#include +#include + +#include "db_common.h" +#include "db_constant.h" +#include "generic_single_ver_kv_entry.h" +#include "platform_specific.h" +#include "single_ver_data_packet.h" +#include "value_hash_calc.h" + +using namespace DistributedDB; +namespace DistributedDBTest { +int DistributedDBToolsTest::GetCurrentDir(std::string &dir) +{ + static const int maxFileLength = 1024; + dir = ""; + char buffer[maxFileLength] = {0}; + int length = readlink("/proc/self/exe", buffer, maxFileLength); + if (length < 0 || length >= maxFileLength) { + LOGE("read directory err length:%d", length); + return -E_LENGTH_ERROR; + } + LOGD("DIR = %s", buffer); + dir = buffer; + if (std::string::npos == dir.rfind("/") && std::string::npos == dir.rfind("\\")) { + LOGE("current patch format err"); + return -E_INVALID_PATH; + } + + if (dir.rfind("/") != std::string::npos) { + dir.erase(dir.rfind("/") + 1); + } + return E_OK; +} + +void DistributedDBToolsTest::TestDirInit(std::string& dir) +{ + if (GetCurrentDir(dir) != E_OK) { + dir = "/"; + } + + dir.append("testDbDir"); + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + if (OS::MakeDBDirectory(dir) != 0) { + LOGI("MakeDirectory err!"); + dir = "/"; + return; + } + } else { + closedir(dirTmp); + } +} + +int DistributedDBToolsTest::RemoveTestDbFiles(const std::string& dir) +{ + bool isExisted = OS::CheckPathExistence(dir); + if (!isExisted) { + return E_OK; + } + + int nFile = 0; + std::string dirName; + struct dirent *direntPtr = nullptr; + DIR *dirPtr = opendir(dir.c_str()); + if (dirPtr == nullptr) { + LOGE("opendir error!"); + return -E_INVALID_PATH; + } + while (true) { + direntPtr = readdir(dirPtr); + // condition to exit the loop + if (direntPtr == nullptr) { + break; + } + // only remove all *.db files + std::string str(direntPtr->d_name); + if (str == "." || str == "..") { + continue; + } + dirName.clear(); + dirName.append(dir).append("/").append(str); + if (direntPtr->d_type == DT_DIR) { + RemoveTestDbFiles(dirName); + rmdir(dirName.c_str()); + } else if (remove(dirName.c_str()) != 0) { + LOGI("remove file: %s failed!", dirName.c_str()); + continue; + } + nFile++; + } + closedir(dirPtr); + LOGI("Total %d test db files are removed!", nFile); + return 0; +} + +void DistributedDBToolsTest::GetRandomKeyValue(std::vector &value, uint32_t defaultSize) +{ + uint32_t randSize = 0; + if (defaultSize == 0) { + uint8_t simSize = 0; + RAND_bytes(&simSize, 1); + randSize = (simSize == 0) ? 1 : simSize; + } else { + randSize = defaultSize; + } + + value.resize(randSize); + RAND_bytes(value.data(), randSize); +} + +KvStoreObserverTest::KvStoreObserverTest() : callCount_(0), isCleared_(false) +{} + +void KvStoreObserverTest::OnChange(const KvStoreChangedData& data) +{ + callCount_++; + inserted_ = data.GetEntriesInserted(); + updated_ = data.GetEntriesUpdated(); + deleted_ = data.GetEntriesDeleted(); + isCleared_ = data.IsCleared(); + LOGD("Onchangedata :%zu -- %zu -- %zu -- %d", inserted_.size(), updated_.size(), deleted_.size(), isCleared_); + LOGD("Onchange() called success!"); +} +} \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/common/distributeddb_tools_test.h b/mock/distributeddb/test/fuzztest/common/distributeddb_tools_test.h new file mode 100644 index 0000000000000000000000000000000000000000..c47f71b6b5b309371e1d06e110344b0bead4bf7a --- /dev/null +++ b/mock/distributeddb/test/fuzztest/common/distributeddb_tools_test.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_TOOLS_TEST_H +#define DISTRIBUTEDDB_TOOLS_TEST_H + +#include +#include +#include +#include +#include +#include + +#include "db_types.h" +#include "kv_store_changed_data.h" +#include "kv_store_delegate_impl.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_observer.h" +#include "log_print.h" +#include "message.h" +#include "query.h" + +namespace DistributedDBTest { +class DistributedDBToolsTest final { +public: + DistributedDBToolsTest() {} + ~DistributedDBToolsTest() {} + + static void TestDirInit(std::string &); + // remove the test db files in the test directory of dir. + static int RemoveTestDbFiles(const std::string &); + static int GetCurrentDir(std::string& dir); + static void GetRandomKeyValue(std::vector &value, uint32_t defaultSize = 0); +}; + +class KvStoreObserverTest : public DistributedDB::KvStoreObserver { +public: + KvStoreObserverTest(); + ~KvStoreObserverTest() {} + + // callback function will be called when the db data is changed. + void OnChange(const DistributedDB::KvStoreChangedData&); + +private: + unsigned long callCount_; + bool isCleared_; + std::list inserted_; + std::list updated_; + std::list deleted_; +}; +} // namespace DistributedDBTest +#endif // DISTRIBUTEDDB_TOOLS_TEST_H \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/delegate_fuzzer/BUILD.gn b/mock/distributeddb/test/fuzztest/delegate_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..8e12dd17cca35515cc690873d95c926e805d6270 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/delegate_fuzzer/BUILD.gn @@ -0,0 +1,103 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("DelegateFuzzTest") { + module_out_path = "distributeddatamgr/distributeddb" + + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/sqlite", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/multiver", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/src", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include/relational", + "//third_party/jsoncpp/include/json", + "//third_party/skia/third_party/externals/spirv-headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/skia/third_party/externals/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/jsoncpp/include/json", + "//third_party/grpc/src/core/lib/json", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + fuzz_config_file = "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/delegate_fuzzer" + + sources = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "delegate_fuzzer.cpp", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + "//utils/native/base:utils", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### + +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":DelegateFuzzTest", + ] +} diff --git a/mock/distributeddb/test/fuzztest/delegate_fuzzer/corpus/init b/mock/distributeddb/test/fuzztest/delegate_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..bc977bd9738ee9a70b362067f57a9c63d3adb801 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/delegate_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/delegate_fuzzer/delegate_fuzzer.cpp b/mock/distributeddb/test/fuzztest/delegate_fuzzer/delegate_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d526c52b9d53f0d77ab2f970865477e78d0f876 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/delegate_fuzzer/delegate_fuzzer.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "delegate_fuzzer.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_test.h" + +using namespace DistributedDB; +using namespace DistributedDBTest; + +namespace OHOS { +std::vector CreateEntries(const uint8_t* data, size_t size, std::vector& keys) +{ + std::vector entries; + // key'length is less than 1024. + auto count = static_cast(std::min(size, size_t(1024))); + for (int i = 1; i < count; i++) { + Entry entry; + entry.key = std::vector(data, data + 1); + entry.value = std::vector(data, data + size); + keys.push_back(entry.key); + entries.push_back(entry); + } + return entries; +} + +void MultiCombineFuzzer(const uint8_t* data, size_t size, KvStoreDelegate::Option &option) +{ + static auto kvManger = KvStoreDelegateManager("APP_ID", "USER_ID"); + KvStoreConfig config; + DistributedDBToolsTest::TestDirInit(config.dataDir); + kvManger.SetKvStoreConfig(config); + KvStoreDelegate *kvDelegatePtr = nullptr; + kvManger.GetKvStore("distributed_delegate_test", option, + [&kvDelegatePtr](DBStatus status, KvStoreDelegate* kvDelegate) { + if (status == DBStatus::OK) { + kvDelegatePtr = kvDelegate; + } + }); + KvStoreObserverTest *observer = new (std::nothrow) KvStoreObserverTest; + if ((kvDelegatePtr == nullptr) || (observer == nullptr)) { + return; + } + + kvDelegatePtr->RegisterObserver(observer); + Key key = std::vector(data, data + (size % 1024)); /* 1024 is max */ + Value value = std::vector(data, data + size); + kvDelegatePtr->Put(key, value); + KvStoreSnapshotDelegate* kvStoreSnapshotPtr = nullptr; + kvDelegatePtr->GetKvStoreSnapshot(nullptr, + [&kvStoreSnapshotPtr](DBStatus status, KvStoreSnapshotDelegate* kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + if (kvStoreSnapshotPtr == nullptr) { + return; + } + auto valueCallback = [&value] (DBStatus status, const Value &getValue) { + value = getValue; + }; + + kvStoreSnapshotPtr->Get(key, valueCallback); + kvDelegatePtr->Delete(key); + kvStoreSnapshotPtr->Get(key, valueCallback); + std::vector keys; + kvDelegatePtr->PutBatch(CreateEntries(data, size, keys)); + Key keyPrefix = std::vector(data, data + 1); + kvStoreSnapshotPtr->GetEntries(keyPrefix, [](DBStatus status, const std::vector &entries) { + (void) entries.size(); + }); + + kvDelegatePtr->DeleteBatch(keys); + kvDelegatePtr->Clear(); + kvDelegatePtr->UnRegisterObserver(observer); + kvDelegatePtr->ReleaseKvStoreSnapshot(kvStoreSnapshotPtr); + kvManger.CloseKvStore(kvDelegatePtr); + kvManger.DeleteKvStore("distributed_delegate_test"); + DistributedDBToolsTest::RemoveTestDbFiles(config.dataDir); +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + CipherPassword passwd; + KvStoreDelegate::Option option = {true, true, false, CipherType::DEFAULT, passwd}; + OHOS::MultiCombineFuzzer(data, size, option); + option = {true, false, false, CipherType::DEFAULT, passwd}; + OHOS::MultiCombineFuzzer(data, size, option); + return 0; +} + diff --git a/mock/distributeddb/test/fuzztest/delegate_fuzzer/delegate_fuzzer.h b/mock/distributeddb/test/fuzztest/delegate_fuzzer/delegate_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..0c3b6b65b97c468475217b83a49e45a7e941ddad --- /dev/null +++ b/mock/distributeddb/test/fuzztest/delegate_fuzzer/delegate_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EDELEGATE_FUZZER_H +#define EDELEGATE_FUZZER_H + +#define FUZZ_PROJECT_NAME "Delegate_fuzzer" + +#endif // EDELEGATE_FUZZER_H diff --git a/mock/distributeddb/test/fuzztest/delegate_fuzzer/project.xml b/mock/distributeddb/test/fuzztest/delegate_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..be92381e05fc528d2fba5603365ee6df71b5b840 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/delegate_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 30 + + 4096 + + diff --git a/mock/distributeddb/test/fuzztest/fileoper_fuzzer/BUILD.gn b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..25bdaf6ce394667d59f1901a361e2e5a8611f9e8 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/BUILD.gn @@ -0,0 +1,104 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("FileOperFuzzTest") { + module_out_path = "distributeddatamgr/distributeddb" + + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/sqlite", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/multiver", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include/relational", + "//third_party/jsoncpp/include/json", + "//third_party/skia/third_party/externals/spirv-headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/skia/third_party/externals/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/jsoncpp/include/json", + "//third_party/grpc/src/core/lib/json", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//third_party/googletest/googletest/include/gtest", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + fuzz_config_file = "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/fileoper_fuzzer" + + sources = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "fileoper_fuzzer.cpp", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + "//utils/native/base:utils", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":FileOperFuzzTest", + ] +} +############################################################################### diff --git a/mock/distributeddb/test/fuzztest/fileoper_fuzzer/corpus/init b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..2f20d95e05f2a7d94f3ac5d1eb0288a5f3e3965e --- /dev/null +++ b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/corpus/init @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +FUZZ \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/fileoper_fuzzer/fileoper_fuzzer.cpp b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/fileoper_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..05532d00d325727634e874c8ca605cd81560bd7f --- /dev/null +++ b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/fileoper_fuzzer.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fileoper_fuzzer.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_test.h" +#include "process_communicator_test_stub.h" + +using namespace DistributedDB; +using namespace DistributedDBTest; + +namespace OHOS { +static auto g_kvManager = KvStoreDelegateManager("APP_ID", "USER_ID"); +std::vector CreateEntries(const uint8_t* data, size_t size) +{ + std::vector entries; + auto count = static_cast(std::min(size, size_t(1024))); + for (int i = 1; i < count; i++) { + Entry entry; + entry.key = std::vector (data, data + i); + entry.value = std::vector (data, data + size); + entries.push_back(entry); + } + return entries; +} + +void SingerVerExportAndImport(const uint8_t* data, size_t size, const std::string& testDir) +{ + KvStoreNbDelegate::Option nbOption = {true, false, true}; + KvStoreNbDelegate *kvNbDelegatePtr = nullptr; + + g_kvManager.GetKvStore("distributed_file_oper_single", nbOption, + [&kvNbDelegatePtr](DBStatus status, KvStoreNbDelegate* kvNbDelegate) { + if (status == DBStatus::OK) { + kvNbDelegatePtr = kvNbDelegate; + } + }); + if (kvNbDelegatePtr == nullptr) { + return; + } + kvNbDelegatePtr->PutBatch(CreateEntries(data, size)); + + std::string rawString(reinterpret_cast(data), size); + std::string singleExportFileName = testDir + "/" + rawString; + + CipherPassword passwd; + passwd.SetValue(data, size); + kvNbDelegatePtr->Export(singleExportFileName, passwd); + kvNbDelegatePtr->Import(singleExportFileName, passwd); + + g_kvManager.CloseKvStore(kvNbDelegatePtr); + g_kvManager.DeleteKvStore("distributed_file_oper_single"); +} + +void MultiVerExportAndImport(const uint8_t* data, size_t size, const std::string& testDir) +{ + CipherPassword passwd; + passwd.SetValue(data, size); + KvStoreDelegate::Option option = {true, false, true, CipherType::DEFAULT, passwd}; + KvStoreDelegate *kvDelegatePtr = nullptr; + g_kvManager.GetKvStore("distributed_file_oper_multi", option, + [&kvDelegatePtr](DBStatus status, KvStoreDelegate* kvStoreDelegate) { + if (status == DBStatus::OK) { + kvDelegatePtr = kvStoreDelegate; + } + }); + if (kvDelegatePtr == nullptr) { + return; + } + kvDelegatePtr->PutBatch(CreateEntries(data, size)); + std::string rawString(reinterpret_cast(data), size); + std::string multiExportFileName = testDir + "/" + rawString; + kvDelegatePtr->Export(multiExportFileName, passwd); + kvDelegatePtr->Import(multiExportFileName, passwd); + g_kvManager.CloseKvStore(kvDelegatePtr); + g_kvManager.DeleteKvStore("distributed_file_oper_multi"); +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + KvStoreConfig config; + DistributedDBToolsTest::TestDirInit(config.dataDir); + OHOS::g_kvManager.SetKvStoreConfig(config); + OHOS::g_kvManager.SetProcessLabel("FUZZ", "DISTRIBUTEDDB"); + OHOS::g_kvManager.SetProcessCommunicator(std::make_shared()); + OHOS::SingerVerExportAndImport(data, size, config.dataDir); + OHOS::MultiVerExportAndImport(data, size, config.dataDir); + DistributedDBToolsTest::RemoveTestDbFiles(config.dataDir); + return 0; +} + diff --git a/mock/distributeddb/test/fuzztest/fileoper_fuzzer/fileoper_fuzzer.h b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/fileoper_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..d22d0e88dbcdface37b2f8315eeba765ae565af9 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/fileoper_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FILEOPER_FUZZER_H +#define FILEOPER_FUZZER_H + +#define FUZZ_PROJECT_NAME "fileoper_fuzzer" + +#endif // FILEOPER_FUZZER_H diff --git a/mock/distributeddb/test/fuzztest/fileoper_fuzzer/project.xml b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..be92381e05fc528d2fba5603365ee6df71b5b840 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/fileoper_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 30 + + 4096 + + diff --git a/mock/distributeddb/test/fuzztest/importfile_fuzzer/BUILD.gn b/mock/distributeddb/test/fuzztest/importfile_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..e404fe105ca2aeaaa9f8ead0b4919b9325428ef9 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/importfile_fuzzer/BUILD.gn @@ -0,0 +1,103 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("ImportFileFuzzTest") { + module_out_path = "distributeddatamgr/distributeddb" + + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/sqlite", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/multiver", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/src", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include/relational", + "//third_party/jsoncpp/include/json", + "//third_party/skia/third_party/externals/spirv-headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/skia/third_party/externals/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/jsoncpp/include/json", + "//third_party/grpc/src/core/lib/json", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + fuzz_config_file = "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/importfile_fuzzer" + + sources = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "importfile_fuzzer.cpp", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + "//utils/native/base:utils", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### + +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":ImportFileFuzzTest", + ] +} diff --git a/mock/distributeddb/test/fuzztest/importfile_fuzzer/corpus/init b/mock/distributeddb/test/fuzztest/importfile_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..bc977bd9738ee9a70b362067f57a9c63d3adb801 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/importfile_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/importfile_fuzzer/importfile_fuzzer.cpp b/mock/distributeddb/test/fuzztest/importfile_fuzzer/importfile_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e8e73c69fb3af434354c0736a5dee61988e2678 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/importfile_fuzzer/importfile_fuzzer.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "importfile_fuzzer.h" +#include +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_test.h" +#include "platform_specific.h" +#include "process_communicator_test_stub.h" + +using namespace DistributedDB; +using namespace DistributedDBTest; +using namespace std; + +static KvStoreConfig g_config; + +namespace OHOS { +void SingerVerImport(const uint8_t *data, size_t size, const std::string &importFile) +{ + static auto kvManager = KvStoreDelegateManager("APP_ID", "USER_ID"); + kvManager.SetKvStoreConfig(g_config); + kvManager.SetProcessLabel("FUZZ", "DISTRIBUTEDDB"); + kvManager.SetProcessCommunicator(std::make_shared()); + CipherPassword passwd; + passwd.SetValue(data, size); + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, passwd}; + + KvStoreNbDelegate *kvNbDelegatePtr = nullptr; + kvManager.GetKvStore("distributed_import_single", option, + [&kvNbDelegatePtr](DBStatus status, KvStoreNbDelegate* kvNbDelegate) { + if (status == DBStatus::OK) { + kvNbDelegatePtr = kvNbDelegate; + } + }); + if (kvNbDelegatePtr == nullptr) { + return; + } + + kvNbDelegatePtr->Import(importFile, passwd); + kvManager.CloseKvStore(kvNbDelegatePtr); + kvManager.DeleteKvStore("distributed_import_single"); +} + +bool MakeImportFile(const uint8_t *data, size_t size, const std::string &realPath) +{ + std::ofstream ofs(realPath, std::ofstream::out); + if (!ofs.is_open()) { + LOGE("the file open failed"); + return false; + } + ofs.write(reinterpret_cast(data), size); + return true; +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + std::string dataDir; + DistributedDBToolsTest::TestDirInit(dataDir); + g_config.dataDir = dataDir; + std::string path = dataDir + "/fuzz" + std::to_string(U16_AT(data)); + std::string realPath; + OS::GetRealPath(path, realPath); + if (OHOS::MakeImportFile(data, size, realPath)) { + OHOS::SingerVerImport(data, size, realPath); + } + + DistributedDBToolsTest::RemoveTestDbFiles(dataDir); + return 0; +} + diff --git a/mock/distributeddb/test/fuzztest/importfile_fuzzer/importfile_fuzzer.h b/mock/distributeddb/test/fuzztest/importfile_fuzzer/importfile_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..4e36950e4a765eac9f19bf7744214451356ea817 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/importfile_fuzzer/importfile_fuzzer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IMPORTFILE_FUZZER_H +#define IMPORTFILE_FUZZER_H + +#define FUZZ_PROJECT_NAME "ImportFile_fuzzer" + +#include + +uint16_t U16_AT(const uint8_t * const &ptr) +{ + // 8 - 0 + return (ptr[0] << 8) | ptr[1]; +} + +uint32_t U32_AT(const uint8_t * const &ptr) +{ + // 24 - 16 - 8 - 0 + return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; +} + +#endif // IMPORTFILE_FUZZER_H diff --git a/mock/distributeddb/test/fuzztest/importfile_fuzzer/project.xml b/mock/distributeddb/test/fuzztest/importfile_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..be92381e05fc528d2fba5603365ee6df71b5b840 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/importfile_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 30 + + 4096 + + diff --git a/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/BUILD.gn b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..94e6a46a76ff98be0ace0b59dd057c3eb8f494f6 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/BUILD.gn @@ -0,0 +1,102 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("IProcessCommunicatorFuzzTest") { + module_out_path = "distributeddatamgr/distributeddb" + + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/sqlite", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/multiver", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/src", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include/relational", + "//third_party/jsoncpp/include/json", + "//third_party/skia/third_party/externals/spirv-headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/skia/third_party/externals/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/jsoncpp/include/json", + "//third_party/grpc/src/core/lib/json", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + fuzz_config_file = "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer" + + sources = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp", + "iprocesscommunicator_fuzzer.cpp", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + "//utils/native/base:utils", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### + +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":IProcessCommunicatorFuzzTest", + ] +} diff --git a/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/corpus/init b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..bc977bd9738ee9a70b362067f57a9c63d3adb801 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/iprocesscommunicator_fuzzer.cpp b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/iprocesscommunicator_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ddaf1cd49ce84240339d075356c2cfde2e52429 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/iprocesscommunicator_fuzzer.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "iprocesscommunicator_fuzzer.h" +#include +#include "iprocess_communicator.h" +#include "distributeddb_tools_test.h" + +using namespace DistributedDB; +using namespace DistributedDBTest; + +class IProcessCommunicatorFuzzer { + /* Keep C++ file names the same as the class name */ +}; + +namespace OHOS { +class ProcessCommunicatorFuzzTest : public DistributedDB::IProcessCommunicator { +public: + ProcessCommunicatorFuzzTest() {} + ~ProcessCommunicatorFuzzTest() {} + DistributedDB::DBStatus Start(const std::string &processLabel) override + { + return DistributedDB::OK; + } + // The Stop should only be called after Start successfully + DistributedDB::DBStatus Stop() override + { + return DistributedDB::OK; + } + DistributedDB::DBStatus RegOnDeviceChange(const DistributedDB::OnDeviceChange &callback) override + { + onDeviceChange_ = callback; + return DistributedDB::OK; + } + DistributedDB::DBStatus RegOnDataReceive(const DistributedDB::OnDataReceive &callback) override + { + onDataReceive_ = callback; + return DistributedDB::OK; + } + DistributedDB::DBStatus SendData(const DistributedDB::DeviceInfos &dstDevInfo, + const uint8_t *datas, uint32_t length) override + { + return DistributedDB::OK; + } + uint32_t GetMtuSize() override + { + return 1 * 1024 * 1024; // 1 * 1024 * 1024 Byte. + } + DistributedDB::DeviceInfos GetLocalDeviceInfos() override + { + DistributedDB::DeviceInfos info; + info.identifier = "default"; + return info; + } + + std::vector GetRemoteOnlineDeviceInfosList() override + { + std::vector info; + return info; + } + + bool IsSameProcessLabelStartedOnPeerDevice(const DistributedDB::DeviceInfos &peerDevInfo) override + { + return true; + } + + void FuzzOnDeviceChange(const DistributedDB::DeviceInfos &devInfo, bool isOnline) + { + if (onDeviceChange_ == nullptr) { + return; + } + onDeviceChange_(devInfo, isOnline); + } + + void FuzzOnDataReceive(const DistributedDB::DeviceInfos &devInfo, const uint8_t* data, size_t size) + { + if (onDataReceive_ == nullptr) { + return; + } + onDataReceive_(devInfo, data, size); + } + +private: + DistributedDB::OnDeviceChange onDeviceChange_ = nullptr; + DistributedDB::OnDataReceive onDataReceive_ = nullptr; +}; + +void CommunicatorFuzzer(const uint8_t* data, size_t size) +{ + static auto kvManager = KvStoreDelegateManager("APP_ID", "USER_ID"); + std::string rawString(reinterpret_cast(data), size); + KvStoreDelegateManager::SetProcessLabel(rawString, "defaut"); + auto communicator = std::make_shared(); + KvStoreDelegateManager::SetProcessCommunicator(communicator); + std::string testDir; + DistributedDBToolsTest::TestDirInit(testDir); + KvStoreConfig config; + config.dataDir = testDir; + kvManager.SetKvStoreConfig(config); + KvStoreNbDelegate::Option option = {true, false, false}; + KvStoreNbDelegate *kvNbDelegatePtr = nullptr; + kvManager.GetKvStore(rawString, option, + [&kvNbDelegatePtr](DBStatus status, KvStoreNbDelegate* kvNbDelegate) { + if (status == DBStatus::OK) { + kvNbDelegatePtr = kvNbDelegate; + } + }); + DeviceInfos device = {"defaut"}; + communicator->FuzzOnDataReceive(device, data, size); + if (kvNbDelegatePtr != nullptr) { + kvManager.CloseKvStore(kvNbDelegatePtr); + kvManager.DeleteKvStore(rawString); + } +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + // 4 bytes is required + if (size < 4) { + return 0; + } + OHOS::CommunicatorFuzzer(data, size); + return 0; +} diff --git a/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/iprocesscommunicator_fuzzer.h b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/iprocesscommunicator_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..dd1cd5e5aa592705d0f424d838c4f71ef0797b59 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/iprocesscommunicator_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IPROCESSCOMMUNICATOR_FUZZER_H +#define IPROCESSCOMMUNICATOR_FUZZER_H + +#define FUZZ_PROJECT_NAME "IprocessCommunicator_fuzzer" + +#endif // IPROCESSCOMMUNICATOR_FUZZER_H diff --git a/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/project.xml b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..be92381e05fc528d2fba5603365ee6df71b5b840 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/iprocesscommunicator_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 30 + + 4096 + + diff --git a/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/BUILD.gn b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..ecb5962b6127dfedf2013d165c729e24106e2b6e --- /dev/null +++ b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/BUILD.gn @@ -0,0 +1,103 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("KvStoreResultSetFuzzTest") { + module_out_path = "distributeddatamgr/distributeddb" + + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/sqlite", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/multiver", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/src", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include/relational", + "//third_party/jsoncpp/include/json", + "//third_party/skia/third_party/externals/spirv-headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/skia/third_party/externals/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/jsoncpp/include/json", + "//third_party/grpc/src/core/lib/json", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + fuzz_config_file = "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/kvstoreresultset_fuzzer" + + sources = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "kvstoreresultset_fuzzer.cpp", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + "//utils/native/base:utils", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### + +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":KvStoreResultSetFuzzTest", + ] +} diff --git a/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/corpus/init b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..bc977bd9738ee9a70b362067f57a9c63d3adb801 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/kvstoreresultset_fuzzer.cpp b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/kvstoreresultset_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f88b8ded2b7745b85cfffd9372e4a3445b6dd383 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/kvstoreresultset_fuzzer.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kvstoreresultset_fuzzer.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_test.h" +#include "process_communicator_test_stub.h" + +using namespace DistributedDB; +using namespace DistributedDBTest; +using namespace std; + +namespace OHOS { +static auto g_kvManager = KvStoreDelegateManager("APP_ID", "USER_ID"); +void ResultSetFuzzer(const uint8_t* data, size_t size) +{ + KvStoreNbDelegate::Option option = {true, false, true}; + KvStoreNbDelegate *kvNbDelegatePtr = nullptr; + + g_kvManager.GetKvStore("distributed_nb_delegate_result_set_test", option, + [&kvNbDelegatePtr](DBStatus status, KvStoreNbDelegate * kvNbDelegate) { + if (status == DBStatus::OK) { + kvNbDelegatePtr = kvNbDelegate; + } + }); + if (kvNbDelegatePtr == nullptr) { + return; + } + + Key testKey; + Value testValue; + for (size_t i = 0; i < size; i++) { + testKey.clear(); + testValue.clear(); + testKey.push_back(data[i]); + testValue.push_back(data[i]); + kvNbDelegatePtr->Put(testKey, testValue); + } + + Key keyPrefix; + KvStoreResultSet *readResultSet = nullptr; + kvNbDelegatePtr->GetEntries(keyPrefix, readResultSet); + if (readResultSet != nullptr) { + readResultSet->GetCount(); + readResultSet->GetPosition(); + readResultSet->MoveToNext(); + readResultSet->MoveToPrevious(); + readResultSet->MoveToFirst(); + readResultSet->MoveToLast(); + + if (size == 0) { + return; + } + auto pos = U32_AT(data) % size; + readResultSet->MoveToPosition(pos++); + readResultSet->Move(0 - pos); + readResultSet->IsFirst(); + readResultSet->IsLast(); + readResultSet->IsBeforeFirst(); + readResultSet->IsAfterLast(); + kvNbDelegatePtr->CloseResultSet(readResultSet); + } + + g_kvManager.CloseKvStore(kvNbDelegatePtr); + g_kvManager.DeleteKvStore("distributed_nb_delegate_result_set_test"); +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + // u32 4 bytes + if (size < 4) { + return 0; + } + KvStoreConfig config; + DistributedDBToolsTest::TestDirInit(config.dataDir); + OHOS::g_kvManager.SetKvStoreConfig(config); + OHOS::ResultSetFuzzer(data, size); + DistributedDBToolsTest::RemoveTestDbFiles(config.dataDir); + return 0; +} + diff --git a/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/kvstoreresultset_fuzzer.h b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/kvstoreresultset_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..a4bee3fb55841b15c16273d2bf10c0eedd84eab0 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/kvstoreresultset_fuzzer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KVSTORERESULTSET_FUZZER_H +#define KVSTORERESULTSET_FUZZER_H + +#define FUZZ_PROJECT_NAME "kvstoreresultset_fuzzer" + +#include + +uint16_t U16_AT(const uint8_t * const &ptr) +{ + // 8 - 0 + return (ptr[0] << 8) | ptr[1]; +} + +uint32_t U32_AT(const uint8_t * const &ptr) +{ + // 24 - 16 - 8 - 0 + return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; +} + +#endif // KVSTORERESULTSET_FUZZER_H diff --git a/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/project.xml b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..be92381e05fc528d2fba5603365ee6df71b5b840 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/kvstoreresultset_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 30 + + 4096 + + diff --git a/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/BUILD.gn b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..da6612af24d76f914dcfcc335e061b306cc01897 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/BUILD.gn @@ -0,0 +1,103 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("NbDelegateFuzzTest") { + module_out_path = "distributeddatamgr/distributeddb" + + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/sqlite", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/multiver", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/src", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include/relational", + "//third_party/jsoncpp/include/json", + "//third_party/skia/third_party/externals/spirv-headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/skia/third_party/externals/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/jsoncpp/include/json", + "//third_party/grpc/src/core/lib/json", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + fuzz_config_file = "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/nbdelegate_fuzzer" + + sources = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "nbdelegate_fuzzer.cpp", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + "//utils/native/base:utils", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### + +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":NbDelegateFuzzTest", + ] +} diff --git a/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/corpus/init b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..bc977bd9738ee9a70b362067f57a9c63d3adb801 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/nbdelegate_fuzzer.cpp b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/nbdelegate_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5138d498f002c823f2abb3a012b384912f8b5e45 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/nbdelegate_fuzzer.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nbdelegate_fuzzer.h" +#include +#include "distributeddb_tools_test.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_observer.h" +#include "platform_specific.h" + +class KvStoreNbDelegateCURDFuzzer { + /* Keep C++ file names the same as the class name. */ +}; + +namespace OHOS { +using namespace DistributedDB; +using namespace DistributedDBTest; + +class KvStoreObserverFuzzTest : public DistributedDB::KvStoreObserver { +public: + KvStoreObserverFuzzTest(); + ~KvStoreObserverFuzzTest() = default; + KvStoreObserverFuzzTest(const KvStoreObserverFuzzTest &) = delete; + KvStoreObserverFuzzTest& operator=(const KvStoreObserverFuzzTest &) = delete; + KvStoreObserverFuzzTest(KvStoreObserverFuzzTest &&) = delete; + KvStoreObserverFuzzTest& operator=(KvStoreObserverFuzzTest &&) = delete; + // callback function will be called when the db data is changed. + void OnChange(const DistributedDB::KvStoreChangedData &); + + // reset the callCount_ to zero. + void ResetToZero(); + // get callback results. + unsigned long GetCallCount() const; + const std::list &GetEntriesInserted() const; + const std::list &GetEntriesUpdated() const; + const std::list &GetEntriesDeleted() const; + bool IsCleared() const; + +private: + unsigned long callCount_ = 0; + bool isCleared_ = false; + std::list inserted_ {}; + std::list updated_ {}; + std::list deleted_ {}; +}; + +KvStoreObserverFuzzTest::KvStoreObserverFuzzTest() +{ + callCount_ = 0; +} + +void KvStoreObserverFuzzTest::OnChange(const KvStoreChangedData &data) +{ + callCount_++; + inserted_ = data.GetEntriesInserted(); + updated_ = data.GetEntriesUpdated(); + deleted_ = data.GetEntriesDeleted(); + isCleared_ = data.IsCleared(); +} + +void KvStoreObserverFuzzTest::ResetToZero() +{ + callCount_ = 0; + isCleared_ = false; + inserted_.clear(); + updated_.clear(); + deleted_.clear(); +} + +unsigned long KvStoreObserverFuzzTest::GetCallCount() const +{ + return callCount_; +} + +const std::list &KvStoreObserverFuzzTest::GetEntriesInserted() const +{ + return inserted_; +} + +const std::list &KvStoreObserverFuzzTest::GetEntriesUpdated() const +{ + return updated_; +} +const std::list &KvStoreObserverFuzzTest::GetEntriesDeleted() const +{ + return deleted_; +} + +bool KvStoreObserverFuzzTest::IsCleared() const +{ + return isCleared_; +} + +std::vector CreateEntries(const uint8_t* data, size_t size, std::vector keys) +{ + std::vector entries; + // key'length is less than 1024. + auto count = static_cast(std::min(size, size_t(1024))); + for (int i = 1; i < count; i++) { + Entry entry; + entry.key = std::vector(data, data + i); + keys.push_back(entry.key); + entry.value = std::vector(data, data + size); + entries.push_back(entry); + } + return entries; +} + +void FuzzCURD(const uint8_t* data, size_t size, KvStoreNbDelegate *kvNbDelegatePtr) +{ + auto observer = new (std::nothrow) KvStoreObserverFuzzTest; + if ((observer == nullptr) || (kvNbDelegatePtr == nullptr)) { + return; + } + Key key = std::vector(data, data + (size % 1024)); /* 1024 is max */ + Value value = std::vector(data, data + size); + kvNbDelegatePtr->RegisterObserver(key, size, observer); + kvNbDelegatePtr->SetConflictNotifier(size, [](const KvStoreNbConflictData &data) { + (void)data.GetType(); + }); + + Value valueRead; + kvNbDelegatePtr->PutLocal(key, value); + kvNbDelegatePtr->GetLocal(key, valueRead); + kvNbDelegatePtr->DeleteLocal(key); + kvNbDelegatePtr->Put(key, value); + kvNbDelegatePtr->Put(key, value); + std::vector vect; + kvNbDelegatePtr->GetEntries(key, vect); + kvNbDelegatePtr->Delete(key); + kvNbDelegatePtr->Get(key, valueRead); + std::vector keys; + std::vector tmp = CreateEntries(data, size, keys); + kvNbDelegatePtr->PutBatch(tmp); + if (!keys.empty()) { + /* random deletePublic updateTimestamp 2 */ + kvNbDelegatePtr->UnpublishToLocal(keys[0], (data[0] > data[1]), (data[2] > data[1])); + } + kvNbDelegatePtr->DeleteBatch(keys); + kvNbDelegatePtr->UnRegisterObserver(observer); + kvNbDelegatePtr->PutLocalBatch(tmp); + + if (!keys.empty()) { + /* random deletePublic updateTimestamp 2 */ + kvNbDelegatePtr->PublishLocal(keys[0], (data[0] > data[1]), (data[2] > data[1]), nullptr); + } + kvNbDelegatePtr->DeleteBatch(keys); + std::string rawString(reinterpret_cast(data), size); + kvNbDelegatePtr->RemoveDeviceData(rawString); +} + +void CombineTest(const uint8_t* data, size_t size, KvStoreNbDelegate::Option &option) +{ + static auto kvManager = KvStoreDelegateManager("APP_ID", "USER_ID"); + KvStoreConfig config; + DistributedDBToolsTest::TestDirInit(config.dataDir); + kvManager.SetKvStoreConfig(config); + KvStoreNbDelegate *kvNbDelegatePtr = nullptr; + kvManager.GetKvStore("distributed_nb_delegate_test", option, + [&kvNbDelegatePtr] (DBStatus status, KvStoreNbDelegate* kvNbDelegate) { + if (status == DBStatus::OK) { + kvNbDelegatePtr = kvNbDelegate; + } + }); + FuzzCURD(data, size, kvNbDelegatePtr); + kvManager.CloseKvStore(kvNbDelegatePtr); + kvManager.DeleteKvStore("distributed_nb_delegate_test"); + DistributedDBToolsTest::RemoveTestDbFiles(config.dataDir); +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + DistributedDB::KvStoreNbDelegate::Option option = {true, false, false}; + OHOS::CombineTest(data, size, option); + option = {true, true, false}; + OHOS::CombineTest(data, size, option); + return 0; +} + diff --git a/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/nbdelegate_fuzzer.h b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/nbdelegate_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..e476b2a947c7b8f41eeca3fe2057b7be839052c4 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/nbdelegate_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NBDELEGATE_FUZZER_H +#define NBDELEGATE_FUZZER_H + +#define FUZZ_PROJECT_NAME "NbDelegate_fuzzer" + +#endif // NBDELEGATE_FUZZER_H diff --git a/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/project.xml b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..be92381e05fc528d2fba5603365ee6df71b5b840 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/nbdelegate_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 30 + + 4096 + + diff --git a/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/BUILD.gn b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..93723536751c4188d4a350796b00baf0c728b882 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/BUILD.gn @@ -0,0 +1,107 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("ParseCkeckFuzzTest") { + module_out_path = "distributeddatamgr/distributeddb" + + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/sqlite", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/multiver", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/src", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include/relational", + "//third_party/jsoncpp/include/json", + "//third_party/skia/third_party/externals/spirv-headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/skia/third_party/externals/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/jsoncpp/include/json", + "//third_party/grpc/src/core/lib/json", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + fuzz_config_file = "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/parseckeck_fuzzer" + + sources = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/flatbuffer_schema.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/json_object.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/schema_object.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/schema_utils.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "parseckeck_fuzzer.cpp", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + "//utils/native/base:utils", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### + +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":ParseCkeckFuzzTest", + ] +} diff --git a/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/corpus/init b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..bc977bd9738ee9a70b362067f57a9c63d3adb801 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/parseckeck_fuzzer.cpp b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/parseckeck_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ad5ca658e66301dad0e23239af5438d7ea92ae7 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/parseckeck_fuzzer.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "parseckeck_fuzzer.h" +#include "distributeddb_tools_test.h" +#include "schema_object.h" +#include "schema_utils.h" + +using namespace DistributedDB; +using namespace DistributedDBTest; + +static KvStoreConfig g_config; + +namespace OHOS { +void GetSchmaKvstore(const uint8_t* data, size_t size) +{ + static auto kvManager = KvStoreDelegateManager("APP_ID", "USER_ID"); + kvManager.SetKvStoreConfig(g_config); + KvStoreNbDelegate::Option option = {true, false, false}; + std::string schemaString(reinterpret_cast(data), size); + option.schema = schemaString; + KvStoreNbDelegate *kvNbDelegatePtr = nullptr; + + kvManager.GetKvStore("distributed_nb_get_schemakvstore", option, + [&kvNbDelegatePtr] (DBStatus status, KvStoreNbDelegate* kvNbDelegate) { + if (status == DBStatus::OK) { + kvNbDelegatePtr = kvNbDelegate; + } + }); + + kvManager.CloseKvStore(kvNbDelegatePtr); + kvManager.DeleteKvStore("distributed_nb_get_schemakvstore"); +} + +void ParseSchemaString(const uint8_t* data, size_t size) +{ + std::string schemaString(reinterpret_cast(data), size); + SchemaObject schemaOri; + schemaOri.ParseFromSchemaString(schemaString); + schemaOri.CompareAgainstSchemaString(schemaString); +} + +void CompareSchemaString(const uint8_t* data, size_t size) +{ + // beginning half / 2 + std::string schemaString(data, data + (size / 2)); + // ending half / 2 ~ end. + std::string schemaString2(data + (size / 2), data + size); + SchemaObject schemaOri; + schemaOri.ParseFromSchemaString(schemaString); + schemaOri.ParseFromSchemaString(schemaString2); +} + +void CheckFieldName(const uint8_t* data, size_t size) +{ + std::string schemaAttrString(reinterpret_cast(data), size); + SchemaUtils::CheckFieldName(schemaAttrString); +} + +void ParseFieldPath(const uint8_t* data, size_t size) +{ + std::string schemaAttrString(reinterpret_cast(data), size); + FieldPath outPath; + SchemaUtils::ParseAndCheckFieldPath(schemaAttrString, outPath); +} + +void CheckSchemaAttribute(const uint8_t* data, size_t size) +{ + std::string schemaAttrString(reinterpret_cast(data), size); + SchemaAttribute outAttr; + SchemaUtils::ParseAndCheckSchemaAttribute(schemaAttrString, outAttr); + SchemaUtils::ParseAndCheckSchemaAttribute(schemaAttrString, outAttr); +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + DistributedDBToolsTest::TestDirInit(g_config.dataDir); + OHOS::GetSchmaKvstore(data, size); + OHOS::ParseSchemaString(data, size); + OHOS::CompareSchemaString(data, size); + OHOS::CheckFieldName(data, size); + OHOS::ParseFieldPath(data, size); + OHOS::CheckSchemaAttribute(data, size); + + DistributedDBToolsTest::RemoveTestDbFiles(g_config.dataDir); + return 0; +} + diff --git a/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/parseckeck_fuzzer.h b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/parseckeck_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..451c718a8308915097aaf7d86c867f024f5598a2 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/parseckeck_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PARSECHECK_FUZZER_H +#define PARSECHECK_FUZZER_H + +#define FUZZ_PROJECT_NAME "ParseCkeck_fuzzer" + +#endif // PARSECHECK_FUZZER_H diff --git a/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/project.xml b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..be92381e05fc528d2fba5603365ee6df71b5b840 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/parseckeck_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 30 + + 4096 + + diff --git a/mock/distributeddb/test/fuzztest/query_fuzzer/BUILD.gn b/mock/distributeddb/test/fuzztest/query_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..b9379a1ebbdcc8149c26ade3a53e12115c96b142 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/query_fuzzer/BUILD.gn @@ -0,0 +1,104 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("QueryFuzzTest") { + module_out_path = "distributeddatamgr/distributeddb" + + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/sqlite", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/multiver", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/src", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include/relational", + "//third_party/jsoncpp/include/json", + "//third_party/skia/third_party/externals/spirv-headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/skia/third_party/externals/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/jsoncpp/include/json", + "//third_party/grpc/src/core/lib/json", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + fuzz_config_file = "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/query_fuzzer" + + sources = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/json_object.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/schema_object.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/schema_utils.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "query_fuzzer.cpp", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + "//utils/native/base:utils", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### + +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":QueryFuzzTest", + ] +} diff --git a/mock/distributeddb/test/fuzztest/query_fuzzer/corpus/init b/mock/distributeddb/test/fuzztest/query_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..8eb5a7d6eb6b7d71f0c70c244e5768d62bee6ac5 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/query_fuzzer/corpus/init @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +FUZZ \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/query_fuzzer/project.xml b/mock/distributeddb/test/fuzztest/query_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..be92381e05fc528d2fba5603365ee6df71b5b840 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/query_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 30 + + 4096 + + diff --git a/mock/distributeddb/test/fuzztest/query_fuzzer/query_fuzzer.cpp b/mock/distributeddb/test/fuzztest/query_fuzzer/query_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..892a5c36a4e3faaf053143a9c54592e4093ea2ae --- /dev/null +++ b/mock/distributeddb/test/fuzztest/query_fuzzer/query_fuzzer.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "query_fuzzer.h" +#include "get_query_info.h" + +using namespace DistributedDB; +using namespace std; + +namespace { + constexpr const char *TEST_FIELD_NAME = "$.test"; +} + +namespace OHOS { +void FuzzEqualTo(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().EqualTo(rawString, static_cast(size)); +} + +void FuzzNotEqualTo(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().NotEqualTo(TEST_FIELD_NAME, rawString); +} + +void FuzzGreaterThan(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().GreaterThan(rawString, static_cast(U32_AT(data))); +} + +void FuzzLessThan(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().LessThan(TEST_FIELD_NAME, rawString); +} + +void FuzzGreaterThanOrEqualTo(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().GreaterThanOrEqualTo(rawString, static_cast(size)); +} + +void FuzzLessThanOrEqualTo(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().LessThanOrEqualTo(TEST_FIELD_NAME, rawString); +} + +void FuzzOrderBy(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().GreaterThanOrEqualTo(rawString, true); + query = Query::Select().GreaterThanOrEqualTo(rawString, false); +} + +void FuzzLimit(const uint8_t* data, size_t size) +{ + Query query = Query::Select().Limit(static_cast(size), static_cast(U32_AT(data))); +} + +void FuzzLike(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().Like(rawString, rawString); +} + +void FuzzNotLike(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().NotLike(TEST_FIELD_NAME, rawString); +} + +void FuzzIn(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + std::vector values; + // 512 max size + for (int i = 0; i < static_cast(U32_AT(data) % 512); i++) { + values.push_back(rawString); + } + Query query = Query::Select().In(TEST_FIELD_NAME, values); +} + +void FuzzNotIn(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + std::vector values; + // 512 max size + for (int i = 0; i < static_cast(size % 512); i++) { + values.push_back(rawString); + } + Query query = Query::Select().NotIn(TEST_FIELD_NAME, values); +} + +void FuzzIsNull(const uint8_t* data, size_t size) +{ + std::string rawString(reinterpret_cast(data), size); + Query query = Query::Select().IsNull(rawString); +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + // u32 4 bytes + if (size < 4) { + return 0; + } + // Run your code on data + OHOS::FuzzEqualTo(data, size); + OHOS::FuzzNotEqualTo(data, size); + OHOS::FuzzGreaterThan(data, size); + OHOS::FuzzLessThan(data, size); + OHOS::FuzzGreaterThanOrEqualTo(data, size); + OHOS::FuzzLessThanOrEqualTo(data, size); + OHOS::FuzzOrderBy(data, size); + OHOS::FuzzLimit(data, size); + OHOS::FuzzLike(data, size); + OHOS::FuzzNotLike(data, size); + OHOS::FuzzIn(data, size); + OHOS::FuzzNotIn(data, size); + OHOS::FuzzIsNull(data, size); + return 0; +} + diff --git a/mock/distributeddb/test/fuzztest/query_fuzzer/query_fuzzer.h b/mock/distributeddb/test/fuzztest/query_fuzzer/query_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..60c06746887b2c6076d0f932c813c1e5f83e8d01 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/query_fuzzer/query_fuzzer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef QUERY_FUZZER_H +#define QUERY_FUZZER_H + +#define FUZZ_PROJECT_NAME "Query_fuzzer" + +#include + +uint16_t U16_AT(const uint8_t * const &ptr) +{ + // 8 - 0 + return (ptr[0] << 8) | ptr[1]; +} + +uint32_t U32_AT(const uint8_t * const &ptr) +{ + // 24 - 16 - 8 - 0 + return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; +} + +#endif // QUERY_FUZZER_H diff --git a/mock/distributeddb/test/fuzztest/rekey_fuzzer/BUILD.gn b/mock/distributeddb/test/fuzztest/rekey_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..59884e5871f2e05f5f4b150e727b86f703330355 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/rekey_fuzzer/BUILD.gn @@ -0,0 +1,103 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("ReKeyFuzzTest") { + module_out_path = "distributeddatamgr/distributeddb" + + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/interfaces/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/sqlite", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/storage/src/multiver", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/communicator/src", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/syncer/src", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/include/relational", + "//third_party/jsoncpp/include/json", + "//third_party/skia/third_party/externals/spirv-headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/skia/third_party/externals/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsoncpp/dist/json", + "//third_party/jsoncpp/include/json", + "//third_party/grpc/src/core/lib/json", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + fuzz_config_file = "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/rekey_fuzzer" + + sources = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/fuzztest/common/distributeddb_tools_test.cpp", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "rekey_fuzzer.cpp", + ] + + defines = [ + "SQLITE_ENABLE_SNAPSHOT", + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "LOW_LEVEL_MEM_DEV", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_shared", + "//utils/native/base:utils", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### + +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + ":ReKeyFuzzTest", + ] +} diff --git a/mock/distributeddb/test/fuzztest/rekey_fuzzer/corpus/init b/mock/distributeddb/test/fuzztest/rekey_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..bc977bd9738ee9a70b362067f57a9c63d3adb801 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/rekey_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/mock/distributeddb/test/fuzztest/rekey_fuzzer/project.xml b/mock/distributeddb/test/fuzztest/rekey_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..be92381e05fc528d2fba5603365ee6df71b5b840 --- /dev/null +++ b/mock/distributeddb/test/fuzztest/rekey_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 30 + + 4096 + + diff --git a/mock/distributeddb/test/fuzztest/rekey_fuzzer/rekey_fuzzer.cpp b/mock/distributeddb/test/fuzztest/rekey_fuzzer/rekey_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..212d2f1e79ecce15eb2874c545ced9ad3bc7f99b --- /dev/null +++ b/mock/distributeddb/test/fuzztest/rekey_fuzzer/rekey_fuzzer.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rekey_fuzzer.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_test.h" + +using namespace DistributedDB; +using namespace DistributedDBTest; + +namespace OHOS { +static auto g_kvManager = KvStoreDelegateManager("APP_ID", "USER_ID"); +std::vector CreateEntries(const uint8_t* data, size_t size) +{ + std::vector entries; + + auto count = static_cast(std::min(size, size_t(1024))); + for (int i = 1; i < count; i++) { + Entry entry; + entry.key = std::vector (data, data + i); + entry.value = std::vector (data, data + size); + entries.push_back(entry); + } + return entries; +} + +void SingerVerReKey(const uint8_t* data, size_t size) +{ + CipherPassword passwd; + // div 2 -> half + passwd.SetValue(data, (size / 2)); + + KvStoreNbDelegate::Option nbOption = {true, false, true, CipherType::DEFAULT, passwd}; + KvStoreNbDelegate *kvNbDelegatePtr = nullptr; + + g_kvManager.GetKvStore("distributed_nb_rekey_test", nbOption, + [&kvNbDelegatePtr](DBStatus status, KvStoreNbDelegate * kvNbDelegate) { + if (status == DBStatus::OK) { + kvNbDelegatePtr = kvNbDelegate; + } + }); + + if (kvNbDelegatePtr != nullptr) { + kvNbDelegatePtr->PutBatch(CreateEntries(data, size)); + passwd.SetValue(data, size); + kvNbDelegatePtr->Rekey(passwd); + g_kvManager.CloseKvStore(kvNbDelegatePtr); + } +} + +void MultiVerVerReKey(const uint8_t* data, size_t size) +{ + CipherPassword passwd; + // div 2 -> half + passwd.SetValue(data, (size / 2)); + + KvStoreNbDelegate::Option nbOption = {true, false, true, CipherType::DEFAULT, passwd}; + KvStoreNbDelegate *kvNbDelegatePtr = nullptr; + + g_kvManager.GetKvStore("distributed_rekey_test", nbOption, + [&kvNbDelegatePtr](DBStatus status, KvStoreNbDelegate * kvNbDelegate) { + if (status == DBStatus::OK) { + kvNbDelegatePtr = kvNbDelegate; + } + }); + + if (kvNbDelegatePtr != nullptr) { + kvNbDelegatePtr->PutBatch(CreateEntries(data, size)); + CipherPassword passwd; + passwd.SetValue(data, size); + kvNbDelegatePtr->Rekey(passwd); + g_kvManager.CloseKvStore(kvNbDelegatePtr); + } +} +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + KvStoreConfig config; + DistributedDBToolsTest::TestDirInit(config.dataDir); + OHOS::g_kvManager.SetKvStoreConfig(config); + OHOS::SingerVerReKey(data, size); + OHOS::MultiVerVerReKey(data, size); + DistributedDBToolsTest::RemoveTestDbFiles(config.dataDir); + return 0; +} + diff --git a/mock/distributeddb/test/fuzztest/rekey_fuzzer/rekey_fuzzer.h b/mock/distributeddb/test/fuzztest/rekey_fuzzer/rekey_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..9524bc76e1bc7a263d1e974d43acde769e6213bb --- /dev/null +++ b/mock/distributeddb/test/fuzztest/rekey_fuzzer/rekey_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef REKEY_FUZZER_H +#define REKEY_FUZZER_H + +#define FUZZ_PROJECT_NAME "Rekey_fuzzer" + +#endif // REKEY_FUZZER_H diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_auto_launch_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_auto_launch_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..970373b368fa88b8c0330a9ac683679748e477e1 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_auto_launch_test.cpp @@ -0,0 +1,1014 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "auto_launch.h" +#include "db_common.h" +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "kv_store_nb_conflict_data.h" +#include "kvdb_manager.h" +#include "kvdb_pragma.h" +#include "log_print.h" +#include "platform_specific.h" +#include "virtual_communicator_aggregator.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + const std::string APP_ID = "appId"; + const std::string USER_ID = "userId"; + const std::string STORE_ID_0 = "storeId0"; + const std::string STORE_ID_1 = "storeId1"; + const std::string STORE_ID_2 = "storeId2"; + const std::string STORE_ID_3 = "storeId3"; + const std::string STORE_ID_4 = "storeId4"; + const std::string STORE_ID_5 = "storeId5"; + const std::string STORE_ID_6 = "storeId6"; + const std::string STORE_ID_7 = "storeId7"; + const std::string STORE_ID_8 = "storeId8"; + string g_testDir; + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + VirtualCommunicatorAggregator *g_communicatorAggregator = nullptr; + + const int TEST_ENABLE_CNT = 10; // 10 time + const int TEST_ONLINE_CNT = 200; // 10 time + const int WAIT_TIME = 1000; // 1000ms + const int LIFE_CYCLE_TIME = 5000; // 5000ms + const int WAIT_SHORT_TIME = 200; // 20ms + const Timestamp TIME_ADD = 1000; // not zero is ok + const std::string REMOTE_DEVICE_ID = "remote_device"; + const std::string THIS_DEVICE = "real_device"; + + const Key KEY1{'k', 'e', 'y', '1'}; + const Key KEY2{'k', 'e', 'y', '2'}; + const Value VALUE1{'v', 'a', 'l', 'u', 'e', '1'}; + const Value VALUE2{'v', 'a', 'l', 'u', 'e', '2'}; + KvDBProperties g_propA; + KvDBProperties g_propB; + KvDBProperties g_propC; + KvDBProperties g_propD; + KvDBProperties g_propE; + KvDBProperties g_propF; + KvDBProperties g_propG; + KvDBProperties g_propH; + KvDBProperties g_propI; + std::string g_identifierA; + std::string g_identifierB; + std::string g_identifierC; + std::string g_identifierD; + std::string g_identifierE; + std::string g_identifierF; + std::string g_identifierG; + std::string g_identifierH; + std::string g_identifierI; + std::string g_dualIdentifierA; + std::string g_dualIdentifierB; + std::string g_dualIdentifierC; + std::string g_dualIdentifierD; + std::string g_dualIdentifierE; + std::string g_dualIdentifierF; + std::string g_dualIdentifierG; + std::string g_dualIdentifierH; + std::string g_dualIdentifierI; +} + +class DistributedDBAutoLaunchUnitTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown() {}; +}; + +void DistributedDBAutoLaunchUnitTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + string dir = g_testDir; + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + if (DistributedDBToolsUnitTest::RemoveTestDbFiles( + g_testDir + "/" + DBCommon::TransferStringToHex(g_identifierA) + "/single_ver") != 0) { + LOGE("rm test db files error!"); + } + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); +} + +void DistributedDBAutoLaunchUnitTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles( + g_testDir + "/" + DBCommon::TransferStringToHex(g_identifierA) + "/single_ver") != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); +} + +static void GetProperty(KvDBProperties &prop, std::string &identifier, std::string storeId, std::string &dualIdentifier) +{ + prop.SetStringProp(KvDBProperties::USER_ID, USER_ID); + prop.SetStringProp(KvDBProperties::APP_ID, APP_ID); + prop.SetStringProp(KvDBProperties::STORE_ID, storeId); + identifier = DBCommon::TransferHashString(USER_ID + "-" + APP_ID + "-" + storeId); + prop.SetStringProp(KvDBProperties::IDENTIFIER_DATA, identifier); + dualIdentifier = DBCommon::TransferHashString(APP_ID + "-" + storeId); + prop.SetStringProp(KvDBProperties::DUAL_TUPLE_IDENTIFIER_DATA, dualIdentifier); + std::string identifierDirA = DBCommon::TransferStringToHex(identifier); + prop.SetStringProp(KvDBProperties::IDENTIFIER_DIR, identifierDirA); + prop.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + prop.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + prop.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + prop.SetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false); +} + +void DistributedDBAutoLaunchUnitTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + if (DistributedDBToolsUnitTest::RemoveTestDbFiles( + g_testDir + "/" + DBCommon::TransferStringToHex(g_identifierA) + "/single_ver") != 0) { + LOGE("rm test db files error!"); + } + GetProperty(g_propA, g_identifierA, STORE_ID_0, g_dualIdentifierA); + GetProperty(g_propB, g_identifierB, STORE_ID_1, g_dualIdentifierB); + GetProperty(g_propC, g_identifierC, STORE_ID_2, g_dualIdentifierC); + GetProperty(g_propD, g_identifierD, STORE_ID_3, g_dualIdentifierD); + GetProperty(g_propE, g_identifierE, STORE_ID_4, g_dualIdentifierE); + GetProperty(g_propF, g_identifierF, STORE_ID_5, g_dualIdentifierF); + GetProperty(g_propG, g_identifierG, STORE_ID_6, g_dualIdentifierG); + GetProperty(g_propH, g_identifierH, STORE_ID_7, g_dualIdentifierH); + GetProperty(g_propI, g_identifierI, STORE_ID_8, g_dualIdentifierI); +} + +static void PutSyncData(const KvDBProperties &prop, const Key &key, const Value &value) +{ + int errCode = E_OK; + auto kvStore = static_cast(KvDBManager::OpenDatabase(prop, errCode)); + ASSERT_NE(kvStore, nullptr); + auto *connection = kvStore->GetDBConnection(errCode); + ASSERT_NE(connection, nullptr); + if (kvStore != nullptr) { + std::vector vect; + Timestamp time; + kvStore->GetMaxTimestamp(time); + time += TIME_ADD; + LOGD("time:%" PRIu64, time); + vect.push_back({key, value, time, 0, DBCommon::TransferHashString(REMOTE_DEVICE_ID)}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(kvStore, vect, REMOTE_DEVICE_ID), E_OK); + } + RefObject::DecObjRef(kvStore); + connection->Close(); + connection = nullptr; +} + +static void SetLifeCycleTime(const KvDBProperties &prop) +{ + int errCode = E_OK; + auto kvStore = static_cast(KvDBManager::OpenDatabase(prop, errCode)); + ASSERT_NE(kvStore, nullptr); + auto *connection = kvStore->GetDBConnection(errCode); + ASSERT_NE(connection, nullptr); + uint32_t time = LIFE_CYCLE_TIME; + EXPECT_EQ(connection->Pragma(PRAGMA_SET_AUTO_LIFE_CYCLE, static_cast(&time)), E_OK); + RefObject::DecObjRef(kvStore); + connection->Close(); + connection = nullptr; +} + +/** + * @tc.name: AutoLaunch001 + * @tc.desc: basic enable/disable func + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch001, TestSize.Level3) +{ + /** + * @tc.steps: step1. right param A enable + * @tc.expected: step1. success. + */ + AutoLaunchOption option; + option.notifier = nullptr; + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, nullptr, option); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step2. wrong param B enable + * @tc.expected: step2. failed. + */ + g_propB.SetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, nullptr, option); + EXPECT_TRUE(errCode != E_OK); + + /** + * @tc.steps: step3. right param C enable + * @tc.expected: step3. success. + */ + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propC, nullptr, option); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step4. param A disable + * @tc.expected: step4. E_OK. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA, g_dualIdentifierA, USER_ID); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step5. param B disable + * @tc.expected: step5. -E_NOT_FOUND. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB, g_dualIdentifierB, USER_ID); + EXPECT_TRUE(errCode == -E_NOT_FOUND); + + /** + * @tc.steps: step6. param C disable + * @tc.expected: step6. E_OK. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierC, g_dualIdentifierC, USER_ID); + EXPECT_TRUE(errCode == E_OK); +} + +/** + * @tc.name: AutoLaunch002 + * @tc.desc: online callback + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch002, TestSize.Level3) +{ + std::mutex cvMutex; + std::condition_variable cv; + bool finished = false; + std::map statusMap; + + auto notifier = [&cvMutex, &cv, &finished, &statusMap] (const std::string &userId, const std::string &appId, + const std::string &storeId, AutoLaunchStatus status) { + LOGD("int AutoLaunch002 notifier status:%d", status); + std::string identifier = DBCommon::TransferHashString(userId + "-" + appId + "-" + storeId); + std::unique_lock lock(cvMutex); + statusMap[identifier] = status; + LOGD("int AutoLaunch002 notifier statusMap.size():%d", statusMap.size()); + if (statusMap.size() == 2) { // A and B + finished = true; + cv.notify_one(); + } + }; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps: step1. right param A B enable + * @tc.expected: step1. success. + */ + AutoLaunchOption option; + option.notifier = nullptr; + option.observer = observer; + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, notifier, option) == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, notifier, option) == E_OK); + + /** + * @tc.steps: step2. RunOnConnectCallback + * @tc.expected: step2. success. + */ + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, true); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + + /** + * @tc.steps: step3. PutSyncData + * @tc.expected: step3. notifier WRITE_OPENED + */ + PutSyncData(g_propA, KEY1, VALUE1); + PutSyncData(g_propB, KEY1, VALUE1); + { + std::unique_lock lock(cvMutex); + cv.wait(lock, [&finished] {return finished;}); + EXPECT_TRUE(statusMap[g_identifierA] == WRITE_OPENED); + EXPECT_TRUE(statusMap[g_identifierB] == WRITE_OPENED); + statusMap.clear(); + finished = false; + } + EXPECT_TRUE(observer->GetCallCount() == 2); // A and B + delete observer; + /** + * @tc.steps: step4. param A B disable + * @tc.expected: step4. notifier WRITE_CLOSED + */ + EXPECT_TRUE(RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA, g_dualIdentifierA, USER_ID) + == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB, g_dualIdentifierB, USER_ID) + == E_OK); + + std::unique_lock lock(cvMutex); + cv.wait(lock, [&finished] {return finished;}); + EXPECT_TRUE(statusMap[g_identifierA] == WRITE_CLOSED); + EXPECT_TRUE(statusMap[g_identifierB] == WRITE_CLOSED); + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, false); +} + +/** + * @tc.name: AutoLaunch003 + * @tc.desc: CommunicatorLackCallback + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch003, TestSize.Level3) +{ + std::mutex cvMutex; + std::condition_variable cv; + bool finished = false; + std::map statusMap; + + auto notifier = [&cvMutex, &cv, &finished, &statusMap] (const std::string &userId, const std::string &appId, + const std::string &storeId, AutoLaunchStatus status) { + LOGD("int AutoLaunch002 notifier status:%d", status); + std::string identifier = DBCommon::TransferHashString(userId + "-" + appId + "-" + storeId); + std::unique_lock lock(cvMutex); + statusMap[identifier] = status; + LOGD("int AutoLaunch002 notifier statusMap.size():%zu", statusMap.size()); + finished = true; + cv.notify_one(); + }; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + + /** + * @tc.steps: step1. right param A B enable + * @tc.expected: step1. success. + */ + AutoLaunchOption option; + option.notifier = nullptr; + option.observer = observer; + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, notifier, option); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, notifier, option); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step2. RunCommunicatorLackCallback + * @tc.expected: step2. success. + */ + LabelType label(g_identifierA.begin(), g_identifierA.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + + /** + * @tc.steps: step3. PutSyncData + * @tc.expected: step3. notifier WRITE_OPENED + */ + PutSyncData(g_propA, KEY2, VALUE2); + { + std::unique_lock lock(cvMutex); + cv.wait(lock, [&finished] {return finished;}); + EXPECT_TRUE(statusMap[g_identifierA] == WRITE_OPENED); + statusMap.clear(); + finished = false; + } + EXPECT_TRUE(observer->GetCallCount() == 1); // only A + delete observer; + /** + * @tc.steps: step4. param A B disable + * @tc.expected: step4. notifier WRITE_CLOSED + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB, g_dualIdentifierB, USER_ID); + EXPECT_TRUE(errCode == E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA, g_dualIdentifierA, USER_ID); + EXPECT_TRUE(errCode == E_OK); + + std::unique_lock lock(cvMutex); + cv.wait(lock, [&finished] {return finished;}); + EXPECT_TRUE(statusMap[g_identifierA] == WRITE_CLOSED); + EXPECT_TRUE(statusMap.size() == 1); +} + +/** + * @tc.name: AutoLaunch004 + * @tc.desc: basic enable/disable func + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch004, TestSize.Level3) +{ + /** + * @tc.steps: step1. right param A~H enable + * @tc.expected: step1. success. + */ + AutoLaunchOption option; + option.notifier = nullptr; + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, nullptr, option) == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, nullptr, option) == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propC, nullptr, option) == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propD, nullptr, option) == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propE, nullptr, option) == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propF, nullptr, option) == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propG, nullptr, option) == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propH, nullptr, option) == E_OK); + + /** + * @tc.steps: step2. right param I enable + * @tc.expected: step2. -E_MAX_LIMITS. + */ + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propI, nullptr, option); + EXPECT_TRUE(errCode == -E_MAX_LIMITS); + + /** + * @tc.steps: step3. param A disable + * @tc.expected: step3. E_OK. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA, g_dualIdentifierA, USER_ID); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step4. right param I enable + * @tc.expected: step4. E_OK. + */ + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propI, nullptr, option); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step6. param B~I disable + * @tc.expected: step6. E_OK. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB, g_dualIdentifierB, USER_ID); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierC, g_dualIdentifierC, USER_ID); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierD, g_dualIdentifierD, USER_ID); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierE, g_dualIdentifierE, USER_ID); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierF, g_dualIdentifierF, USER_ID); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierG, g_dualIdentifierG, USER_ID); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierH, g_dualIdentifierH, USER_ID); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierI, g_dualIdentifierI, USER_ID); + EXPECT_TRUE(errCode == E_OK); +} + +/** + * @tc.name: AutoLaunch005 + * @tc.desc: online device before enable + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch005, TestSize.Level3) +{ + std::mutex cvMutex; + std::condition_variable cv; + bool finished = false; + std::map statusMap; + + auto notifier = [&cvMutex, &cv, &finished, &statusMap] (const std::string &userId, const std::string &appId, + const std::string &storeId, AutoLaunchStatus status) { + LOGD("int AutoLaunch002 notifier status:%d", status); + std::string identifier = DBCommon::TransferHashString(userId + "-" + appId + "-" + storeId); + std::unique_lock lock(cvMutex); + statusMap[identifier] = status; + LOGD("int AutoLaunch002 notifier statusMap.size():%zu", statusMap.size()); + finished = true; + cv.notify_one(); + }; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps: step1. RunOnConnectCallback + * @tc.expected: step1. success. + */ + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, true); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + + /** + * @tc.steps: step2. right param A enable + * @tc.expected: step2. success. + */ + AutoLaunchOption option; + option.notifier = nullptr; + option.observer = observer; + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, notifier, option); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step3. PutSyncData + * @tc.expected: step3. notifier WRITE_OPENED + */ + PutSyncData(g_propA, KEY1, VALUE1); + { + std::unique_lock lock(cvMutex); + cv.wait(lock, [&finished] {return finished;}); + EXPECT_TRUE(statusMap[g_identifierA] == WRITE_OPENED); + statusMap.clear(); + finished = false; + } + EXPECT_TRUE(observer->GetCallCount() == 1); // only A + /** + * @tc.steps: step4. param A disable + * @tc.expected: step4. notifier WRITE_CLOSED + */ + std::string identifierA = g_propA.GetStringProp(KvDBProperties::DUAL_TUPLE_IDENTIFIER_DATA, ""); + std::string userIdA = g_propA.GetStringProp(KvDBProperties::USER_ID, ""); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA, g_dualIdentifierB, USER_ID); + EXPECT_TRUE(errCode == E_OK); + + std::unique_lock lock(cvMutex); + cv.wait(lock, [&finished] {return finished;}); + EXPECT_TRUE(statusMap[g_identifierA] == WRITE_CLOSED); + delete observer; + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, false); +} + +/** + * @tc.name: AutoLaunch006 + * @tc.desc: online callback + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch006, TestSize.Level3) +{ + auto notifier = [] (const std::string &userId, const std::string &appId, + const std::string &storeId, AutoLaunchStatus status) { + LOGD("int AutoLaunch006 notifier status:%d", status); + }; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + std::mutex cvLock; + std::condition_variable cv; + bool threadIsWorking = true; + thread aggregatorThread([&cvLock, &cv, &threadIsWorking]() { + LabelType label(g_identifierA.begin(), g_identifierA.end()); + for (int i = 0; i < TEST_ONLINE_CNT; i++) { + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, true); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_SHORT_TIME)); + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, false); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_SHORT_TIME)); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_SHORT_TIME)); + LOGD("AutoLaunch006 thread i:%d", i); + } + std::unique_lock lock(cvLock); + threadIsWorking = false; + cv.notify_one(); + }); + aggregatorThread.detach(); + AutoLaunchOption option; + option.notifier = nullptr; + option.observer = observer; + for (int i = 0; i < TEST_ENABLE_CNT; i++) { + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, notifier, option); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, notifier, option); + EXPECT_TRUE(errCode == E_OK); + + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA, g_dualIdentifierA, USER_ID); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB, g_dualIdentifierB, USER_ID); + EXPECT_TRUE(errCode == E_OK); + LOGD("AutoLaunch006 disable i:%d", i); + } + std::unique_lock lock(cvLock); + cv.wait(lock, [&threadIsWorking] { return !threadIsWorking; }); + + delete observer; + observer = nullptr; + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, false); +} + +namespace { +std::mutex g_cvMutex; +std::condition_variable g_cv; +bool g_finished = false; +std::map g_statusMap; +void ConflictNotifierCallback(const KvStoreNbConflictData &data) +{ + LOGD("in ConflictNotifierCallback"); + Key key; + Value oldValue; + Value newValue; + data.GetKey(key); + data.GetValue(KvStoreNbConflictData::ValueType::OLD_VALUE, oldValue); + data.GetValue(KvStoreNbConflictData::ValueType::NEW_VALUE, newValue); + EXPECT_TRUE(key == KEY1); + EXPECT_TRUE(oldValue == VALUE1); + EXPECT_TRUE(newValue == VALUE2); + g_finished = true; + g_cv.notify_one(); +} + +void TestAutoLaunchNotifier(const std::string &userId, const std::string &appId, const std::string &storeId, + AutoLaunchStatus status) +{ + LOGD("int AutoLaunchNotifier, status:%d", status); + std::string identifier = DBCommon::TransferHashString(userId + "-" + appId + "-" + storeId); + std::unique_lock lock(g_cvMutex); + g_statusMap[identifier] = status; + g_finished = true; + g_cv.notify_one(); +}; + +bool AutoLaunchCallBack(const std::string &identifier, AutoLaunchParam ¶m, KvStoreObserverUnitTest *observer, + bool ret) +{ + LOGD("int AutoLaunchCallBack"); + EXPECT_TRUE(identifier == g_identifierA); + param.userId = USER_ID; + param.appId = APP_ID; + param.storeId = STORE_ID_0; + CipherPassword passwd; + param.option = {true, false, CipherType::DEFAULT, passwd, "", false, g_testDir, observer, + CONFLICT_FOREIGN_KEY_ONLY, ConflictNotifierCallback}; + param.notifier = TestAutoLaunchNotifier; + return ret; +} + +bool AutoLaunchCallBackBadParam(const std::string &identifier, AutoLaunchParam ¶m) +{ + LOGD("int AutoLaunchCallBack"); + EXPECT_TRUE(identifier == g_identifierA); + param.notifier = TestAutoLaunchNotifier; + return true; +} +} + +/** + * @tc.name: AutoLaunch007 + * @tc.desc: enhancement callback return true + * @tc.type: FUNC + * @tc.require: AR000EPARJ + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch007, TestSize.Level3) +{ + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps: step1. SetAutoLaunchRequestCallback + * @tc.expected: step1. success. + */ + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback( + std::bind(AutoLaunchCallBack, std::placeholders::_1, std::placeholders::_2, observer, true), DBType::DB_KV); + /** + * @tc.steps: step2. RunCommunicatorLackCallback + * @tc.expected: step2. success. + */ + LabelType label(g_identifierA.begin(), g_identifierA.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step3. PutSyncData key1 value1 + * @tc.expected: step3. notifier WRITE_OPENED + */ + PutSyncData(g_propA, KEY1, VALUE1); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + EXPECT_TRUE(g_statusMap[g_identifierA] == WRITE_OPENED); + g_statusMap.clear(); + g_finished = false; + } + EXPECT_TRUE(observer->GetCallCount() == 1); // only A + /** + * @tc.steps: step4. PutSyncData key1 value2 + * @tc.expected: step4. ConflictNotifierCallback + */ + PutSyncData(g_propA, KEY1, VALUE2); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + g_finished = false; + } + /** + * @tc.steps: step5. wait life cycle ,db close + * @tc.expected: step5. notifier WRITE_CLOSED + */ + SetLifeCycleTime(g_propA); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + EXPECT_TRUE(g_statusMap[g_identifierA] == WRITE_CLOSED); + g_statusMap.clear(); + g_finished = false; + } + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(nullptr, DBType::DB_KV); + delete observer; +} + +/** + * @tc.name: AutoLaunch008 + * @tc.desc: enhancement callback return false + * @tc.type: FUNC + * @tc.require: AR000EPARJ + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch008, TestSize.Level3) +{ + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps: step1. SetAutoLaunchRequestCallback + * @tc.expected: step1. success. + */ + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback( + std::bind(AutoLaunchCallBack, std::placeholders::_1, std::placeholders::_2, observer, false), DBType::DB_KV); + /** + * @tc.steps: step2. RunCommunicatorLackCallback + * @tc.expected: step2. success. + */ + LabelType label(g_identifierA.begin(), g_identifierA.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step3. PutSyncData key1 value1 + * @tc.expected: step3. db not open + */ + PutSyncData(g_propA, KEY1, VALUE1); + PutSyncData(g_propA, KEY1, VALUE2); + + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + EXPECT_TRUE(observer->GetCallCount() == 0); + EXPECT_TRUE(g_finished == false); + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(nullptr, DBType::DB_KV); + delete observer; +} + +/** + * @tc.name: AutoLaunch009 + * @tc.desc: enhancement callback return bad param + * @tc.type: FUNC + * @tc.require: AR000EPARJ + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch009, TestSize.Level3) +{ + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps: step1. SetAutoLaunchRequestCallback + * @tc.expected: step1. success. + */ + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(AutoLaunchCallBackBadParam, DBType::DB_KV); + /** + * @tc.steps: step2. RunCommunicatorLackCallback + * @tc.expected: step2. success. + */ + LabelType label(g_identifierA.begin(), g_identifierA.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step3. PutSyncData key1 value1 + * @tc.expected: step3. db not open, notify INVALID_PARAM + */ + PutSyncData(g_propA, KEY1, VALUE1); + PutSyncData(g_propA, KEY1, VALUE2); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + EXPECT_TRUE(observer->GetCallCount() == 0); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + EXPECT_TRUE(g_statusMap[DBCommon::TransferHashString("--")] == INVALID_PARAM); + g_statusMap.clear(); + g_finished = false; + } + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(nullptr, DBType::DB_KV); + delete observer; +} + +/** + * @tc.name: AutoLaunch010 + * @tc.desc: enhancement nullptr callback + * @tc.type: FUNC + * @tc.require: AR000EPARJ + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch010, TestSize.Level3) +{ + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps: step1. SetAutoLaunchRequestCallback, then set nullptr + * @tc.expected: step1. success. + */ + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback( + std::bind(AutoLaunchCallBack, std::placeholders::_1, std::placeholders::_2, observer, false), DBType::DB_KV); + + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(nullptr, DBType::DB_KV); + /** + * @tc.steps: step2. RunCommunicatorLackCallback + * @tc.expected: step2. success. + */ + LabelType label(g_identifierA.begin(), g_identifierA.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step3. PutSyncData key1 value1 + * @tc.expected: step3. db not open + */ + PutSyncData(g_propA, KEY1, VALUE1); + PutSyncData(g_propA, KEY1, VALUE2); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + EXPECT_TRUE(observer->GetCallCount() == 0); + EXPECT_TRUE(g_finished == false); + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(nullptr, DBType::DB_KV); + delete observer; +} + +/** + * @tc.name: AutoLaunch011 + * @tc.desc: enhancement GetKvStoreIdentifier + * @tc.type: FUNC + * @tc.require: AR000EPARJ + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch011, TestSize.Level3) +{ + EXPECT_EQ(KvStoreDelegateManager::GetKvStoreIdentifier("", APP_ID, STORE_ID_0), ""); + EXPECT_EQ(KvStoreDelegateManager::GetKvStoreIdentifier( + USER_ID, APP_ID, STORE_ID_0), DBCommon::TransferHashString(USER_ID + "-" + APP_ID + "-" + STORE_ID_0)); +} + +/** + * @tc.name: AutoLaunch012 + * @tc.desc: CommunicatorLackCallback + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch012, TestSize.Level3) +{ + /** + * @tc.steps: step1. right param A B enable + * @tc.expected: step1. success. + */ + AutoLaunchOption option; + option.notifier = ConflictNotifierCallback; + option.conflictType = CONFLICT_FOREIGN_KEY_ONLY; + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, TestAutoLaunchNotifier, option); + EXPECT_TRUE(errCode == E_OK); + AutoLaunchOption option1; + option1.notifier = nullptr; + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, nullptr, option1); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step2. RunCommunicatorLackCallback + * @tc.expected: step2. success. + */ + LabelType label(g_identifierA.begin(), g_identifierA.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + PutSyncData(g_propA, KEY1, VALUE1); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + EXPECT_TRUE(g_statusMap[g_identifierA] == WRITE_OPENED); + g_statusMap.clear(); + g_finished = false; + } + /** + * @tc.steps: step3. PutSyncData key1 value2 + * @tc.expected: step3. ConflictNotifierCallback + */ + PutSyncData(g_propA, KEY1, VALUE2); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + g_finished = false; + } + /** + * @tc.steps: step4. wait life cycle ,db close + * @tc.expected: step4. notifier WRITE_CLOSED + */ + SetLifeCycleTime(g_propA); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + EXPECT_TRUE(g_statusMap[g_identifierA] == WRITE_CLOSED); + g_statusMap.clear(); + g_finished = false; + } + /** + * @tc.steps: step5. param A B disable + * @tc.expected: step5. OK + */ + std::string identifierA = g_propA.GetStringProp(KvDBProperties::DUAL_TUPLE_IDENTIFIER_DATA, ""); + std::string userIdA = g_propA.GetStringProp(KvDBProperties::USER_ID, ""); + std::string identifierB = g_propB.GetStringProp(KvDBProperties::DUAL_TUPLE_IDENTIFIER_DATA, ""); + std::string userIdB = g_propB.GetStringProp(KvDBProperties::USER_ID, ""); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB, g_dualIdentifierB, USER_ID); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA, g_dualIdentifierA, USER_ID); + EXPECT_TRUE(errCode == E_OK); +} + +/** + * @tc.name: AutoLaunch013 + * @tc.desc: online callback + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBAutoLaunchUnitTest, AutoLaunch013, TestSize.Level3) +{ + auto notifier = [] (const std::string &userId, const std::string &appId, + const std::string &storeId, AutoLaunchStatus status) { + LOGD("int AutoLaunch013 notifier status:%d", status); + }; + /** + * @tc.steps: step1. right param b c enable, a SetAutoLaunchRequestCallback + * @tc.expected: step1. success. + */ + AutoLaunchOption option; + option.notifier = nullptr; + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, notifier, option) == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propC, notifier, option) == E_OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback( + std::bind(AutoLaunchCallBack, std::placeholders::_1, std::placeholders::_2, observer, true), DBType::DB_KV); + + /** + * @tc.steps: step2. RunOnConnectCallback RunCommunicatorLackCallback + * @tc.expected: step2. success. + */ + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, true); + LabelType label(g_identifierA.begin(), g_identifierA.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + + /** + * @tc.steps: step3. PutSyncData + * @tc.expected: step3. notifier WRITE_OPENED + */ + PutSyncData(g_propA, KEY1, VALUE1); + PutSyncData(g_propB, KEY1, VALUE1); + PutSyncData(g_propC, KEY1, VALUE1); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + EXPECT_TRUE(g_statusMap[g_identifierA] == WRITE_OPENED); + g_statusMap.clear(); + g_finished = false; + } + /** + * @tc.steps: step4. PutSyncData key1 value2 + * @tc.expected: step4. ConflictNotifierCallback + */ + PutSyncData(g_propA, KEY1, VALUE2); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + g_finished = false; + } + /** + * @tc.steps: step5. wait life cycle ,db close + * @tc.expected: step5. notifier WRITE_CLOSED + */ + SetLifeCycleTime(g_propA); + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, [] {return g_finished;}); + EXPECT_TRUE(g_statusMap[g_identifierA] == WRITE_CLOSED); + g_statusMap.clear(); + g_finished = false; + } + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(nullptr, DBType::DB_KV); + delete observer; + /** + * @tc.steps: step4. param A B disable + * @tc.expected: step4. notifier WRITE_CLOSED + */ + EXPECT_TRUE(RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB, g_dualIdentifierB, USER_ID) + == E_OK); + EXPECT_TRUE(RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierC, g_dualIdentifierC, USER_ID) + == E_OK); + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, false); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_common_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_common_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..33abc92711ea65812f86ee7344012622a3b1da75 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_common_test.cpp @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_errno.h" +#include "db_common.h" +#include "distributeddb_data_generate_unit_test.h" +#include "log_print.h" +#include "platform_specific.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + std::string g_testDir; + + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + std::placeholders::_1, std::placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); +} + +class DistributedDBCommonTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBCommonTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBCommonTest::TearDownTestCase(void) {} + +void DistributedDBCommonTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + DistributedDBToolsUnitTest::TestDirInit(g_testDir); +} + +void DistributedDBCommonTest::TearDown(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGI("rm test db files error!"); + } +} + +/** + * @tc.name: RemoveAllFilesOfDirectory + * @tc.desc: Test delete all file and dir. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, RemoveAllFilesOfDirectory, TestSize.Level1) +{ + EXPECT_EQ(DBCommon::CreateDirectory(g_testDir + "/dirLevel1_1/"), E_OK); + EXPECT_EQ(DBCommon::CreateDirectory(g_testDir + "/dirLevel1_1/" + "/dirLevel2_1/"), E_OK); + EXPECT_EQ(DBCommon::CreateDirectory(g_testDir + "/dirLevel1_1/" + "/dirLevel2_2/"), E_OK); + EXPECT_EQ(DBCommon::CreateDirectory(g_testDir + "/dirLevel1_1/" + "/dirLevel2_2/" + "/dirLevel3_1/"), E_OK); + + EXPECT_EQ(OS::CreateFileByFileName(g_testDir + "/fileLevel1_1"), E_OK); + EXPECT_EQ(OS::CreateFileByFileName(g_testDir + "/dirLevel1_1/" + "/fileLevel2_1"), E_OK); + EXPECT_EQ(DBCommon::CreateDirectory(g_testDir + "/dirLevel1_1/" + "/dirLevel2_2/" + + "/dirLevel3_1/"+ "/fileLevel4_1/"), E_OK); + + EXPECT_EQ(DBCommon::RemoveAllFilesOfDirectory(g_testDir), E_OK); + + EXPECT_EQ(OS::CheckPathExistence(g_testDir), false); +} + +#ifdef RUNNING_ON_LINUX +/** + * @tc.name: SameProcessReLockFile + * @tc.desc: Test same process repeat lock same file. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, SameProcessReLockFile, TestSize.Level1) +{ + // block mode + EXPECT_EQ(OS::CreateFileByFileName(g_testDir + "/blockmode"), E_OK); + OS::FileHandle fd; + EXPECT_EQ(OS::OpenFile(g_testDir + "/blockmode", fd), E_OK); + + EXPECT_EQ(OS::FileLock(fd, true), E_OK); + EXPECT_EQ(OS::FileLock(fd, true), E_OK); + + // normal mode + OS::FileHandle fd2; + EXPECT_EQ(OS::CreateFileByFileName(g_testDir + "/normalmode"), E_OK); + EXPECT_EQ(OS::OpenFile(g_testDir + "/normalmode", fd2), E_OK); + EXPECT_EQ(OS::FileLock(fd2, true), E_OK); + EXPECT_EQ(OS::FileLock(fd2, true), E_OK); + + // unlock + EXPECT_EQ(OS::FileUnlock(fd), E_OK); + EXPECT_EQ(OS::FileUnlock(fd2), E_OK); // unlock success will close fd +} + +/** + * @tc.name: SameProcessReUnLockFile + * @tc.desc: Test same process repeat lock same file. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, SameProcessReUnLockFile, TestSize.Level1) +{ + // unlock normal file twice + EXPECT_EQ(OS::CreateFileByFileName(g_testDir + "/normalmode"), E_OK); + OS::FileHandle fd; + EXPECT_EQ(OS::OpenFile(g_testDir + "/normalmode", fd), E_OK); + EXPECT_EQ(OS::FileUnlock(fd), E_OK); + EXPECT_EQ(OS::FileUnlock(fd), E_OK); // unlock success will close fd + + EXPECT_EQ(OS::FileLock(fd, true), -E_SYSTEM_API_FAIL); + EXPECT_EQ(OS::FileLock(fd, true), -E_SYSTEM_API_FAIL); + ASSERT_EQ(fd.handle, -1); + + // block mode + EXPECT_EQ(OS::CreateFileByFileName(g_testDir + "/blockmode"), E_OK); + EXPECT_EQ(OS::OpenFile(g_testDir + "/blockmode", fd), E_OK); + + EXPECT_EQ(OS::FileLock(fd, false), E_OK); + EXPECT_EQ(OS::FileLock(fd, false), E_OK); + + EXPECT_EQ(OS::FileUnlock(fd), E_OK); + EXPECT_EQ(OS::FileUnlock(fd), E_OK); +} + +/** + * @tc.name: CalFileSizeTest + * @tc.desc: Test the file size for function test and the performance test. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBCommonTest, CalFileSizeTest, TestSize.Level1) +{ + std::string filePath = g_testDir + "/testFileSize"; + std::ofstream ofs(filePath, std::ofstream::out); + ASSERT_TRUE(ofs.good()); + ofs << "test file size"; + ofs.close(); + uint64_t fileSize = 0; + EXPECT_EQ(OS::CalFileSize(filePath, fileSize), E_OK); + EXPECT_GT(fileSize, 0ULL); + EXPECT_EQ(OS::RemoveFile(filePath), E_OK); +} + +// Distributed db is not recommended to use multiple processes to access +// This testcase only guard for some wrong use on current product +#if defined(RUN_MULTI_PROCESS_TEST) +namespace { +// use file sync diff process information +bool waitForStep(int step, int retryTimes) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + while (retryTimes >= 0 && !OS::CheckPathExistence(g_testDir + "/LOCK_step_" + std::to_string(step))) { + retryTimes = retryTimes - 1; // wait 10ms one times + std::this_thread::sleep_for(std::chrono::milliseconds(10)); // once 10 ms + } + return (retryTimes > 0); +} + +void createStepFlag(int step) +{ + EXPECT_EQ(OS::CreateFileByFileName(g_testDir + "/LOCK_step_" + std::to_string(step)), E_OK); +} +} + +/** + * @tc.name: DiffProcessLockFile + * @tc.desc: Test different process repeat lock same file. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, DiffProcessLockFile, TestSize.Level1) +{ + OS::FileHandle fd; + EXPECT_EQ(OS::OpenFile(g_testDir + DBConstant::DB_LOCK_POSTFIX, fd), E_OK); + EXPECT_EQ(OS::FileLock(fd, false), E_OK); + sleep(1); + LOGI("begin fork new process!!"); + pid_t pid = fork(); + ASSERT_TRUE(pid >= 0); + if (pid < 0) { + return; + } + else if (pid == 0) { + LOGI("child process begin!"); + OS::FileHandle ChildFd; + EXPECT_EQ(OS::OpenFile(g_testDir + DBConstant::DB_LOCK_POSTFIX, ChildFd), E_OK); + ASSERT_TRUE(waitForStep(1, 10)); + EXPECT_EQ(OS::FileLock(ChildFd, false), -E_BUSY); + createStepFlag(2); + EXPECT_EQ(OS::CloseFile(ChildFd), E_OK); + exit(0); + } else { + LOGI("main process begin!"); + EXPECT_EQ(OS::FileLock(fd, false), E_OK); + createStepFlag(1); + + ASSERT_TRUE(waitForStep(2, 100)); + EXPECT_EQ(OS::CloseFile(fd), E_OK); // fd close, lock invalid + } +} + +/** + * @tc.name: DiffProcessLockFileBlocked + * @tc.desc: Test different process repeat lock same file. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, DiffProcessLockFileBlocked, TestSize.Level1) +{ + EXPECT_EQ(OS::CreateFileByFileName(g_testDir + DBConstant::DB_LOCK_POSTFIX), E_OK); + OS::FileHandle fd; + EXPECT_EQ(OS::OpenFile(g_testDir + DBConstant::DB_LOCK_POSTFIX, fd), E_OK); + EXPECT_EQ(OS::FileLock(fd, true), E_OK); + sleep(1); + LOGI("begin fork new process!!"); + int count = 10; // wait 10 times 10 ms for block wait + pid_t pid = fork(); + ASSERT_TRUE(pid >= 0); + if (pid < 0) { + return; + } + else if (pid == 0) { + LOGI("child process begin!"); + EXPECT_FALSE(OS::CheckPathExistence(g_testDir + "/LOCK_step_1")); + OS::FileHandle ChildFd; + EXPECT_EQ(OS::OpenFile(g_testDir + DBConstant::DB_LOCK_POSTFIX, ChildFd), E_OK); + EXPECT_EQ(OS::FileLock(ChildFd, true), E_OK); + createStepFlag(1); + EXPECT_EQ(OS::FileUnlock(ChildFd), E_OK); + LOGI("child process finish!"); + exit(0); + } else { + LOGI("main process begin!"); + while (count--) { + LOGI("main process waiting!"); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); // once 10 ms + } + ASSERT_FALSE(waitForStep(1, 10)); + EXPECT_EQ(OS::FileUnlock(fd), E_OK); + ASSERT_TRUE(waitForStep(1, 10)); + } +} + +/** + * @tc.name: DiffProcessGetDBBlocked + * @tc.desc: Test block other process get kvstore when db locked. + * @tc.type: FUNC + * @tc.require: AR000CQDV7 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, DiffProcessGetDBBlocked, TestSize.Level1) +{ + std::string storeId = "DiffProcessGetDBBlocked"; + std::string origId = USER_ID + "-" + APP_ID + "-" + storeId; + std::string identifier = DBCommon::TransferHashString(origId); + std::string hexDir = DBCommon::TransferStringToHex(identifier); + std::string lockFile = g_testDir + "/" + hexDir + DBConstant::DB_LOCK_POSTFIX; + EXPECT_EQ(DBCommon::CreateDirectory(g_testDir + "/" + hexDir), E_OK); + EXPECT_EQ(OS::CreateFileByFileName(lockFile), E_OK); + LOGI("Create lock file[%s]", lockFile.c_str()); + + LOGI("begin fork new process!!"); + pid_t pid = fork(); + OS::FileHandle fd; + ASSERT_TRUE(pid >= 0); + if (pid == 0) { + LOGI("child process begin!"); + ASSERT_TRUE(waitForStep(1, 10)); + KvStoreNbDelegate::Option option = {true, false, false}; + option.isNeedIntegrityCheck = true; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == BUSY); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + createStepFlag(2); + exit(0); + } else { + LOGI("main process begin!"); + EXPECT_EQ(OS::OpenFile(lockFile, fd), E_OK); + EXPECT_EQ(OS::FileLock(fd, false), E_OK); + createStepFlag(1); + } + + // Prevent the child process from not being completed, the main process ends to clean up resources + EXPECT_TRUE(waitForStep(2, 1000)); + EXPECT_EQ(OS::FileUnlock(fd), E_OK); +} + +/** + * @tc.name: DiffProcessDeleteDBBlocked + * @tc.desc: Test block other process delete kvstore when db locked. + * @tc.type: FUNC + * @tc.require: AR000CQDV7 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, DiffProcessDeleteDBBlocked, TestSize.Level1) +{ + std::string storeId = "DiffProcessDeleteDBBlocked"; + std::string origId = USER_ID + "-" + APP_ID + "-" + storeId; + std::string identifier = DBCommon::TransferHashString(origId); + std::string hexDir = DBCommon::TransferStringToHex(identifier); + std::string lockFile = g_testDir + "/" + hexDir + DBConstant::DB_LOCK_POSTFIX; + EXPECT_EQ(DBCommon::CreateDirectory(g_testDir + "/" + hexDir), E_OK); + EXPECT_EQ(OS::CreateFileByFileName(lockFile), E_OK); + LOGI("Create lock file[%s]", lockFile.c_str()); + + KvStoreNbDelegate::Option option = {true, false, false}; + option.isNeedIntegrityCheck = true; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + LOGI("begin fork new process!!"); + pid_t pid = fork(); + OS::FileHandle fd; + ASSERT_TRUE(pid >= 0); + if (pid == 0) { + LOGI("child process begin!"); + ASSERT_TRUE(waitForStep(1, 10)); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), BUSY); + createStepFlag(2); + exit(0); + } else { + LOGI("main process begin!"); + EXPECT_EQ(OS::OpenFile(lockFile, fd), E_OK); + EXPECT_EQ(OS::FileLock(fd, false), E_OK); + createStepFlag(1); + } + + // Prevent the child process from not being completed, the main process ends to clean up resources + EXPECT_TRUE(waitForStep(2, 1000)); + EXPECT_EQ(OS::FileUnlock(fd), E_OK); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); +} + +/** + * @tc.name: DiffProcessGetDBBlocked001 + * @tc.desc: Test block other process get kvstore when db locked. + * @tc.type: FUNC + * @tc.require: AR000CQDV7 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, DiffProcessGetDBBlocked001, TestSize.Level1) +{ + std::string storeId = "DiffProcessGetDBBlocked001"; + std::string origId = USER_ID + "-" + APP_ID + "-" + storeId; + std::string identifier = DBCommon::TransferHashString(origId); + std::string hexDir = DBCommon::TransferStringToHex(identifier); + std::string lockFile = g_testDir + "/" + hexDir + DBConstant::DB_LOCK_POSTFIX; + EXPECT_EQ(DBCommon::CreateDirectory(g_testDir + "/" + hexDir), E_OK); + EXPECT_EQ(OS::CreateFileByFileName(lockFile), E_OK); + LOGI("Create lock file[%s]", lockFile.c_str()); + + LOGI("begin fork new process!!"); + pid_t pid = fork(); + OS::FileHandle fd; + ASSERT_TRUE(pid >= 0); + if (pid == 0) { + LOGI("child process begin!"); + ASSERT_TRUE(waitForStep(1, 10)); + KvStoreNbDelegate::Option option = {true, false, false}; + option.isNeedIntegrityCheck = true; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + createStepFlag(2); + exit(0); + } else { + LOGI("main process begin!"); + EXPECT_EQ(OS::OpenFile(lockFile, fd), E_OK); + EXPECT_EQ(OS::FileLock(fd, false), E_OK); + createStepFlag(1); + } + ASSERT_TRUE(waitForStep(1, 100)); + + EXPECT_EQ(OS::FileUnlock(fd), E_OK); + + ASSERT_TRUE(waitForStep(2, 100)); +} + +/** + * @tc.name: DiffProcessGetDB + * @tc.desc: Test block other process get kvstore. + * @tc.type: FUNC + * @tc.require: AR000CQDV7 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, DiffProcessGetDB, TestSize.Level1) +{ + std::string storeId = "DiffProcessGetDB"; + KvStoreNbDelegate::Option option = {true, false, false}; + option.isNeedIntegrityCheck = true; + LOGI("begin fork new process!!"); + pid_t pid = fork(); + ASSERT_TRUE(pid >= 0); + if (pid == 0) { + LOGI("child process begin!"); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + createStepFlag(2); + EXPECT_TRUE(waitForStep(1, 1000)); + exit(0); + } else { + LOGI("main process begin!"); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + createStepFlag(1); + } + EXPECT_TRUE(waitForStep(2, 100)); + // Prevent the child process from not being completed, the main process ends to clean up resources + g_mgr.CloseKvStore(g_kvNbDelegatePtr); +} + +/** + * @tc.name: DiffProcessDeleteDB + * @tc.desc: Test block other process delete kvstore. + * @tc.type: FUNC + * @tc.require: AR000CQDV7 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, DiffProcessDeleteDB, TestSize.Level1) +{ + std::string storeId = "DiffProcessGetDB"; + KvStoreNbDelegate::Option option = {true, false, false}; + option.isNeedIntegrityCheck = true; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + LOGI("begin fork new process!!"); + pid_t pid = fork(); + ASSERT_TRUE(pid >= 0); + if (pid == 0) { + LOGI("child process begin!"); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + createStepFlag(2); + EXPECT_TRUE(waitForStep(1, 1000)); + exit(0); + } else { + LOGI("main process begin!"); + g_mgr.DeleteKvStore(storeId); + createStepFlag(1); + } + EXPECT_TRUE(waitForStep(2, 100)); + + // Prevent the child process from not being completed, the main process ends to clean up resources + EXPECT_TRUE(waitForStep(1, 100)); +} + +/** + * @tc.name: DiffProcessGetAndDeleteDB + * @tc.desc: Test block other process delete kvstore. + * @tc.type: FUNC + * @tc.require: AR000CQDV7 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBCommonTest, DiffProcessGetAndDeleteDB, TestSize.Level1) +{ + std::string storeId = "DiffProcessGetAndDeleteDB"; + KvStoreNbDelegate::Option option = {true, false, false}; + option.isNeedIntegrityCheck = true; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + LOGI("begin fork new process!!"); + pid_t pid = fork(); + ASSERT_TRUE(pid >= 0); + if (pid == 0) { + LOGI("child process begin!"); + g_mgr.DeleteKvStore(storeId); // one process OK, one process NOT_FOUND + createStepFlag(2); + EXPECT_TRUE(waitForStep(1, 1000)); + exit(0); + } else { + LOGI("main process begin!"); + g_mgr.DeleteKvStore(storeId); + createStepFlag(1); + } + EXPECT_TRUE(waitForStep(2, 100)); + + // Prevent the child process from not being completed, the main process ends to clean up resources + EXPECT_TRUE(waitForStep(1, 1000)); +} +#endif +#endif + diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_data_compression_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_data_compression_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e3754c6530831edfbf73133870182a910d2d1052 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_data_compression_test.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "distributeddb_tools_unit_test.h" +#include "data_compression.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { +#ifndef OMIT_ZLIB +// LENGTH IS 680. +unsigned char g_srcStr[] = + "I come from Alabama with my banjo on my knee," + "I'm going to Louisiana, my true love for to see," + "It rained all night the day I left," + "The weather it was dry," + "The sun so hot, I froze to death," + "Susanna don't you cry," + "Oh, Susanna," + "Oh don't you cry for me," + "For I come from Alabama," + "With my banjo on my knee," + "I had a dream the other night when everything was still," + "I thought I saw Susanna a-coming down the hill," + "The buckwheat cake was in her mouth," + "The tear was in her eye," + "Says I, I'm coming from the south," + "Susanna, don't you cry," + "Oh, Susanna," + "Oh don't you cry for me," + "I'm going to Louisiana," + "With my banjo on my knee," + "Oh, Susanna," + "Oh don't you cry for me," + "I'm going to Louisiana," + "With my banjo on my knee."; +} +#endif +class DistributedDBDataCompressionTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBDataCompressionTest::SetUpTestCase(void) +{} + +void DistributedDBDataCompressionTest::TearDownTestCase(void) +{} + +void DistributedDBDataCompressionTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBDataCompressionTest::TearDown(void) +{} + +/** + * @tc.name: DataCompression1 + * @tc.desc: To test the function compress and uncompress works well in normal situation. + * @tc.type: FUNC + * @tc.require: AR000G3QTT + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBDataCompressionTest, DataCompression1, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare a source data. And compress it. + * @tc.expected: step1. Compress successfully. Compressed data length is less than srcLen. + */ +#ifndef OMIT_ZLIB + const int origLen = sizeof(g_srcStr); + vector srcData(g_srcStr, g_srcStr + sizeof(g_srcStr)); + + vector compressedData; + EXPECT_EQ(DataCompression::GetInstance(CompressAlgorithm::ZLIB)->Compress(srcData, compressedData), E_OK); + EXPECT_LT(compressedData.size(), srcData.size()); + + /** + * @tc.steps:step2. Uncompress the compressed data. + * @tc.expected: step2. Uncompress successfully. Uncompressed data equals to source data. + */ + vector uncompressedData; + EXPECT_EQ(DataCompression::GetInstance(CompressAlgorithm::ZLIB)->Uncompress( + compressedData, uncompressedData, origLen), E_OK); + EXPECT_EQ(srcData, uncompressedData); +#endif // OMIT_ZLIB +} + +/** + * @tc.name: DataCompression2 + * @tc.desc: To test uncompress failed when compressed is destroyed. + * @tc.type: FUNC + * @tc.require: AR000G3QTT + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBDataCompressionTest, DataCompression2, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare a source data. And compress it. + * @tc.expected: step1. Compress successfully. Compressed data length is less than srcLen. + */ +#ifndef OMIT_ZLIB + const int origLen = sizeof(g_srcStr); + vector srcData(g_srcStr, g_srcStr + sizeof(g_srcStr)); + + vector compressedData; + EXPECT_EQ(DataCompression::GetInstance(CompressAlgorithm::ZLIB)->Compress(srcData, compressedData), E_OK); + EXPECT_LT(compressedData.size(), srcData.size()); + + /** + * @tc.steps:step2. Destroy the compressed data. + */ + *(compressedData.begin()) = ~*(compressedData.begin()); + + /** + * @tc.steps:step3. Uncompress the compressed data. + * @tc.expected: step3. Uncompressed failed and return -E_SYSTEM_API_FAIL. + */ + vector uncompressedData; + EXPECT_EQ(DataCompression::GetInstance(CompressAlgorithm::ZLIB)->Uncompress( + compressedData, uncompressedData, origLen), -E_SYSTEM_API_FAIL); +#endif // OMIT_ZLIB +} + +/** + * @tc.name: DataCompression3 + * @tc.desc: To test uncompress works when bufferLen is larger but under 30M limit, + and uncompress failed when bufferLen is beyond the 30M limit. + * @tc.type: FUNC + * @tc.require: AR000G3QTT + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBDataCompressionTest, DataCompression3, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare a source data. And compress it. + * @tc.expected: step1. Compress successfully. Compressed data length is less than srcLen. + */ +#ifndef OMIT_ZLIB + vector srcData(g_srcStr, g_srcStr + sizeof(g_srcStr)); + + vector compressedData; + EXPECT_EQ(DataCompression::GetInstance(CompressAlgorithm::ZLIB)->Compress(srcData, compressedData), E_OK); + EXPECT_LT(compressedData.size(), srcData.size()); + + /** + * @tc.steps:step2. Set origLen a larger num under 30M limit. And uncompress. + * @tc.expected: step1. Uncompress successfully. + */ + vector uncompressedData; + int incorrectLen = 10000; + EXPECT_EQ(DataCompression::GetInstance(CompressAlgorithm::ZLIB)->Uncompress( + compressedData, uncompressedData, incorrectLen), E_OK); + EXPECT_EQ(srcData, uncompressedData); + + /** + * @tc.steps:step2. Set origLen a larger num beyond 30M limit. And uncompress. + * @tc.expected: step1. Uncompress failed and return E_INVALID_ARGS. + */ + uncompressedData.clear(); + incorrectLen = 31457281; // 30M + 1 + EXPECT_EQ(DataCompression::GetInstance(CompressAlgorithm::ZLIB)->Uncompress( + compressedData, uncompressedData, incorrectLen), -E_INVALID_ARGS); +#endif // OMIT_ZLIB +} diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..74c440cf5760a9493eb95b611382a77b7aabae68 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "distributeddb_data_generate_unit_test.h" +#include "store_types.h" + +using namespace DistributedDB; + +namespace DistributedDBUnitTest { +void GenerateKey(int keyCount, int startPosition, Key &keyTest) +{ + if (keyCount <= 0) { + return; + } + int i; + for (i = 0; i < keyCount; i++) { + keyTest.push_back(KEY_NUM[(i + startPosition) % NUM_LENGTH]); + } +} + +void GenerateValue(int valueCount, int startPosition, Value &valueTest) +{ + if (valueCount <= 0) { + return; + } + int i; + for (i = 0; i < valueCount; i++) { + valueTest.push_back(VALUE_LETTER[(i + startPosition) % LETTER_LENGTH]); + } +} + +void GenerateEntry(int entryCount, int startPosition, Entry &entryTest) +{ + if (entryCount <= 0) { + return; + } + GenerateKey(entryCount, startPosition, entryTest.key); + GenerateValue(entryCount, startPosition, entryTest.value); +} + +void GenerateEntryVector(int entryVectorCount, int entryCount, std::vector &entrysTest) +{ + if (entryVectorCount <= 0 || entryCount <= 0) { + return; + } + int i; + for (i = 0; i < entryVectorCount; i++) { + Entry entry; + GenerateEntry(entryCount, i, entry); + entrysTest.push_back(entry); + } +} + +void GenerateRecords(int recordNum, std::vector &entries, std::vector &keys, int keySize, int valSize) +{ + Entry entry; + // start from index 1 + for (int recordIndex = 1; recordIndex <= recordNum; ++recordIndex) { + std::string cntStr = std::to_string(recordIndex); + int len = cntStr.length(); + if (keySize <= len) { + break; + } + if (valSize <= len) { + break; + } + + entry.key.assign((keySize - len), '0'); + entry.value.assign((valSize - len), 'v'); + for (auto item = cntStr.begin(); item != cntStr.end(); ++item) { + entry.key.push_back(*item); + entry.value.push_back(*item); + } + entries.push_back(entry); + keys.push_back(entry.key); + + entry.key.clear(); + entry.value.clear(); + } +} +} // namespace DistributedDBUnitTest \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.h b/mock/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.h new file mode 100644 index 0000000000000000000000000000000000000000..3b6f8d8efef990940ada15ff4e02779217e9cb61 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_DATA_GENERATE_UNIT_H +#define DISTRIBUTEDDB_DATA_GENERATE_UNIT_H + +#include +#include +#include "distributeddb_tools_unit_test.h" +#include "store_types.h" + +namespace DistributedDBUnitTest { +// define some variables to init a KvStoreDelegateManager object. +const std::string APP_ID = "app0"; +const std::string SCHEMA_APP_ID = "app1"; +const std::string USER_ID = "user0"; + +const std::string STORE_ID_LOCAL = "distributed_local_db_test"; +const std::string STORE_ID_SYNC = "distributed_sync_db_test"; +const std::string STORE_ID_1 = "distributed_db_test1"; +const std::string STORE_ID_2 = "distributed_db_test2"; +const std::string STORE_ID_3 = "distributed_db_test3"; +const std::string STORE_ID_4 = "distributed_db_test4"; +const std::string STORE_ID_5 = "distributed_db_test5"; +const std::string STORE_ID_6 = "distributed_db_test6"; +const std::string STORE_ID_7 = "distributed_db_test7"; +const std::string STORE_ID_8 = "distributed_db_test8"; + +const int NUM_LENGTH = 10; +const int LETTER_LENGTH = 52; +const uint8_t KEY_NUM[NUM_LENGTH] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; +const uint8_t VALUE_LETTER[LETTER_LENGTH] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'L', 'M', 'L', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', + 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', + 'j', 'l', 'm', 'l', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z' +}; + +const DistributedDB::Key KEY_1 = {'1'}; +const DistributedDB::Value VALUE_1 = {'a'}; +const DistributedDB::Key KEY_2 = {'2'}; +const DistributedDB::Value VALUE_2 = {'b'}; +const DistributedDB::Key KEY_3 = {'3'}; +const DistributedDB::Value VALUE_3 = {'c'}; +const DistributedDB::Key KEY_4 = {'4'}; +const DistributedDB::Value VALUE_4 = {'d'}; +const DistributedDB::Key KEY_5 = {'5'}; +const DistributedDB::Value VALUE_5 = {'e'}; +const DistributedDB::Key KEY_6 = {'6'}; +const DistributedDB::Value VALUE_6 = {'f'}; +const DistributedDB::Key KEY_7 = {'7'}; +const DistributedDB::Value VALUE_7 = {'g'}; + +const DistributedDB::Key NULL_KEY_1; +const DistributedDB::Value NULL_VALUE_1; + +const DistributedDB::Entry ENTRY_1 = {KEY_1, VALUE_1}; +const DistributedDB::Entry ENTRY_2 = {KEY_2, VALUE_2}; +const DistributedDB::Entry NULL_ENTRY_1 = {NULL_KEY_1, VALUE_1}; +const DistributedDB::Entry NULL_ENTRY_2 = {KEY_1, NULL_VALUE_1}; + +const DistributedDB::Entry ENTRY_3 = {KEY_3, VALUE_3}; +const DistributedDB::Entry ENTRY_4 = {KEY_4, VALUE_4}; + +const DistributedDB::Entry KV_ENTRY_1 = {KEY_1, VALUE_1}; +const DistributedDB::Entry KV_ENTRY_2 = {KEY_2, VALUE_2}; +const DistributedDB::Entry KV_ENTRY_3 = {KEY_3, VALUE_3}; +const DistributedDB::Entry KV_ENTRY_4 = {KEY_4, VALUE_4}; + +const std::vector ENTRY_VECTOR = {ENTRY_1, ENTRY_2}; + +const int DEFAULT_NB_KEY_VALUE_SIZE = 10; + +// generate a key, has keyCount chars, from KEY_NUM[startPosition], return keyTest +void GenerateKey(int keyCount, int startPosition, DistributedDB::Key &keyTest); + +// generate a value, has valueCount chars, from VALUE_LETTER[startPosition], return valueTest +void GenerateValue(int valueCount, int startPosition, DistributedDB::Value &valueTest); + +/* + * generate an entry, entry.key and entry.value have valueCount chars, + * from KEY_NUM[startPosition] and VALUE_LETTER[startPosition], return entryTest + */ +void GenerateEntry(int entryCount, int startPosition, DistributedDB::Entry &entryTest); + +/* + * generate a vector, vector.size() is entryVectorCount, entry.key and entry.value have valueCount chars, + * from KEY_NUM[startPosition] and VALUE_LETTER[startPosition], return entrysTest + */ +void GenerateEntryVector(int entryVectorCount, int entryCount, std::vector &entrysTest); + +void GenerateRecords(int recordNum, std::vector &entries, std::vector &keys, + int keySize = DEFAULT_NB_KEY_VALUE_SIZE, int valSize = DEFAULT_NB_KEY_VALUE_SIZE); +} // namespace DistributedDBUnitTest + +#endif // DISTRIBUTEDDB_DATA_GENERATE_UNIT_H diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_json_precheck_unit_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_json_precheck_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ffae09ca33fcd98fbb9ef472887a69d6454cbcda --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_json_precheck_unit_test.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_JSON +#include +#include +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "json_object.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; + +namespace { + const int MAX_DEPTH_FOR_TEST = 10; + const int STRING1_DEPTH = 12; + const int STRING3_DEPTH = 6; + + // nest depth = 12 and valid. + const string JSON_STRING1 = "{\"#14\":[[{\"#11\":{\"#8\":[{\"#5\":[[{\"#2\":[{\"#0\":\"value_\"},\"value_\"]," + "\"#3\":\"value_\"},\"value_\"],\"value_\"],\"#6\":\"value_\"},\"value_\"],\"#9\":\"value_\"}," + "\"#12\":\"value_\"},\"value_\"],\"value_\"],\"#15\":{\"#18\":{\"#16\":\"value_\"},\"#19\":\"value_\"}}"; + + // nest depth = 12 and invalid happens in nest depth = 2. + const string JSON_STRING2 = "{\"#17\":[\"just for mistake pls.[{\"#14\":[[{\"#11\":{\"#8\":{\"#5\":[{\"#2\":" + "{\"#0\":\"value_\"},\"#3\":\"value_\"},\"value_\"],\"#6\":\"value_\"},\"#9\":\"value_\"}," + "\"#12\":\"value_\"},\"value_\"],\"value_\"],\"#15\":\"value_\"},\"value_\"],\"value_\"]," + "\"#18\":{\"#21\":{\"#19\":\"value_\"},\"#22\":\"value_\"}}"; + + // nest depth = 6 and valid. + const string JSON_STRING3 = "{\"#5\":[{\"#2\":[[{\"#0\":\"value_\"},\"value_\"],\"value_\"],\"#3\":\"value_\"}," + "\"value_\"],\"#6\":{\"#7\":\"value_\",\"#8\":\"value_\"}}"; + + // nest depth = 6 and invalid happens in nest depth = 3. + const string JSON_STRING4 = "{\"#6\":[{\"#3\":\"just for mistake pls.[{\"#0\":[\"value_\"],\"#1\":\"value_\"}," + "\"value_\"],\"#4\":\"value_\"},\"value_\"],\"#7\":{\"#8\":\"value_\",\"#9\":\"value_\"}}"; + + // nest depth = 15 and invalid happens in nest depth = 11. + const string JSON_STRING5 = "{\"#35\":[{\"#29\":{\"#23\":{\"#17\":{\"#11\":{\"#8\":[{\"#5\":[{\"#2\":" + "\"just for mistake pls.[[[{\"#0\":\"value_\"},\"value_\"],\"value_\"],\"value_\"],\"#3\":\"value_\"}," + "\"value_\"],\"#6\":\"value_\"},\"value_\"],\"#9\":\"value_\"},\"#12\":{\"#13\":\"value_\"," + "\"#14\":\"value_\"}},\"#18\":{\"#19\":\"value_\",\"#20\":\"value_\"}},\"#24\":{\"#25\":\"value_\"," + "\"#26\":\"value_\"}},\"#30\":{\"#31\":\"value_\",\"#32\":\"value_\"}},\"value_\"],\"#36\":" + "{\"#37\":[\"value_\"],\"#38\":\"value_\"}}"; + + uint32_t g_oriMaxNestDepth = 0; +} + +class DistributedDBJsonPrecheckUnitTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown() {}; +}; + +void DistributedDBJsonPrecheckUnitTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Specifies a maximum nesting depth of 10. + */ + g_oriMaxNestDepth = JsonObject::SetMaxNestDepth(MAX_DEPTH_FOR_TEST); +} + +void DistributedDBJsonPrecheckUnitTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Reset nesting depth to origin value. + */ + JsonObject::SetMaxNestDepth(g_oriMaxNestDepth); +} + +void DistributedDBJsonPrecheckUnitTest::SetUp() +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +/** + * @tc.name: Precheck Valid String 001 + * @tc.desc: json string is legal + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBJsonPrecheckUnitTest, ParseValidString001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Check legal json string with nesting depth of 12. + * @tc.expected: step1. return value = 12. + */ + int errCode = E_OK; + int stepOne = JsonObject::CalculateNestDepth(JSON_STRING1, errCode); + EXPECT_TRUE(errCode == E_OK); + EXPECT_TRUE(stepOne == STRING1_DEPTH); + + /** + * @tc.steps: step2. Parsing of legal json string with nesting depth greater than 10 failed. + * @tc.expected: step2. Parsing result failed. + */ + JsonObject tempObj; + int stepTwo = tempObj.Parse(JSON_STRING1); + EXPECT_TRUE(stepTwo != E_OK); +} + +/** + * @tc.name: Precheck Valid String 002 + * @tc.desc: json string is legal + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBJsonPrecheckUnitTest, ParseValidString002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Check legal json string with nesting depth of 6. + * @tc.expected: step1. return value = 6. + */ + int errCode = E_OK; + int stepOne = JsonObject::CalculateNestDepth(JSON_STRING3, errCode); + EXPECT_TRUE(errCode == E_OK); + EXPECT_TRUE(stepOne == STRING3_DEPTH); + + /** + * @tc.steps: step2. Parsing of legal json string with nesting depth less than 10 success. + * @tc.expected: step2. Parsing result success. + */ + JsonObject tempObj; + int stepTwo = tempObj.Parse(JSON_STRING3); + EXPECT_TRUE(stepTwo == E_OK); +} + +/** + * @tc.name: Precheck invalid String 001 + * @tc.desc: The json string has been detected illegal before exceeding the specified nesting depth. + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBJsonPrecheckUnitTest, ParseInvalidString001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Parsing of illegal json string with nesting depth greater than 10 success. + * @tc.expected: step1. Parsing result failed. + */ + JsonObject tempObj; + int stepOne = tempObj.Parse(JSON_STRING2); + EXPECT_TRUE(stepOne != E_OK); +} + +/** + * @tc.name: Precheck invalid String 002 + * @tc.desc: The json string has been detected illegal before exceeding the specified nesting depth. + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBJsonPrecheckUnitTest, ParseInvalidString002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Parsing of illegal json string with nesting depth less than 10 success. + * @tc.expected: step1. Parsing result failed. + */ + JsonObject tempObj; + int stepOne = tempObj.Parse(JSON_STRING4); + EXPECT_TRUE(stepOne != E_OK); +} + +/** + * @tc.name: Precheck invalid String 003 + * @tc.desc: The json string has been detected illegal before exceeding the specified nesting depth. + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBJsonPrecheckUnitTest, ParseInvalidString003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Detect illegal json string with nesting depth greater than 10. + * @tc.expected: step1. return value > 10. + */ + int errCode = E_OK; + int stepOne = JsonObject::CalculateNestDepth(JSON_STRING5, errCode); + EXPECT_TRUE(errCode == E_OK); + EXPECT_TRUE(stepOne > MAX_DEPTH_FOR_TEST); + + /** + * @tc.steps: step2. Parsing of illegal json string with nesting depth greater than 10 success. + * @tc.expected: step2. Parsing result failed. + */ + JsonObject tempObj; + int stepTwo = tempObj.Parse(JSON_STRING5); + EXPECT_TRUE(stepTwo != E_OK); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_notification_chain_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_notification_chain_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7208d44505ee235e9503d3f0668cd56d118c0fbf --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_notification_chain_test.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "notification_chain.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace std; +using namespace DistributedDBUnitTest; + +namespace { + const EventType COMMIT_EVENT = 1; + const int INVALID_EVENT = 0; + NotificationChain *g_notificationChain = nullptr; + NotificationChain::Listener *g_listener = nullptr; + int g_onEventTestNum = 0; + bool g_onFinalizeCalled = false; + + auto g_onEventFunction = [](void *arg) { + g_onEventTestNum = *(reinterpret_cast(arg)); + LOGI("g_onEventFunction called."); + }; + + auto g_onFinalize = []() { + g_onFinalizeCalled = true; + LOGI("g_onFinalize called."); + }; +} + +class DistributedDBNotificationChainTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBNotificationChainTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Create a NotificationChain. + */ + g_notificationChain = new (std::nothrow) NotificationChain(); + EXPECT_TRUE(g_notificationChain != nullptr); +} + +void DistributedDBNotificationChainTest::TearDownTestCase(void) +{ + /** + * @tc.setup: Release a NotificationChain. + */ + g_notificationChain->OnLastRef([]() { LOGI("g_notificationChain finalize called."); }); + g_notificationChain->KillAndDecObjRef(g_notificationChain); + g_notificationChain = nullptr; +} + +void DistributedDBNotificationChainTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: Register a listener to the NotificationChain + */ + g_onEventTestNum = 0; + g_onFinalizeCalled = false; + int result = g_notificationChain->RegisterEventType(COMMIT_EVENT); + EXPECT_TRUE(result == E_OK); + int errCode = E_OK; + g_listener = g_notificationChain->RegisterListener(COMMIT_EVENT, g_onEventFunction, g_onFinalize, errCode); + EXPECT_TRUE(g_listener != nullptr); +} + +void DistributedDBNotificationChainTest::TearDown(void) +{ + /** + * @tc.setup: Unregister a listener to the NotificationChain + */ + g_listener->Drop(); + g_listener = nullptr; + EXPECT_TRUE(g_notificationChain->UnRegisterEventType(COMMIT_EVENT) == E_OK); +} + +/** + * @tc.name: RegisterEvent001 + * @tc.desc: Register an exits event. + * @tc.type: FUNC + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBNotificationChainTest, RegisterEvent001, TestSize.Level0) +{ + /** + * @tc.steps: step1. call RegisterEventType to register a exist event + * @tc.expected: step1. function return -E_ALREADY_REGISTER + */ + int result = g_notificationChain->RegisterEventType(COMMIT_EVENT); + EXPECT_TRUE(result == -E_ALREADY_REGISTER); +} + +/** + * @tc.name: RegisterListener001 + * @tc.desc: Register and unregister a listener. + * @tc.type: FUNC + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBNotificationChainTest, RegisterListener001, TestSize.Level0) +{ + /** + * @tc.steps: step1. call RegisterListener to register a listener + * @tc.expected: step1. function return a not null listener + */ + int errCode = E_OK; + NotificationChain::Listener *listener = + g_notificationChain->RegisterListener(COMMIT_EVENT, g_onEventFunction, g_onFinalize, errCode); + EXPECT_TRUE(listener != nullptr); + + /** + * @tc.steps: step2. call Drop to unregister the listener + * @tc.expected: step2. function return E_OK + */ + int result = listener->Drop(); + EXPECT_TRUE(g_onFinalizeCalled); + EXPECT_TRUE(result == E_OK); +} + +/** + * @tc.name: NotifyEvent001 + * @tc.desc: notify an event to listener. + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBNotificationChainTest, NotifyEvent001, TestSize.Level0) +{ + int errCode = E_OK; + /** + * @tc.steps: step1. call RegisterListener to register a listener + * @tc.expected: step1. function return a not null listener + */ + NotificationChain::Listener *listener = + g_notificationChain->RegisterListener(COMMIT_EVENT, g_onEventFunction, g_onFinalize, errCode); + EXPECT_TRUE(listener != nullptr); + + /** + * @tc.steps: step2. call NotifyEvent to notify an event + * @tc.expected: step2. the listener's callback should be called + */ + int testNum = 2048; + g_notificationChain->NotifyEvent(COMMIT_EVENT, &testNum); + EXPECT_TRUE(g_onEventTestNum == testNum); + listener->Drop(); +} + +/** + * @tc.name: UnRegisterEvent001 + * @tc.desc: unregister a invalid event. + * @tc.type: FUNC + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBNotificationChainTest, UnRegisterEvent001, TestSize.Level0) +{ + /** + * @tc.steps: step1. UnRegisterEventType a invalid event + * @tc.expected: step1. function should return -E_NOT_FOUND + */ + int result = g_notificationChain->UnRegisterEventType(INVALID_EVENT); + EXPECT_EQ(result, -E_NOT_FOUND); +} diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_parcel_unit_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_parcel_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff527723342fca9efcc0819106acf8a95d710689 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_parcel_unit_test.cpp @@ -0,0 +1,680 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "distributeddb_tools_unit_test.h" + +#include + +#include "parcel.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +class DistributedDBParcelTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBParcelTest::SetUpTestCase(void) +{ +} + +void DistributedDBParcelTest::TearDownTestCase(void) +{ +} + +void DistributedDBParcelTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBParcelTest::TearDown(void) +{ +} + +/** + * @tc.name: WriteInt001 + * @tc.desc: write and read a integer. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteInt001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + int writeData1 = INT_MAX; + uint32_t writeData2 = UINT32_MAX; + uint64_t writeData3 = 0; + + uint32_t len = Parcel::GetIntLen() + Parcel::GetUInt32Len() + Parcel::GetUInt64Len(); + len = Parcel::GetEightByteAlign(len); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteInt(writeData1); + EXPECT_TRUE(ret == E_OK); + ret = writeParcel.WriteUInt32(writeData2); + EXPECT_TRUE(ret == E_OK); + ret = writeParcel.WriteUInt64(writeData3); + EXPECT_TRUE(ret == E_OK); + // write overflow + ret = writeParcel.WriteUInt64(writeData3); + EXPECT_TRUE(ret != E_OK); + + /** + * @tc.steps: step2. read the vector, the vector should same as written vector; + * @tc.expected: step1. read out vector same as written vector; + */ + int readData1; + uint32_t readData2; + uint64_t readData3; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadInt(readData1); + EXPECT_TRUE(readLen == Parcel::GetIntLen()); + readLen += readParcel.ReadUInt32(readData2); + EXPECT_TRUE(readLen == Parcel::GetIntLen() + Parcel::GetUInt32Len()); + readLen += readParcel.ReadUInt64(readData3); + EXPECT_TRUE(readLen == Parcel::GetIntLen() + Parcel::GetUInt32Len() + Parcel::GetUInt64Len()); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readData1 == writeData1); + EXPECT_TRUE(readData2 == writeData2); + EXPECT_TRUE(readData3 == writeData3); + // read overflow + readLen = readParcel.ReadUInt64(readData3); + EXPECT_TRUE(readParcel.IsError()); + EXPECT_TRUE(readLen == 0); + delete []buf; +} + +/** + * @tc.name: WriteVector001 + * @tc.desc: write and read a vector. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData = {1, 2, 5, 7, 20, 30, 99}; + uint32_t len = Parcel::GetVectorLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. read the vector, the vector should same as written vector; + * @tc.expected: step1. read out vector same as written vector; + */ + vector readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadVector(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen == len); + EXPECT_TRUE(readData.size() == writeData.size()); + EXPECT_TRUE(DistributedDBToolsUnitTest::CompareVector(writeData, readData)); + delete []buf; +} + +/** + * @tc.name: WriteVector002 + * @tc.desc: write and read an empty vector. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector002, TestSize.Level1) +{ + /** + * @tc.steps: step1. create an empty vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData; + uint32_t len = Parcel::GetVectorLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. read the vector, the vector should same as written vector; + * @tc.expected: step1. read out vector same as written vector; + */ + vector readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadVector(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen == len); + EXPECT_TRUE(readData.size() == writeData.size()); + EXPECT_TRUE(DistributedDBToolsUnitTest::CompareVector(writeData, readData)); + delete []buf; +} + +/** + * @tc.name: WriteVector003 + * @tc.desc: write and read a vector. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector003, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData = {1, 2, 5, 7, 20, 30, 99, 0xffffffff, 0x5678, 0x98765432, 0xabcdef12}; + uint32_t len = Parcel::GetVectorLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. read the vector, the vector should same as written vector; + * @tc.expected: step1. read out vector same as written vector; + */ + vector readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadVector(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen == len); + EXPECT_TRUE(readData.size() == writeData.size()); + EXPECT_TRUE(DistributedDBToolsUnitTest::CompareVector(writeData, readData)); + delete []buf; +} + +/** + * @tc.name: WriteVector004 + * @tc.desc: write and read an empty vector. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector004, TestSize.Level1) +{ + /** + * @tc.steps: step1. create an empty vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData; + uint32_t len = Parcel::GetVectorLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. read the vector, the vector should same as written vector; + * @tc.expected: step1. read out vector same as written vector; + */ + vector readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadVector(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen == len); + EXPECT_TRUE(readData.size() == writeData.size()); + EXPECT_TRUE(DistributedDBToolsUnitTest::CompareVector(writeData, readData)); + delete []buf; +} + +/** + * @tc.name: WriteVector005 + * @tc.desc: write and read a vector. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector005, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData = {1, 2, 5, 7, 20, 30, 99, 0xffffffffffffffff, + 0x5678, 0x98765432ffffffff, 0xabcdef1212345678}; + uint32_t len = Parcel::GetVectorLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. read the vector, the vector should same as written vector; + * @tc.expected: step1. read out vector same as written vector; + */ + vector readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadVector(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen == len); + EXPECT_TRUE(readData.size() == writeData.size()); + EXPECT_TRUE(DistributedDBToolsUnitTest::CompareVector(writeData, readData)); + delete []buf; +} + +/** + * @tc.name: WriteVector006 + * @tc.desc: write and read an empty vector. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector006, TestSize.Level1) +{ + /** + * @tc.steps: step1. create an empty vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData; + uint32_t len = Parcel::GetVectorLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. read the vector, the vector should same as written vector; + * @tc.expected: step1. read out vector same as written vector; + */ + vector readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadVector(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen == len); + EXPECT_TRUE(readData.size() == writeData.size()); + EXPECT_TRUE(DistributedDBToolsUnitTest::CompareVector(writeData, readData)); + delete []buf; +} + +/** + * @tc.name: WriteVector007 + * @tc.desc: write and read a vector, insert a wrong len. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector007, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData = {1, 2, 5, 7, 20, 30, 99}; + uint32_t len = Parcel::GetVectorLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. set a wrong len, the len is INT_MAX; + * @tc.expected: the vector should be empty, and isError should be true. + */ + *(reinterpret_cast(buf)) = HostToNet(static_cast(INT32_MAX)); + + /** + * @tc.steps: step3. read the vector + * @tc.expected: the vector should be empty, and isError should be true. + */ + vector readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadVector(readData); + EXPECT_TRUE(readParcel.IsError()); + EXPECT_TRUE(readLen == 0); + EXPECT_TRUE(readData.size() == 0); + delete []buf; +} + +/** + * @tc.name: WriteVector008 + * @tc.desc: write and read a vector, insert a wrong len. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector008, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData = {1, 2, 5, 7, 20, 30, 99, 0xff, 0xff, 0x1f, 0xab, 0x45}; + uint32_t len = Parcel::GetVectorLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. set a wrong len, the len is bigger than it should be; + * @tc.expected: the vector should be empty, and isError should be true. + */ + *(reinterpret_cast(buf)) = HostToNet(static_cast(writeData.size()) + 1); + + /** + * @tc.steps: step3. read the vector + * @tc.expected: the vector should be empty, and isError should be true. + */ + vector readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadVector(readData); + EXPECT_TRUE(readParcel.IsError()); + EXPECT_TRUE(readLen == 0); + EXPECT_TRUE(readData.size() == 0); + delete []buf; +} + +/** + * @tc.name: WriteVector009 + * @tc.desc: write and read a vector, insert a wrong len. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector009, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData = {1, 2, 5, 7, 20, 30, 99}; + uint32_t len = Parcel::GetVectorLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. set a wrong len, the len is smaller than it should be; + * @tc.expected: the vector should be empty, and isError should be true; + */ + *(reinterpret_cast(buf)) = HostToNet(static_cast(writeData.size()) - 1); + + /** + * @tc.steps: step3. read the vector + * @tc.expected: the vector should be same as writeData.sub(0, len - 1); + */ + vector readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadVector(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen != 0); + EXPECT_TRUE(readData.size() == writeData.size() - 1); + EXPECT_TRUE(DistributedDBToolsUnitTest::CompareVectorN(readData, writeData, writeData.size() - 1)); + delete []buf; +} + +#ifndef LOW_LEVEL_MEM_DEV +/** + * @tc.name: WriteVector010 + * @tc.desc: write and read a vector, vector len is INT_MAX. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteVector010, TestSize.Level2) +{ + /** + * @tc.steps: step1. create a vector, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + vector writeData(INT32_MAX, 0xff); + uint32_t len = Parcel::GetVectorLen(writeData); + EXPECT_TRUE(len == 0); + len = Parcel::GetEightByteAlign(static_cast(INT32_MAX) + 4); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteVector(writeData); + delete []buf; + EXPECT_TRUE(ret != EOK); +} +#endif + +/** + * @tc.name: WriteString001 + * @tc.desc: write and read a string normally. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteString001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a string, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + string writeData("abcd1234ffff234"); + uint32_t len = Parcel::GetStringLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteString(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step3. read the string + * @tc.expected: the string should be read correctly; + */ + string readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadString(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen == len); + EXPECT_TRUE(readData == writeData); + delete []buf; +} + +/** + * @tc.name: WriteString002 + * @tc.desc: write and read a string, insert a wrong len. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteString002, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a string, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + string writeData("abcd1234ffff234"); + uint32_t len = Parcel::GetStringLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteString(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. set a wrong len, the len is smaller than it should be; + * @tc.expected: the vector should be empty, and isError should be true; + */ + *(reinterpret_cast(buf)) = HostToNet(static_cast(writeData.size()) - 1); + + /** + * @tc.steps: step3. read the string + * @tc.expected: the string should be read correctly; + */ + string readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadString(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen != 0); + EXPECT_TRUE(readData == writeData.substr(0, writeData.size() - 1)); + delete []buf; +} + +/** + * @tc.name: WriteString003 + * @tc.desc: write and read a string, insert a wrong len. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteString003, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a string, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + string writeData("abcd1234ffff2349poff"); + uint32_t len = Parcel::GetStringLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteString(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. set a wrong len, the len is bigger than it should be; + * @tc.expected: the string should be empty, and isError should be true; + */ + *(reinterpret_cast(buf)) = HostToNet(static_cast(writeData.size()) + 1); + + /** + * @tc.steps: step3. read the string + * @tc.expected: the string should be read with error; + */ + string readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadString(readData); + EXPECT_TRUE(readParcel.IsError()); + EXPECT_TRUE(readLen == 0); + EXPECT_TRUE(readData.size()== 0); + delete []buf; +} + +/** + * @tc.name: WriteString004 + * @tc.desc: write and read a string, string is empty. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteString004, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a string, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + string writeData; + uint32_t len = Parcel::GetStringLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteString(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step3. read the string + * @tc.expected: the string should be read with error; + */ + string readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadString(readData); + EXPECT_TRUE(!readParcel.IsError()); + EXPECT_TRUE(readLen != 0); + EXPECT_TRUE(readData.size()== 0); + delete []buf; +} + +/** + * @tc.name: WriteString005 + * @tc.desc: write and read a string, insert a INT_MAX len. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteString005, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a string, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + string writeData; + uint32_t len = Parcel::GetStringLen(writeData); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteString(writeData); + EXPECT_TRUE(ret == EOK); + + /** + * @tc.steps: step2. set a wrong len, the len is INT32_MAX; + * @tc.expected: the string should be empty, and isError should be true; + */ + *(reinterpret_cast(buf)) = HostToNet(static_cast(INT32_MAX)); + + /** + * @tc.steps: step3. read the string + * @tc.expected: the string should be read with error; + */ + string readData; + Parcel readParcel(buf, len); + uint32_t readLen = readParcel.ReadString(readData); + EXPECT_TRUE(readParcel.IsError()); + EXPECT_TRUE(readLen == 0); + EXPECT_TRUE(readData.size() == 0); + delete []buf; +} +#ifndef LOW_LEVEL_MEM_DEV +/** + * @tc.name: WriteString006 + * @tc.desc: write and read a string, string size is INT_MAX. + * @tc.type: FUNC + * @tc.require: AR000CQE0U + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBParcelTest, WriteString006, TestSize.Level2) +{ + /** + * @tc.steps: step1. create a string, and write it into a buffer; + * @tc.expected: step1. write ok; + */ + string writeData(INT32_MAX, 'z'); + uint32_t len = Parcel::GetStringLen(writeData); + EXPECT_TRUE(len == 0); + len = Parcel::GetEightByteAlign(static_cast(INT32_MAX) + 4); + uint8_t *buf = new (nothrow) uint8_t[len]; + ASSERT_NE(buf, nullptr); + Parcel writeParcel(buf, len); + int ret = writeParcel.WriteString(writeData); + delete []buf; + EXPECT_TRUE(ret != EOK); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_relational_schema_object_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_relational_schema_object_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2192f068889f03a470ca12c815a137eb2e0d9c31 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_relational_schema_object_test.cpp @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_JSON +#include +#include + +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "relational_schema_object.h" +#include "schema_utils.h" +#include "schema_constant.h" +#include "schema_negotiate.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + const std::string NORMAL_SCHEMA = R""({ + "SCHEMA_VERSION": "2.0", + "SCHEMA_TYPE": "RELATIVE", + "TABLES": [{ + "NAME": "FIRST", + "DEFINE": { + "field_name1": { + "COLUMN_ID":1, + "TYPE": "STRING", + "NOT_NULL": true, + "DEFAULT": "abcd" + }, + "field_name2": { + "COLUMN_ID":2, + "TYPE": "MYINT(21)", + "NOT_NULL": false, + "DEFAULT": "222" + }, + "field_name3": { + "COLUMN_ID":3, + "TYPE": "INTGER", + "NOT_NULL": false, + "DEFAULT": "1" + } + }, + "AUTOINCREMENT": true, + "UNIQUE": ["field_name1", ["field_name2", "field_name3"]], + "PRIMARY_KEY": "field_name1", + "INDEX": { + "index_name1": ["field_name1", "field_name2"], + "index_name2": ["field_name3"] + } + }, { + "NAME": "SECOND", + "DEFINE": { + "key": { + "COLUMN_ID":1, + "TYPE": "BLOB", + "NOT_NULL": true + }, + "value": { + "COLUMN_ID":2, + "TYPE": "BLOB", + "NOT_NULL": false + } + }, + "PRIMARY_KEY": "field_name1" + }] + })""; + + const std::string INVALID_SCHEMA = R""({ + "SCHEMA_VERSION": "2.0", + "SCHEMA_TYPE": "RELATIVE", + "TABLES": [{ + "NAME": "FIRST", + "DEFINE": { + "field_name1": { + "COLUMN_ID":1, + "TYPE": "STRING", + "NOT_NULL": true, + "DEFAULT": "abcd" + },"field_name2": { + "COLUMN_ID":2, + "TYPE": "MYINT(21)", + "NOT_NULL": false, + "DEFAULT": "222" + } + }, + "PRIMARY_KEY": "field_name1" + }] + })""; + + const std::string INVALID_JSON_STRING = R""({ + "SCHEMA_VERSION": "2.0", + "SCHEMA_TYPE": "RELATIVE", + "TABLES": [{ + "NAME": "FIRST", + "DEFINE": { + "field_name1": {)""; + + const std::string SCHEMA_VERSION_STR_1 = R"("SCHEMA_VERSION": "1.0",)"; + const std::string SCHEMA_VERSION_STR_2 = R"("SCHEMA_VERSION": "2.0",)"; + const std::string SCHEMA_VERSION_STR_INVALID = R"("SCHEMA_VERSION": "awd3",)"; + const std::string SCHEMA_TYPE_STR_NONE = R"("SCHEMA_TYPE": "NONE",)"; + const std::string SCHEMA_TYPE_STR_JSON = R"("SCHEMA_TYPE": "JSON",)"; + const std::string SCHEMA_TYPE_STR_FLATBUFFER = R"("SCHEMA_TYPE": "FLATBUFFER",)"; + const std::string SCHEMA_TYPE_STR_RELATIVE = R"("SCHEMA_TYPE": "RELATIVE",)"; + const std::string SCHEMA_TYPE_STR_INVALID = R"("SCHEMA_TYPE": "adewaaSAD",)"; + + const std::string SCHEMA_TABLE_STR = R""("TABLES": [{ + "NAME": "FIRST", + "DEFINE": { + "field_name1": { + "COLUMN_ID":1, + "TYPE": "STRING", + "NOT_NULL": true, + "DEFAULT": "abcd" + },"field_name2": { + "COLUMN_ID":2, + "TYPE": "MYINT(21)", + "NOT_NULL": false, + "DEFAULT": "222" + } + }, + "PRIMARY_KEY": "field_name1" + }])""; + + const std::string TABLE_DEFINE_STR = R""({ + "NAME": "FIRST", + "DEFINE": { + "field_name1": { + "COLUMN_ID":1, + "TYPE": "STRING", + "NOT_NULL": true, + "DEFAULT": "abcd" + },"field_name2": { + "COLUMN_ID":2, + "TYPE": "MYINT(21)", + "NOT_NULL": false, + "DEFAULT": "222" + } + }, + "PRIMARY_KEY": "field_name1" + })""; + + const std::string TABLE_DEFINE_STR_NAME = R""("NAME": "FIRST",)""; + const std::string TABLE_DEFINE_STR_NAME_INVALID = R"("NAME": 123,)"; + const std::string TABLE_DEFINE_STR_FIELDS = R""("DEFINE": { + "field_name1": { + "COLUMN_ID":1, + "TYPE": "STRING", + "NOT_NULL": true, + "DEFAULT": "abcd" + },"field_name2": { + "COLUMN_ID":2, + "TYPE": "MYINT(21)", + "NOT_NULL": false, + "DEFAULT": "222" + } + },)""; + const std::string TABLE_DEFINE_STR_FIELDS_EMPTY = R""("DEFINE": {},)""; + const std::string TABLE_DEFINE_STR_FIELDS_NOTYPE = R""("DEFINE": { + "field_name1": { + "COLUMN_ID":1, + "NOT_NULL": true, + "DEFAULT": "abcd" + }},)""; + const std::string TABLE_DEFINE_STR_KEY = R""("PRIMARY_KEY": "field_name1")""; + const std::string TABLE_DEFINE_STR_KEY_INVALID = R""("PRIMARY_KEY": false)""; +} + +class DistributedDBRelationalSchemaObjectTest : public testing::Test { +public: + static void SetUpTestCase(void) {}; + static void TearDownTestCase(void) {}; + void SetUp() override; + void TearDown() override {}; +}; + +void DistributedDBRelationalSchemaObjectTest::SetUp() +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +/** + * @tc.name: RelationalSchemaParseTest001 + * @tc.desc: Test relational schema parse from json string + * @tc.type: FUNC + * @tc.require: AR000GK58I + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBRelationalSchemaObjectTest, RelationalSchemaParseTest001, TestSize.Level1) +{ + const std::string schemaStr = NORMAL_SCHEMA; + RelationalSchemaObject schemaObj; + int errCode = schemaObj.ParseFromSchemaString(schemaStr); + EXPECT_EQ(errCode, E_OK); + + RelationalSchemaObject schemaObj2; + schemaObj2.ParseFromSchemaString(schemaObj.ToSchemaString()); + EXPECT_EQ(errCode, E_OK); +} + +/** + * @tc.name: RelationalSchemaParseTest002 + * @tc.desc: Test relational schema parse from invalid json string + * @tc.type: FUNC + * @tc.require: AR000GK58I + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBRelationalSchemaObjectTest, RelationalSchemaParseTest002, TestSize.Level1) +{ + RelationalSchemaObject schemaObj; + + std::string schemaStr01(SchemaConstant::SCHEMA_STRING_SIZE_LIMIT + 1, 's'); + int errCode = schemaObj.ParseFromSchemaString(schemaStr01); + EXPECT_EQ(errCode, -E_INVALID_ARGS); + + errCode = schemaObj.ParseFromSchemaString(INVALID_JSON_STRING); + EXPECT_EQ(errCode, -E_JSON_PARSE_FAIL); + + std::string noVersion = "{" + SCHEMA_TYPE_STR_RELATIVE + SCHEMA_TABLE_STR + "}"; + errCode = schemaObj.ParseFromSchemaString(noVersion); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidVersion1 = "{" + SCHEMA_VERSION_STR_1 + SCHEMA_TYPE_STR_RELATIVE + SCHEMA_TABLE_STR + "}"; + errCode = schemaObj.ParseFromSchemaString(invalidVersion1); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidVersion2 = "{" + SCHEMA_VERSION_STR_INVALID + SCHEMA_TYPE_STR_RELATIVE + SCHEMA_TABLE_STR + "}"; + errCode = schemaObj.ParseFromSchemaString(invalidVersion2); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string noType = "{" + SCHEMA_VERSION_STR_2 + SCHEMA_TABLE_STR + "}"; + errCode = schemaObj.ParseFromSchemaString(noType); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidType1 = "{" + SCHEMA_VERSION_STR_2 + SCHEMA_TYPE_STR_NONE + SCHEMA_TABLE_STR + "}"; + errCode = schemaObj.ParseFromSchemaString(invalidType1); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidType2 = "{" + SCHEMA_VERSION_STR_2 + SCHEMA_TYPE_STR_JSON + SCHEMA_TABLE_STR + "}"; + errCode = schemaObj.ParseFromSchemaString(invalidType2); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidType3 = "{" + SCHEMA_VERSION_STR_2 + SCHEMA_TYPE_STR_FLATBUFFER + SCHEMA_TABLE_STR + "}"; + errCode = schemaObj.ParseFromSchemaString(invalidType3); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidType4 = "{" + SCHEMA_VERSION_STR_2 + SCHEMA_TYPE_STR_INVALID + SCHEMA_TABLE_STR + "}"; + errCode = schemaObj.ParseFromSchemaString(invalidType4); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string noTable = "{" + SCHEMA_VERSION_STR_2 + + SCHEMA_TYPE_STR_RELATIVE.substr(0, SCHEMA_TYPE_STR_RELATIVE.length() - 1) + "}"; + errCode = schemaObj.ParseFromSchemaString(noTable); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); +} + +namespace { +std::string GenerateFromTableStr(const std::string &tableStr) +{ + return R""({ + "SCHEMA_VERSION": "2.0", + "SCHEMA_TYPE": "RELATIVE", + "TABLES": )"" + tableStr + "}"; +} +} + +/** + * @tc.name: RelationalSchemaParseTest003 + * @tc.desc: Test relational schema parse from invalid json string + * @tc.type: FUNC + * @tc.require: AR000GK58I + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBRelationalSchemaObjectTest, RelationalSchemaParseTest003, TestSize.Level1) +{ + RelationalSchemaObject schemaObj; + int errCode = E_OK; + + errCode = schemaObj.ParseFromSchemaString(GenerateFromTableStr(TABLE_DEFINE_STR)); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidTableStr01 = "{" + TABLE_DEFINE_STR_FIELDS + TABLE_DEFINE_STR_KEY + "}"; + errCode = schemaObj.ParseFromSchemaString(GenerateFromTableStr("[" + invalidTableStr01 + "]")); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidTableStr02 = "{" + TABLE_DEFINE_STR_NAME_INVALID + TABLE_DEFINE_STR_FIELDS + + TABLE_DEFINE_STR_KEY + "}"; + errCode = schemaObj.ParseFromSchemaString(GenerateFromTableStr("[" + invalidTableStr02 + "]")); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidTableStr04 = "{" + TABLE_DEFINE_STR_NAME + TABLE_DEFINE_STR_FIELDS_NOTYPE + + TABLE_DEFINE_STR_KEY + "}"; + errCode = schemaObj.ParseFromSchemaString(GenerateFromTableStr("[" + invalidTableStr04 + "]")); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + std::string invalidTableStr05 = "{" + TABLE_DEFINE_STR_NAME + TABLE_DEFINE_STR_FIELDS + + TABLE_DEFINE_STR_KEY_INVALID + "}"; + errCode = schemaObj.ParseFromSchemaString(GenerateFromTableStr("[" + invalidTableStr05 + "]")); + EXPECT_EQ(errCode, -E_SCHEMA_PARSE_FAIL); + + errCode = schemaObj.ParseFromSchemaString(""); + EXPECT_EQ(errCode, -E_INVALID_ARGS); +} + +/** + * @tc.name: RelationalSchemaCompareTest001 + * @tc.desc: Test relational schema negotiate with same schema string + * @tc.type: FUNC + * @tc.require: AR000GK58I + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBRelationalSchemaObjectTest, RelationalSchemaCompareTest001, TestSize.Level1) +{ + RelationalSchemaObject schemaObj; + int errCode = schemaObj.ParseFromSchemaString(NORMAL_SCHEMA); + EXPECT_EQ(errCode, E_OK); + + RelationalSyncOpinion opinion = SchemaNegotiate::MakeLocalSyncOpinion(schemaObj, NORMAL_SCHEMA, + static_cast(SchemaType::RELATIVE)); + EXPECT_EQ(opinion.at("FIRST").permitSync, true); + EXPECT_EQ(opinion.at("FIRST").checkOnReceive, false); + EXPECT_EQ(opinion.at("FIRST").requirePeerConvert, false); +} + +/** + * @tc.name: RelationalTableCompareTest001 + * @tc.desc: Test relational schema negotiate with same schema string + * @tc.type: FUNC + * @tc.require: AR000GK58I + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBRelationalSchemaObjectTest, RelationalTableCompareTest001, TestSize.Level1) +{ + RelationalSchemaObject schemaObj; + int errCode = schemaObj.ParseFromSchemaString(NORMAL_SCHEMA); + EXPECT_EQ(errCode, E_OK); + TableInfo table1 = schemaObj.GetTable("FIRST"); + TableInfo table2 = schemaObj.GetTable("FIRST"); + EXPECT_EQ(table1.CompareWithTable(table2), -E_RELATIONAL_TABLE_EQUAL); + + table2.AddIndexDefine("indexname", {"field_name2", "field_name1"}); + EXPECT_EQ(table1.CompareWithTable(table2), -E_RELATIONAL_TABLE_COMPATIBLE); + + TableInfo table3 = schemaObj.GetTable("SECOND"); + EXPECT_EQ(table1.CompareWithTable(table3), -E_RELATIONAL_TABLE_INCOMPATIBLE); + + TableInfo table4 = schemaObj.GetTable("FIRST"); + table4.AddField(table3.GetFields().at("value")); + EXPECT_EQ(table1.CompareWithTable(table4), -E_RELATIONAL_TABLE_COMPATIBLE_UPGRADE); + + TableInfo table5 = schemaObj.GetTable("FIRST"); + table5.AddField(table3.GetFields().at("key")); + EXPECT_EQ(table1.CompareWithTable(table5), -E_RELATIONAL_TABLE_INCOMPATIBLE); +} + +/** + * @tc.name: RelationalSchemaOpinionTest001 + * @tc.desc: Test relational schema sync opinion + * @tc.type: FUNC + * @tc.require: AR000GK58I + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBRelationalSchemaObjectTest, RelationalSchemaOpinionTest001, TestSize.Level1) +{ + RelationalSyncOpinion opinion; + opinion["table_1"] = SyncOpinion {true, false, false}; + opinion["table_2"] = SyncOpinion {false, true, false}; + opinion["table_3"] = SyncOpinion {false, false, true}; + + uint32_t len = SchemaNegotiate::CalculateParcelLen(opinion); + std::vector buff(len, 0); + Parcel writeParcel(buff.data(), len); + int errCode = SchemaNegotiate::SerializeData(opinion, writeParcel); + EXPECT_EQ(errCode, E_OK); + + Parcel readParcel(buff.data(), len); + RelationalSyncOpinion opinionRecv; + errCode = SchemaNegotiate::DeserializeData(readParcel, opinionRecv); + EXPECT_EQ(errCode, E_OK); + + EXPECT_EQ(opinion.size(), opinionRecv.size()); + for (const auto &it : opinion) { + SyncOpinion tableOpinionRecv = opinionRecv.at(it.first); + EXPECT_EQ(it.second.permitSync, tableOpinionRecv.permitSync); + EXPECT_EQ(it.second.requirePeerConvert, tableOpinionRecv.requirePeerConvert); + } +} + +/** + * @tc.name: RelationalSchemaNegotiateTest001 + * @tc.desc: Test relational schema negotiate + * @tc.type: FUNC + * @tc.require: AR000GK58I + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBRelationalSchemaObjectTest, RelationalSchemaNegotiateTest001, TestSize.Level1) +{ + RelationalSyncOpinion localOpinion; + localOpinion["table_1"] = SyncOpinion {true, false, false}; + localOpinion["table_2"] = SyncOpinion {false, true, false}; + localOpinion["table_3"] = SyncOpinion {false, false, true}; + + RelationalSyncOpinion remoteOpinion; + remoteOpinion["table_2"] = SyncOpinion {true, false, false}; + remoteOpinion["table_3"] = SyncOpinion {false, true, false}; + remoteOpinion["table_4"] = SyncOpinion {false, false, true}; + RelationalSyncStrategy strategy = SchemaNegotiate::ConcludeSyncStrategy(localOpinion, remoteOpinion); + + EXPECT_EQ(strategy.size(), 2u); + EXPECT_EQ(strategy.at("table_2").permitSync, true); + EXPECT_EQ(strategy.at("table_3").permitSync, false); +} + +/** + * @tc.name: TableCompareTest001 + * @tc.desc: Test table compare + * @tc.type: FUNC + * @tc.require: AR000GK58I + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBRelationalSchemaObjectTest, TableCompareTest001, TestSize.Level1) +{ + FieldInfo field1; + field1.SetFieldName("a"); + FieldInfo field2; + field2.SetFieldName("b"); + FieldInfo field3; + field3.SetFieldName("c"); + FieldInfo field4; + field4.SetFieldName("d"); + + TableInfo table; + table.AddField(field2); + table.AddField(field3); + + TableInfo inTable1; + inTable1.AddField(field1); + inTable1.AddField(field2); + inTable1.AddField(field3); + EXPECT_EQ(table.CompareWithTable(inTable1), -E_RELATIONAL_TABLE_COMPATIBLE_UPGRADE); + + TableInfo inTable2; + inTable2.AddField(field1); + inTable2.AddField(field2); + inTable2.AddField(field4); + EXPECT_EQ(table.CompareWithTable(inTable2), -E_RELATIONAL_TABLE_INCOMPATIBLE); + + TableInfo inTable3; + inTable3.AddField(field3); + inTable3.AddField(field2); + EXPECT_EQ(table.CompareWithTable(inTable3), -E_RELATIONAL_TABLE_EQUAL); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_schema_object_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_schema_object_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f7520db5a0767d6873732590094c716ca28db1e1 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_schema_object_test.cpp @@ -0,0 +1,1090 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_JSON +#include +#include + +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "schema_constant.h" +#include "schema_object.h" +#include "schema_utils.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; + +namespace { + const std::string VALID_SCHEMA_FULL_DEFINE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":{" + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, NOT NULL, DEFAULT '3.1415'\"," + "\"field_name7\":[]," + "\"field_name8\":{}" + "}" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2.field_name6\"]}"; + const std::string VALID_SCHEMA_INDEX_EMPTY = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"" + "}," + "\"SCHEMA_INDEXES\":[]}"; + const std::string VALID_SCHEMA_NO_INDEX = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"" + "}}"; + const std::string VALID_SCHEMA_PRE_SUF_BLANK = "{\"SCHEMA_VERSION\":\" 1.0\"," + "\"SCHEMA_MODE\":\"STRICT \"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\" BOOL \"" + "}}"; + + const std::string INVALID_SCHEMA_INVALID_JSON = "[\"$.field_name1\", \"$.field_name2.field_name6\"]"; + const std::string INVALID_SCHEMA_LESS_META_FIELD = "{\"SCHEMA_VERSION\":\" 1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"}"; + const std::string INVALID_SCHEMA_MORE_META_FIELD = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"" + "}," + "\"SCHEMA_UNDEFINE_META_FIELD\":[]}"; + const std::string INVALID_SCHEMA_WRONG_VERSION = "{\"SCHEMA_VERSION\":\"1.1\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"" + "}}"; + const std::string INVALID_SCHEMA_WRONG_MODE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"WRONG_MODE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"" + "}}"; + + const std::string INVALID_SCHEMA_DEFINE_EMPTY = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{}}"; + const std::string INVALID_SCHEMA_DEFINE_NEST_TOO_DEEP = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":{" + "\"field_name2\":{" + "\"field_name3\":{" + "\"field_name4\":{" + "\"field_name5\":{" + "}}}}}}}"; + const std::string INVALID_SCHEMA_DEFINE_INVALID_FIELD_NAME = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"12345\":\"BOOL\"" + "}}"; + const std::string INVALID_SCHEMA_DEFINE_INVALID_FIELD_ATTR = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL, DEFAULT null\"" + "}}"; + const std::string INVALID_SCHEMA_DEFINE_INVALID_ARRAY_TYPE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":[3.14]" + "}}"; + + const std::string INVALID_SCHEMA_INDEX_INVALID = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"" + "}," + "\"SCHEMA_INDEXES\":[true, false]}"; + const std::string INVALID_SCHEMA_INDEX_PATH_INVALID = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"" + "}," + "\"SCHEMA_INDEXES\":[\".field_name1\"]}"; + const std::string INVALID_SCHEMA_INDEX_PATH_NOT_EXIST = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name2\"]}"; + const std::string INVALID_SCHEMA_INDEX_PATH_NOT_INDEXABLE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":[]" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\"]}"; + const std::string INVALID_SCHEMA_INDEX_PATH_DUPLICATE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name1\"]}"; + + const std::string SCHEMA_COMPARE_BASELINE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_name5\":{}" + "}}," + "\"SCHEMA_INDEXES\":[\"field_name1\"]}"; + const std::string SCHEMA_DEFINE_MORE_FIELD = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_more1\":\"LONG\"," + "\"field_name5\":{" + "\"field_more2\":\"DOUBLE\"" + "}}}," + "\"SCHEMA_INDEXES\":[\"field_name1\"]}"; + const std::string SCHEMA_DEFINE_MORE_FIELD_NOTNULL_FORBID = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_more1\":\"LONG\"," + "\"field_name5\":{" + "\"field_more2\":\"DOUBLE, NOT NULL\"" + "}}}," + "\"SCHEMA_INDEXES\":[\"field_name1\"]}"; + const std::string SCHEMA_DEFINE_MORE_FIELD_NOTNULL_PERMIT = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_more1\":\"LONG, NOT NULL, DEFAULT 88\"," + "\"field_name5\":{" + "\"field_more2\":\"DOUBLE\"" + "}}}," + "\"SCHEMA_INDEXES\":[\"field_name1\"]}"; + const std::string SCHEMA_DEFINE_LESS_FIELD = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name5\":{}" + "}}," + "\"SCHEMA_INDEXES\":[\"field_name1\"]}"; + const std::string SCHEMA_INDEX_MORE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_name5\":{}" + "}}," + "\"SCHEMA_INDEXES\":[\"field_name1\", [\"field_name2\", \"field_name3.field_name4\"]]}"; + const std::string SCHEMA_INDEX_LESS = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_name5\":{}" + "}}," + "\"SCHEMA_INDEXES\":[]}"; + const std::string SCHEMA_INDEX_CHANGE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_name5\":{}" + "}}," + "\"SCHEMA_INDEXES\":[\"field_name1\", [\"field_name2\", \"field_name1\", \"field_name3.field_name4\"]]}"; + const std::string SCHEMA_DEFINE_MORE_FIELD_MORE_INDEX = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_name5\":{" + "\"field_more1\":\"DOUBLE\"" + "}}}," + "\"SCHEMA_INDEXES\":[\"field_name1\", [\"field_name2\", \"field_name3.field_name4\"]]}"; + const std::string SCHEMA_SKIPSIZE_DIFFER = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_SKIPSIZE\":1," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_name5\":{}" + "}}," + "\"SCHEMA_INDEXES\":[\"field_name1\"]}"; + const std::string SCHEMA_DEFINE_TYPE_DIFFER = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"DOUBLE\"," + "\"field_name5\":{}" + "}}," + "\"SCHEMA_INDEXES\":[\"field_name1\"]}"; + const std::string SCHEMA_DEFINE_NOTNULL_DIFFER = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"INTEGER, DEFAULT 100\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_name5\":{}" + "}}," + "\"SCHEMA_INDEXES\":[\"field_name1\"]}"; + const std::string SCHEMA_DEFINE_DEFAULT_DIFFER = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL, NOT NULL\"," + "\"field_name2\":\"INTEGER, DEFAULT 88\"," + "\"field_name3\":{" + "\"field_name4\":\"STRING\"," + "\"field_name5\":{}" + "}}," + "\"SCHEMA_INDEXES\":[\"field_name1\"]}"; + + // Compare with VALID_SCHEMA_FULL_DEFINE + const std::string VALUE_LESS_FIELD = "{\"field_name1\":true," + "\"field_name2\":{" + "\"field_name3\":100," + "\"field_name8\":{" + "\"field_name9\":200" + "}" + "}}"; + const std::string VALUE_MORE_FIELD = "{\"field_name1\":true," + "\"field_name2\":{" + "\"field_name3\":100," + "\"field_name4\":8589934592," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415926\"," + "\"field_name7\":[true,1,\"inArray\"]," + "\"field_name8\":{" + "\"field_name9\":200" + "}," + "\"field_name10\":300" + "}}"; + const std::string VALUE_TYPE_MISMATCH = "{\"field_name1\":true," + "\"field_name2\":{" + "\"field_name3\":8589934592," + "\"field_name4\":100," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415926\"," + "\"field_name7\":[true,1,\"inArray\"]," + "\"field_name8\":{" + "\"field_name9\":200" + "}" + "}}"; + const std::string VALUE_NOT_NULL_VIOLATION = "{\"field_name1\":true," + "\"field_name2\":{" + "\"field_name3\":null," + "\"field_name4\":8589934592," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415926\"," + "\"field_name7\":[true,1,\"inArray\"]," + "\"field_name8\":{" + "\"field_name9\":200" + "}" + "}}"; + const std::string VALUE_MATCH_STRICT_SCHEMA = "{\"field_name1\":true," + "\"field_name2\":{" + "\"field_name3\":100," + "\"field_name4\":8589934592," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415926\"," + "\"field_name7\":[true,1,\"inArray\"]," + "\"field_name8\":{" + "\"field_name9\":200" + "}" + "}}"; + + // For test lacking field. + const std::string SCHEMA_FOR_TEST_NOTNULL_AND_DEFAULT = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"no_notnull_no_default\":\"BOOL\"," + "\"level_0_nest_0\":{" + "\"has_notnull_no_default\":\"INTEGER, NOT NULL\"," + "\"level_1_nest_0\":{" + "\"no_notnull_has_default\":\"LONG, DEFAULT 100\"," + "\"has_notnull_has_default\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"level_2_nest_0\":{" + "\"extra_0\":\"STRING, NOT NULL, DEFAULT '3.1415'\"," + "\"extra_1\":\"DOUBLE\"," + "\"extra_2\":[]" + "}," + "\"level_2_nest_1\":{" + "\"extra_3\":\"STRING\"," + "\"extra_4\":{}" + "}" + "}" + "}" + "}}"; + const std::string VALUE_NO_LACK_FIELD = "{" + "\"no_notnull_no_default\":true," + "\"level_0_nest_0\":{" + "\"has_notnull_no_default\":10010," + "\"level_1_nest_0\":{" + "\"no_notnull_has_default\":10086," + "\"has_notnull_has_default\":1.38064," + "\"level_2_nest_0\":{" + "\"extra_0\":\"BLOOM\"," + "\"extra_1\":2.71828," + "\"extra_2\":[]" + "}," + "\"level_2_nest_1\":{" + "\"extra_3\":\"Prejudice\"," + "\"extra_4\":{}" + "}" + "}" + "}}"; + const std::string VALUE_LACK_LEVEL_0_NEST_0 = "{\"no_notnull_no_default\":true}"; + const std::string VALUE_LEVEL_0_NEST_0_NOT_OBJECT = "{\"no_notnull_no_default\":true,\"level_0_nest_0\":1}"; + const std::string VALUE_LACK_LEVEL_1_NEST_0 = "{" + "\"no_notnull_no_default\":true," + "\"level_0_nest_0\":{" + "\"has_notnull_no_default\":10010" + "}}"; + +std::string SchemaSwitchMode(const std::string &oriSchemaStr) +{ + std::string resultSchemaStr = oriSchemaStr; + auto iterForStrict = std::search(resultSchemaStr.begin(), resultSchemaStr.end(), + SchemaConstant::KEYWORD_MODE_STRICT.begin(), SchemaConstant::KEYWORD_MODE_STRICT.end()); + auto iterForCompatible = std::search(resultSchemaStr.begin(), resultSchemaStr.end(), + SchemaConstant::KEYWORD_MODE_COMPATIBLE.begin(), SchemaConstant::KEYWORD_MODE_COMPATIBLE.end()); + if (iterForStrict != resultSchemaStr.end()) { + resultSchemaStr.replace(iterForStrict, iterForStrict + SchemaConstant::KEYWORD_MODE_STRICT.size(), + SchemaConstant::KEYWORD_MODE_COMPATIBLE.begin(), SchemaConstant::KEYWORD_MODE_COMPATIBLE.end()); + return resultSchemaStr; + } + if (iterForCompatible != resultSchemaStr.end()) { + resultSchemaStr.replace(iterForCompatible, iterForCompatible + SchemaConstant::KEYWORD_MODE_COMPATIBLE.size(), + SchemaConstant::KEYWORD_MODE_STRICT.begin(), SchemaConstant::KEYWORD_MODE_STRICT.end()); + return resultSchemaStr; + } + return oriSchemaStr; +} +} + +class DistributedDBSchemaObjectTest : public testing::Test { +public: + static void SetUpTestCase(void) {}; + static void TearDownTestCase(void) {}; + void SetUp() override; + void TearDown() override {}; +}; + +void DistributedDBSchemaObjectTest::SetUp() +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +/** + * @tc.name: Parse Valid Schema 001 + * @tc.desc: Parse Valid Schema + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, ParseValidSchema001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Parse valid schema with full define + * @tc.expected: step1. Parse Success. + */ + SchemaObject schema1; + int stepOne = schema1.ParseFromSchemaString(VALID_SCHEMA_FULL_DEFINE); + EXPECT_TRUE(stepOne == E_OK); + + /** + * @tc.steps: step2. Parse valid schema with empty index + * @tc.expected: step2. Parse Success. + */ + SchemaObject schema2; + int stepTwo = schema2.ParseFromSchemaString(VALID_SCHEMA_INDEX_EMPTY); + EXPECT_TRUE(stepTwo == E_OK); + + /** + * @tc.steps: step3. Parse valid schema with no index field + * @tc.expected: step3. Parse Success. + */ + SchemaObject schema3; + int stepThree = schema3.ParseFromSchemaString(VALID_SCHEMA_NO_INDEX); + EXPECT_TRUE(stepThree == E_OK); + + /** + * @tc.steps: step4. Parse valid schema with prefix of suffix blank + * @tc.expected: step4. Parse Success. + */ + SchemaObject schema4; + int stepFour = schema4.ParseFromSchemaString(VALID_SCHEMA_PRE_SUF_BLANK); + EXPECT_TRUE(stepFour == E_OK); +} + +/** + * @tc.name: Parse Invalid Schema 001 + * @tc.desc: Parse Invalid Schema + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, ParseInvalidSchema001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Parse invalid schema which is not valid json + * @tc.expected: step1. Parse Fail. + */ + SchemaObject schema1; + int stepOne = schema1.ParseFromSchemaString(INVALID_SCHEMA_INVALID_JSON); + EXPECT_TRUE(stepOne != E_OK); + + /** + * @tc.steps: step2. Parse invalid schema with less field in depth 0 + * @tc.expected: step2. Parse Fail. + */ + SchemaObject schema2; + int stepTwo = schema2.ParseFromSchemaString(INVALID_SCHEMA_LESS_META_FIELD); + EXPECT_TRUE(stepTwo != E_OK); + + /** + * @tc.steps: step3. Parse invalid schema with more field in depth 0 + * @tc.expected: step3. Parse Fail. + */ + SchemaObject schema3; + int stepThree = schema3.ParseFromSchemaString(INVALID_SCHEMA_MORE_META_FIELD); + EXPECT_TRUE(stepThree != E_OK); + + /** + * @tc.steps: step4. Parse invalid schema with wrong version + * @tc.expected: step4. Parse Fail. + */ + SchemaObject schema4; + int stepFour = schema4.ParseFromSchemaString(INVALID_SCHEMA_WRONG_VERSION); + EXPECT_TRUE(stepFour != E_OK); + + /** + * @tc.steps: step5. Parse invalid schema with wrong mode + * @tc.expected: step5. Parse Fail. + */ + SchemaObject schema5; + int stepFive = schema5.ParseFromSchemaString(INVALID_SCHEMA_WRONG_MODE); + EXPECT_TRUE(stepFive != E_OK); +} + +/** + * @tc.name: Parse Invalid Schema 002 + * @tc.desc: Parse Invalid Schema + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, ParseInvalidSchema002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Parse invalid schema which is empty define + * @tc.expected: step1. Parse Fail. + */ + SchemaObject schema1; + int stepOne = schema1.ParseFromSchemaString(INVALID_SCHEMA_DEFINE_EMPTY); + EXPECT_TRUE(stepOne != E_OK); + + /** + * @tc.steps: step2. Parse invalid schema with define nest too deep + * @tc.expected: step2. Parse Fail. + */ + SchemaObject schema2; + int stepTwo = schema2.ParseFromSchemaString(INVALID_SCHEMA_DEFINE_NEST_TOO_DEEP); + EXPECT_TRUE(stepTwo != E_OK); + + /** + * @tc.steps: step3. Parse invalid schema with invalid fieldname in define + * @tc.expected: step3. Parse Fail. + */ + SchemaObject schema3; + int stepThree = schema3.ParseFromSchemaString(INVALID_SCHEMA_DEFINE_INVALID_FIELD_NAME); + EXPECT_TRUE(stepThree != E_OK); + + /** + * @tc.steps: step4. Parse invalid schema with invalid field attribute in define + * @tc.expected: step4. Parse Fail. + */ + SchemaObject schema4; + int stepFour = schema4.ParseFromSchemaString(INVALID_SCHEMA_DEFINE_INVALID_FIELD_ATTR); + EXPECT_TRUE(stepFour != E_OK); + + /** + * @tc.steps: step5. Parse invalid schema with not empty array in define + * @tc.expected: step5. Parse Fail. + */ + SchemaObject schema5; + int stepFive = schema5.ParseFromSchemaString(INVALID_SCHEMA_DEFINE_INVALID_ARRAY_TYPE); + EXPECT_TRUE(stepFive != E_OK); +} + +/** + * @tc.name: Parse Invalid Schema 003 + * @tc.desc: Parse Invalid Schema + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, ParseInvalidSchema003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Parse invalid schema with invalid array content + * @tc.expected: step1. Parse Fail. + */ + SchemaObject schema1; + int stepOne = schema1.ParseFromSchemaString(INVALID_SCHEMA_INDEX_INVALID); + EXPECT_TRUE(stepOne != E_OK); + + /** + * @tc.steps: step2. Parse invalid schema with invalid path + * @tc.expected: step2. Parse Fail. + */ + SchemaObject schema2; + int stepTwo = schema2.ParseFromSchemaString(INVALID_SCHEMA_INDEX_PATH_INVALID); + EXPECT_TRUE(stepTwo != E_OK); + + /** + * @tc.steps: step3. Parse invalid schema with path not exist + * @tc.expected: step3. Parse Fail. + */ + SchemaObject schema3; + int stepThree = schema3.ParseFromSchemaString(INVALID_SCHEMA_INDEX_PATH_NOT_EXIST); + EXPECT_TRUE(stepThree != E_OK); + + /** + * @tc.steps: step4. Parse invalid schema with path not indexable + * @tc.expected: step4. Parse Fail. + */ + SchemaObject schema4; + int stepFour = schema4.ParseFromSchemaString(INVALID_SCHEMA_INDEX_PATH_NOT_INDEXABLE); + EXPECT_TRUE(stepFour != E_OK); + + /** + * @tc.steps: step5. Parse invalid schema with duplicate + * @tc.expected: step5. Parse Fail. + */ + SchemaObject schema5; + int stepFive = schema5.ParseFromSchemaString(INVALID_SCHEMA_INDEX_PATH_DUPLICATE); + EXPECT_TRUE(stepFive != E_OK); +} + +/** + * @tc.name: Compare Equal Exactly 001 + * @tc.desc: Compare Equal Exactly + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, CompareEqualExactly001, TestSize.Level1) +{ + SchemaObject schemaOri; + int errCode = schemaOri.ParseFromSchemaString(VALID_SCHEMA_FULL_DEFINE); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step1. Compare two same schema with full define + * @tc.expected: step1. Equal exactly. + */ + int stepOne = schemaOri.CompareAgainstSchemaString(VALID_SCHEMA_FULL_DEFINE); + EXPECT_TRUE(stepOne == -E_SCHEMA_EQUAL_EXACTLY); +} + +/** + * @tc.name: Compare Unequal Compatible 001 + * @tc.desc: Compare Unequal Compatible + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, CompareUnequalCompatible001, TestSize.Level1) +{ + SchemaObject compatibleSchema; + int errCode = compatibleSchema.ParseFromSchemaString(SCHEMA_COMPARE_BASELINE); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step1. new schema index more + * @tc.expected: step1. E_SCHEMA_UNEQUAL_COMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_INDEX_MORE); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_COMPATIBLE); + + /** + * @tc.steps: step2. new schema index less + * @tc.expected: step2. E_SCHEMA_UNEQUAL_COMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_INDEX_LESS); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_COMPATIBLE); + + /** + * @tc.steps: step3. new schema index change + * @tc.expected: step3. E_SCHEMA_UNEQUAL_COMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_INDEX_CHANGE); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_COMPATIBLE); +} + +/** + * @tc.name: Compare Unequal Compatible Upgrade 001 + * @tc.desc: Compare Unequal Compatible Upgrade + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, CompareUnequalCompatibleUpgrade001, TestSize.Level1) +{ + SchemaObject compatibleSchema; + int errCode = compatibleSchema.ParseFromSchemaString(SCHEMA_COMPARE_BASELINE); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step1. compatible new schema more field define + * @tc.expected: step1. E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_DEFINE_MORE_FIELD); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE); + + /** + * @tc.steps: step2. compatible new schema more field with not null and default + * @tc.expected: step2. E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_DEFINE_MORE_FIELD_NOTNULL_PERMIT); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE); + + /** + * @tc.steps: step3. compatible new schema more field and more index + * @tc.expected: step3. E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_DEFINE_MORE_FIELD_MORE_INDEX); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE); +} + +/** + * @tc.name: Compare Unequal Incompatible 001 + * @tc.desc: Compare Unequal Incompatible + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, CompareUnequalIncompatible001, TestSize.Level1) +{ + SchemaObject strictSchema; + int errCode = strictSchema.ParseFromSchemaString(SchemaSwitchMode(SCHEMA_COMPARE_BASELINE)); + EXPECT_EQ(errCode, E_OK); + SchemaObject compatibleSchema; + errCode = compatibleSchema.ParseFromSchemaString(SCHEMA_COMPARE_BASELINE); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step1. strict new schema more field define + * @tc.expected: step1. E_SCHEMA_UNEQUAL_INCOMPATIBLE. + */ + errCode = strictSchema.CompareAgainstSchemaString(SchemaSwitchMode(SCHEMA_DEFINE_MORE_FIELD)); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_INCOMPATIBLE); + + /** + * @tc.steps: step2. compatible new schema more field but not null + * @tc.expected: step2. E_SCHEMA_UNEQUAL_INCOMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_DEFINE_MORE_FIELD_NOTNULL_FORBID); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_INCOMPATIBLE); + + /** + * @tc.steps: step3. new schema less field + * @tc.expected: step3. E_SCHEMA_UNEQUAL_INCOMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_DEFINE_LESS_FIELD); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_INCOMPATIBLE); + errCode = strictSchema.CompareAgainstSchemaString(SchemaSwitchMode(SCHEMA_DEFINE_LESS_FIELD)); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_INCOMPATIBLE); + + /** + * @tc.steps: step4. new schema skipsize differ + * @tc.expected: step4. E_SCHEMA_UNEQUAL_INCOMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_SKIPSIZE_DIFFER); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_INCOMPATIBLE); + + /** + * @tc.steps: step5. new schema type differ + * @tc.expected: step5. E_SCHEMA_UNEQUAL_INCOMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_DEFINE_TYPE_DIFFER); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_INCOMPATIBLE); + + /** + * @tc.steps: step6. new schema notnull differ + * @tc.expected: step6. E_SCHEMA_UNEQUAL_INCOMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_DEFINE_NOTNULL_DIFFER); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_INCOMPATIBLE); + + /** + * @tc.steps: step7. new schema default differ + * @tc.expected: step7. E_SCHEMA_UNEQUAL_INCOMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SCHEMA_DEFINE_DEFAULT_DIFFER); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_INCOMPATIBLE); + + /** + * @tc.steps: step8. new schema mode differ + * @tc.expected: step8. E_SCHEMA_UNEQUAL_INCOMPATIBLE. + */ + errCode = compatibleSchema.CompareAgainstSchemaString(SchemaSwitchMode(SCHEMA_COMPARE_BASELINE)); + EXPECT_EQ(errCode, -E_SCHEMA_UNEQUAL_INCOMPATIBLE); +} + +/** + * @tc.name: Check Value 001 + * @tc.desc: Check value both in strict and compatible mode + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, CheckValue001, TestSize.Level1) +{ + SchemaObject schemaStrict; + int errCode = schemaStrict.ParseFromSchemaString(VALID_SCHEMA_FULL_DEFINE); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step1. value has less field in strict mode + * @tc.expected: step1. E_VALUE_MATCH_AMENDED. + */ + ValueObject value1; + errCode = value1.Parse(VALUE_LESS_FIELD); + EXPECT_TRUE(errCode == E_OK); + int stepOne = schemaStrict.CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, value1); + EXPECT_TRUE(stepOne == -E_VALUE_MATCH_AMENDED); + + /** + * @tc.steps: step2. value has more field in strict mode + * @tc.expected: step2. E_VALUE_MISMATCH_FEILD_COUNT. + */ + ValueObject value2; + errCode = value2.Parse(VALUE_MORE_FIELD); + EXPECT_TRUE(errCode == E_OK); + int stepTwo = schemaStrict.CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, value2); + EXPECT_TRUE(stepTwo == -E_VALUE_MISMATCH_FEILD_COUNT); + + /** + * @tc.steps: step3. value type mismatch + * @tc.expected: step3. E_VALUE_MISMATCH_FEILD_TYPE. + */ + ValueObject value3; + errCode = value3.Parse(VALUE_TYPE_MISMATCH); + EXPECT_TRUE(errCode == E_OK); + int stepThree = schemaStrict.CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, value3); + EXPECT_TRUE(stepThree == -E_VALUE_MISMATCH_FEILD_TYPE); + + /** + * @tc.steps: step4. value not null violation + * @tc.expected: step4. E_VALUE_MISMATCH_CONSTRAINT. + */ + ValueObject value4; + errCode = value4.Parse(VALUE_NOT_NULL_VIOLATION); + EXPECT_TRUE(errCode == E_OK); + int stepFour = schemaStrict.CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, value4); + EXPECT_TRUE(stepFour == -E_VALUE_MISMATCH_CONSTRAINT); + + /** + * @tc.steps: step5. value exactly match strict mode + * @tc.expected: step5. E_VALUE_MATCH. + */ + ValueObject value5; + errCode = value5.Parse(VALUE_MATCH_STRICT_SCHEMA); + EXPECT_TRUE(errCode == E_OK); + int stepFive = schemaStrict.CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, value5); + EXPECT_TRUE(stepFive == -E_VALUE_MATCH); + + /** + * @tc.steps: step6. value has more field in compatible mode + * @tc.expected: step6. E_VALUE_MATCH. + */ + std::string compatibleSchemaString = SchemaSwitchMode(VALID_SCHEMA_FULL_DEFINE); + SchemaObject schemaCompatible; + errCode = schemaCompatible.ParseFromSchemaString(compatibleSchemaString); + EXPECT_TRUE(errCode == E_OK); + + ValueObject value6; + std::vector moreFieldValueVector(VALUE_MORE_FIELD.begin(), VALUE_MORE_FIELD.end()); + errCode = value6.Parse(moreFieldValueVector); + EXPECT_TRUE(errCode == E_OK); + int stepSix = schemaCompatible.CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, value6); + EXPECT_TRUE(stepSix == -E_VALUE_MATCH); +} + +/** + * @tc.name: Check Value 002 + * @tc.desc: Check value that has offset + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, CheckValue002, TestSize.Level1) +{ + SchemaObject schemaStrict; + int errCode = schemaStrict.ParseFromSchemaString(VALID_SCHEMA_FULL_DEFINE); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step1. value has less field in strict mode + * @tc.expected: step1. E_VALUE_MATCH and data before offset not change. + */ + std::string beforeOffset = "BOM_CONTENT:"; + std::string strValue = beforeOffset + VALUE_MATCH_STRICT_SCHEMA; + vector vecValue(strValue.begin(), strValue.end()); + + ValueObject value1; + errCode = value1.Parse(vecValue.data(), vecValue.data() + vecValue.size(), beforeOffset.size()); + EXPECT_TRUE(errCode == E_OK); + + int stepOne = schemaStrict.CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, value1); + EXPECT_TRUE(stepOne == -E_VALUE_MATCH); + + std::string valueToString = value1.ToString(); + EXPECT_EQ(strValue.size(), valueToString.size()); + std::string valueBeforeOffset = valueToString.substr(0, beforeOffset.size()); + EXPECT_EQ(valueBeforeOffset, beforeOffset); +} + +/** + * @tc.name: Value Edit 001 + * @tc.desc: Edit the value in right way + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, ValueEdit001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Insert value to ValueObject in different depth + * @tc.expected: step1. Check insert successful + */ + ValueObject testObject; + FieldValue val; + + val.stringValue = "stringValue"; + int errCode = testObject.InsertField(FieldPath{"L1F1", "L2F1", "L3F1", "L4F1"}, FieldType::LEAF_FIELD_STRING, val); + EXPECT_TRUE(errCode == E_OK); + val.doubleValue = 1.1; // 1.1 for test + errCode = testObject.InsertField(FieldPath{"L1F1", "L2F1", "L3F1", "L4F2"}, FieldType::LEAF_FIELD_DOUBLE, val); + EXPECT_TRUE(errCode == E_OK); + val.longValue = INT64_MAX; + errCode = testObject.InsertField(FieldPath{"L1F1", "L2F1", "L3F2"}, FieldType::LEAF_FIELD_LONG, val); + EXPECT_TRUE(errCode == E_OK); + errCode = testObject.InsertField(FieldPath{"L1F1", "L2F2"}, FieldType::LEAF_FIELD_OBJECT, val); + EXPECT_TRUE(errCode == E_OK); + val.integerValue = INT32_MIN; + errCode = testObject.InsertField(FieldPath{"L1F1", "L2F2", "L3F3"}, FieldType::LEAF_FIELD_INTEGER, val); + EXPECT_TRUE(errCode == E_OK); + val.boolValue = true; + errCode = testObject.InsertField(FieldPath{"L1F1", "L2F2", "L3F4"}, FieldType::LEAF_FIELD_BOOL, val); + EXPECT_TRUE(errCode == E_OK); + errCode = testObject.InsertField(FieldPath{"L1F2"}, FieldType::LEAF_FIELD_NULL, val); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step2. Delete value in ValueObject + * @tc.expected: step2. Check delete successful + */ + errCode = testObject.DeleteField(FieldPath{"L1F1", "L2F1", "L3F1", "L4F1"}); + EXPECT_TRUE(errCode == E_OK); + errCode = testObject.DeleteField(FieldPath{"L1F1", "L2F1", "L3F1"}); + EXPECT_TRUE(errCode == E_OK); + errCode = testObject.DeleteField(FieldPath{"L1F1"}); + EXPECT_TRUE(errCode == E_OK); +} + +/** + * @tc.name: Value Edit 002 + * @tc.desc: Edit the value in wrong way + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, ValueEdit002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Insert value to ValueObject in different depth + * @tc.expected: step1. Check insert not successful + */ + ValueObject testObject; + FieldValue val; + + val.stringValue = "stringValue"; + int errCode = testObject.InsertField(FieldPath{"L1F1", "L2F1", "L3F1"}, FieldType::LEAF_FIELD_STRING, val); + EXPECT_TRUE(errCode == E_OK); + val.doubleValue = 1.1; // 1.1 for test + errCode = testObject.InsertField(FieldPath{"L1F1", "L2F1", "L3F1"}, FieldType::LEAF_FIELD_DOUBLE, val); + EXPECT_TRUE(errCode != E_OK); + val.longValue = INT64_MAX; + errCode = testObject.InsertField(FieldPath{"L1F1", "L2F1", "L3F1", "L4F1"}, FieldType::LEAF_FIELD_LONG, val); + EXPECT_TRUE(errCode != E_OK); + + /** + * @tc.steps: step2. Delete value in ValueObject + * @tc.expected: step2. Check delete not successful + */ + errCode = testObject.DeleteField(FieldPath{"L1F1", "L2F1", "L3F1", "L4F1"}); + EXPECT_TRUE(errCode != E_OK); +} + +namespace { +void CheckValueLackField(const SchemaObject &schema, const std::string &oriValue, const std::string &lackField, + int expectErrCode, ValueObject &externalValueObject) +{ + std::string valueStr = oriValue; + auto startIter = std::search(valueStr.begin(), valueStr.end(), lackField.begin(), lackField.end()); + valueStr.erase(startIter, startIter + lackField.size()); + int errCode = externalValueObject.Parse(valueStr); + EXPECT_EQ(errCode, E_OK); + errCode = schema.CheckValueAndAmendIfNeed(ValueSource::FROM_LOCAL, externalValueObject); + EXPECT_EQ(errCode, expectErrCode); +} + +void CheckValueLackField(const SchemaObject &schema, const std::string &oriValue, const std::string &lackField, + int expectErrCode) +{ + ValueObject valueObj; + CheckValueLackField(schema, oriValue, lackField, expectErrCode, valueObj); +} +} + +/** + * @tc.name: Value LackField 001 + * @tc.desc: check the value which lack field + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, ValueLackField001, TestSize.Level1) +{ + SchemaObject schema; + int errCode = schema.ParseFromSchemaString(SCHEMA_FOR_TEST_NOTNULL_AND_DEFAULT); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step1. check value lack no field + * @tc.expected: step1. E_VALUE_MATCH + */ + CheckValueLackField(schema, VALUE_NO_LACK_FIELD, "", -E_VALUE_MATCH); + + /** + * @tc.steps: step2. check value lack field on no_notnull_no_default + * @tc.expected: step2. E_VALUE_MATCH + */ + CheckValueLackField(schema, VALUE_NO_LACK_FIELD, "\"no_notnull_no_default\":true,", -E_VALUE_MATCH); + + /** + * @tc.steps: step3. check value lack field on has_notnull_no_default + * @tc.expected: step3. E_VALUE_MISMATCH_CONSTRAINT + */ + CheckValueLackField(schema, VALUE_NO_LACK_FIELD, "\"has_notnull_no_default\":10010,", + -E_VALUE_MISMATCH_CONSTRAINT); + + /** + * @tc.steps: step4. check value lack field on no_notnull_has_default + * @tc.expected: step4. E_VALUE_MATCH_AMENDED + */ + CheckValueLackField(schema, VALUE_NO_LACK_FIELD, "\"no_notnull_has_default\":10086,", + -E_VALUE_MATCH_AMENDED); + + /** + * @tc.steps: step5. check value lack field on has_notnull_has_default + * @tc.expected: step5. E_VALUE_MATCH_AMENDED + */ + CheckValueLackField(schema, VALUE_NO_LACK_FIELD, "\"has_notnull_has_default\":1.38064,", + -E_VALUE_MATCH_AMENDED); + + /** + * @tc.steps: step6. check value lack entire level_0_nest_0 + * @tc.expected: step6. E_VALUE_MISMATCH_CONSTRAINT + */ + CheckValueLackField(schema, VALUE_LACK_LEVEL_0_NEST_0, "", -E_VALUE_MISMATCH_CONSTRAINT); + + /** + * @tc.steps: step7. check value level_0_nest_0 not json_object + * @tc.expected: step7. E_VALUE_MISMATCH_FEILD_TYPE + */ + CheckValueLackField(schema, VALUE_LEVEL_0_NEST_0_NOT_OBJECT, "", -E_VALUE_MISMATCH_FEILD_TYPE); +} + +/** + * @tc.name: Value LackField 002 + * @tc.desc: check the value which lack field + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBSchemaObjectTest, ValueLackField002, TestSize.Level1) +{ + SchemaObject schema; + int errCode = schema.ParseFromSchemaString(SCHEMA_FOR_TEST_NOTNULL_AND_DEFAULT); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step1. check value lack entire level_1_nest_0 + * @tc.expected: step1. E_VALUE_MATCH_AMENDED + */ + ValueObject val; + CheckValueLackField(schema, VALUE_LACK_LEVEL_1_NEST_0, "", -E_VALUE_MATCH_AMENDED, val); + // Check Field Existence or not + EXPECT_EQ(val.IsFieldPathExist(FieldPath{"level_0_nest_0", "level_1_nest_0"}), true); + EXPECT_EQ(val.IsFieldPathExist(FieldPath{"level_0_nest_0", "level_1_nest_0", "no_notnull_has_default"}), true); + EXPECT_EQ(val.IsFieldPathExist(FieldPath{"level_0_nest_0", "level_1_nest_0", "has_notnull_has_default"}), true); + EXPECT_EQ(val.IsFieldPathExist(FieldPath{"level_0_nest_0", "level_1_nest_0", "level_2_nest_0"}), true); + EXPECT_EQ(val.IsFieldPathExist(FieldPath{"level_0_nest_0", "level_1_nest_0", "level_2_nest_0", "extra_0"}), true); + EXPECT_EQ(val.IsFieldPathExist(FieldPath{"level_0_nest_0", "level_1_nest_0", "level_2_nest_0", "extra_1"}), false); + EXPECT_EQ(val.IsFieldPathExist(FieldPath{"level_0_nest_0", "level_1_nest_0", "level_2_nest_0", "extra_2"}), false); + EXPECT_EQ(val.IsFieldPathExist(FieldPath{"level_0_nest_0", "level_1_nest_0", "level_2_nest_1"}), false); + // Check Field value + FieldValue theValue; + EXPECT_EQ(val.GetFieldValueByFieldPath(FieldPath{"level_0_nest_0", "level_1_nest_0", "no_notnull_has_default"}, + theValue), E_OK); + EXPECT_EQ(theValue.integerValue, 100); + EXPECT_EQ(val.GetFieldValueByFieldPath(FieldPath{"level_0_nest_0", "level_1_nest_0", "has_notnull_has_default"}, + theValue), E_OK); + EXPECT_LT(std::abs(theValue.doubleValue - 3.14), 0.1); + EXPECT_EQ(val.GetFieldValueByFieldPath(FieldPath{"level_0_nest_0", "level_1_nest_0", "level_2_nest_0", "extra_0"}, + theValue), E_OK); + EXPECT_EQ(theValue.stringValue == std::string("3.1415"), true); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_schema_unit_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_schema_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f6a58261172e60d48abd2c5cf24efba22edff52 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_schema_unit_test.cpp @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "distributeddb_tools_unit_test.h" + +#include + +#include "db_errno.h" +#include "log_print.h" +#include "schema_utils.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +class DistributedDBSchemalTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSchemalTest::SetUpTestCase(void) +{ +} + +void DistributedDBSchemalTest::TearDownTestCase(void) +{ +} + +void DistributedDBSchemalTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBSchemalTest::TearDown(void) +{ +} + +namespace { +map g_schemaAttrDefTestDataDir; + +void CheckSchemaAttribute(const SchemaAttribute &res, const SchemaAttribute &check) +{ + EXPECT_EQ(res.type, check.type); + EXPECT_EQ(res.isIndexable, check.isIndexable); + EXPECT_EQ(res.hasNotNullConstraint, check.hasNotNullConstraint); + EXPECT_EQ(res.hasDefaultValue, check.hasDefaultValue); + EXPECT_EQ(res.defaultValue.stringValue, check.defaultValue.stringValue); + EXPECT_EQ(memcmp(&res.defaultValue, &check.defaultValue, 8), 0); // only check this unit 8 byte +} + +void PreNumDataForParseAndCheckSchemaAttribute003() +{ + SchemaAttribute attributeRes; + attributeRes.type = FieldType::LEAF_FIELD_INTEGER; + attributeRes.defaultValue.integerValue = 0; + attributeRes.hasDefaultValue = true; + g_schemaAttrDefTestDataDir["INTEGER, DEFAULT 0"] = attributeRes; + + SchemaAttribute attributeRes1; + attributeRes1.type = FieldType::LEAF_FIELD_INTEGER; + attributeRes1.hasDefaultValue = true; + attributeRes1.hasNotNullConstraint = true; + attributeRes1.defaultValue.integerValue = INT32_MAX; + g_schemaAttrDefTestDataDir["INTEGER, NOT NULL, DEFAULT " + std::to_string(INT32_MAX)] = attributeRes1; + + SchemaAttribute attributeRes2; + attributeRes2.type = FieldType::LEAF_FIELD_INTEGER; + attributeRes2.hasDefaultValue = true; + attributeRes2.defaultValue.integerValue = 0; + g_schemaAttrDefTestDataDir["INTEGER, DEFAULT +0"] = attributeRes2; + + SchemaAttribute attributeRes3; + attributeRes3.type = FieldType::LEAF_FIELD_LONG; + attributeRes3.hasDefaultValue = true; + attributeRes3.defaultValue.longValue = 0; + g_schemaAttrDefTestDataDir["LONG, DEFAULT -0"] = attributeRes3; + + SchemaAttribute attributeRes4; + attributeRes4.type = FieldType::LEAF_FIELD_LONG; + attributeRes4.hasNotNullConstraint = true; + attributeRes4.hasDefaultValue = true; + attributeRes4.defaultValue.longValue = LONG_MAX; + g_schemaAttrDefTestDataDir["LONG, NOT NULL,DEFAULT " + std::to_string(LONG_MAX)] = attributeRes4; +} + +void PreStringDataForParseAndCheckSchemaAttribute003() +{ + SchemaAttribute attributeRes5; + attributeRes5.type = FieldType::LEAF_FIELD_STRING; + attributeRes5.hasDefaultValue = true; + attributeRes5.defaultValue.stringValue = "11ada%$%"; + g_schemaAttrDefTestDataDir["STRING , DEFAULT '11ada%$%'"] = attributeRes5; + + SchemaAttribute attributeRes6; + attributeRes6.type = FieldType::LEAF_FIELD_STRING; + attributeRes6.hasNotNullConstraint = true; + attributeRes6.hasDefaultValue = true; + attributeRes6.defaultValue.stringValue = "asdasd_\n\t"; + g_schemaAttrDefTestDataDir["STRING, NOT NULL , DEFAULT 'asdasd_\n\t'"] = attributeRes6; +} + +void PreDoubleDataForParseAndCheckSchemaAttribute003() +{ + SchemaAttribute attributeRes7; + attributeRes7.type = FieldType::LEAF_FIELD_DOUBLE; + attributeRes7.hasDefaultValue = true; + attributeRes7.defaultValue.doubleValue = 0; + g_schemaAttrDefTestDataDir["DOUBLE,DEFAULT 0.0"] = attributeRes7; + + SchemaAttribute attributeRes8; + attributeRes8.type = FieldType::LEAF_FIELD_DOUBLE; + attributeRes8.hasDefaultValue = true; + attributeRes8.defaultValue.doubleValue = 0; + g_schemaAttrDefTestDataDir["DOUBLE,DEFAULT 0."] = attributeRes8; + + SchemaAttribute attributeRes9; + attributeRes9.type = FieldType::LEAF_FIELD_DOUBLE; + attributeRes9.hasDefaultValue = true; + attributeRes9.defaultValue.doubleValue = 0.1; // 0.1 as test data + g_schemaAttrDefTestDataDir["DOUBLE,DEFAULT 0.1"] = attributeRes9; + + SchemaAttribute attributeRes10; + attributeRes10.type = FieldType::LEAF_FIELD_DOUBLE; + attributeRes10.hasNotNullConstraint = true; + attributeRes10.hasDefaultValue = true; + attributeRes10.defaultValue.doubleValue = -0.123456; // -0.123456 as test data + g_schemaAttrDefTestDataDir["DOUBLE, NOT NULL,DEFAULT -0.123456"] = attributeRes10; + + SchemaAttribute attributeRes11; + attributeRes11.type = FieldType::LEAF_FIELD_DOUBLE; + attributeRes11.hasNotNullConstraint = false; + attributeRes11.hasDefaultValue = true; + attributeRes11.defaultValue.doubleValue = 0; + g_schemaAttrDefTestDataDir["DOUBLE,DEFAULT +0.0"] = attributeRes11; + + // double -0 Has been manually verified + SchemaAttribute attributeRes13; + attributeRes13.type = FieldType::LEAF_FIELD_DOUBLE; + attributeRes13.hasNotNullConstraint = true; + attributeRes13.hasDefaultValue = true; + attributeRes13.defaultValue.doubleValue = DBL_MAX; + g_schemaAttrDefTestDataDir["DOUBLE, NOT NULL,DEFAULT " + std::to_string(DBL_MAX)] = attributeRes13; +} + +void PreBoolDataForParseAndCheckSchemaAttribute003() +{ + SchemaAttribute attributeRes14; + attributeRes14.type = FieldType::LEAF_FIELD_BOOL; + attributeRes14.hasNotNullConstraint = false; + attributeRes14.hasDefaultValue = true; + attributeRes14.defaultValue.boolValue = false; + g_schemaAttrDefTestDataDir["BOOL,DEFAULT false"] = attributeRes14; + + SchemaAttribute attributeRes15; + attributeRes15.type = FieldType::LEAF_FIELD_BOOL; + attributeRes15.hasNotNullConstraint = true; + attributeRes15.hasDefaultValue = true; + attributeRes15.defaultValue.boolValue = true; + g_schemaAttrDefTestDataDir["BOOL, NOT NULL,DEFAULT true"] = attributeRes15; +} +} // namespace + +/** + * @tc.name: ParseAndCheckSchemaAttribute001 + * @tc.desc: Ability to recognize and parse the correct schema attribute format + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBSchemalTest, ParseAndCheckSchemaAttribute001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Preset shcema attribute strings that are correctly written according to the definition. + */ + SchemaAttribute attributeRes; + attributeRes.type = FieldType::LEAF_FIELD_INTEGER; + g_schemaAttrDefTestDataDir["INTEGER"] = attributeRes; + + SchemaAttribute attributeRes1; + attributeRes1.type = FieldType::LEAF_FIELD_BOOL; + attributeRes1.hasNotNullConstraint = true; + g_schemaAttrDefTestDataDir["BOOL, NOT NULL"] = attributeRes1; + + SchemaAttribute attributeRes2; + attributeRes2.type = FieldType::LEAF_FIELD_STRING; + attributeRes2.hasDefaultValue = true; + attributeRes2.defaultValue.stringValue = "dasdads"; + g_schemaAttrDefTestDataDir["STRING,DEFAULT 'dasdads'"] = attributeRes2; + + SchemaAttribute attributeRes3; + attributeRes3.type = FieldType::LEAF_FIELD_DOUBLE; + attributeRes3.hasDefaultValue = true; + attributeRes3.hasNotNullConstraint = true; + attributeRes3.defaultValue.doubleValue = -1.0; + g_schemaAttrDefTestDataDir["\tDOUBLE \t,\t\t\tNOT NULL , DEFAULT -1.0"] = attributeRes3; + + SchemaAttribute attributeRes4; + attributeRes4.type = FieldType::LEAF_FIELD_LONG; + attributeRes4.hasNotNullConstraint = false; + attributeRes4.hasDefaultValue = false; + g_schemaAttrDefTestDataDir["LONG,DEFAULT null"] = attributeRes4; + + /** + * @tc.steps: step2. Call interface + * @tc.expected: step2. Returns E_OK and parses correctly. + */ + for (auto &iter : g_schemaAttrDefTestDataDir) { + SchemaAttribute attributeOut; + LOGD("Attr : %s", iter.first.c_str()); + EXPECT_EQ(SchemaUtils::ParseAndCheckSchemaAttribute(iter.first, attributeOut), E_OK); + CheckSchemaAttribute(iter.second, attributeOut); + } +} + +/** + * @tc.name: ParseAndCheckSchemaAttribute002 + * @tc.desc: Can identify the wrong schema attribute format and report an error. + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBSchemalTest, ParseAndCheckSchemaAttribute002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Preset shcema attributes based on definition error. + */ + std::vector preData = { + "", + " ", + "$INTEGER", + "INTEGER NOT_NULL DEFAULT 1", + "STRING \n DEFAULT 'a'", + "BOOL,NOT NULL", + "LONG,NOT\tNULL", + "BOOL,NOT null", + "bool,not null", + "BOOL,NOT NULL,default false", + "INTEGER,", + "BOOL, NOT NULL,", + "BOOL, NOT NULL,DEFAULT ", + "BOOL, DEFAULT false, NOT NULL", + "DEFAULT 1, LONG, NOT NULL", + "DEFAULT 1", + "NOT NULL, DEFAULT x", + ", NOT NULL DEFAULT 1", + "LONG, NOT NULL, DEFAULT null" + }; + string overflowDol = to_string(DBL_MAX); + overflowDol = '1' + overflowDol; + preData.push_back("DOUBLE, NOT NULL, DEFAULT " + overflowDol); + preData.push_back("DOUBLE, NOT NULL, DEFAULT -" + overflowDol); + + preData.push_back("INTEGER, NOT NULL, DEFAULT 2147483648"); // int max + 1; + preData.push_back("INTEGER, NOT NULL, DEFAULT -2147483649"); + preData.push_back("LONG, NOT NULL, DEFAULT 9223372036854775808"); // long max + 1; + preData.push_back("LONG, NOT NULL, DEFAULT -9223372036854775809"); + + /** + * @tc.steps: step2. Call interface ParseAndCheckSchemaAttribute. + * @tc.expected: step2. Returns -E_SCHEMA_PARSE_FAIL. + */ + for (auto &iter : preData) { + SchemaAttribute attributeOut; + LOGD("Attr : %s", iter.c_str()); + EXPECT_EQ(SchemaUtils::ParseAndCheckSchemaAttribute(iter, attributeOut), -E_SCHEMA_PARSE_FAIL); + } +} + +/** + * @tc.name: ParseAndCheckSchemaAttribute003 + * @tc.desc: Can correctly interpret the meaning of each keyword of the schema attribute + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBSchemalTest, ParseAndCheckSchemaAttribute003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Preset shcema attributes based on defining correct format and content. + */ + g_schemaAttrDefTestDataDir.clear(); + PreNumDataForParseAndCheckSchemaAttribute003(); + PreDoubleDataForParseAndCheckSchemaAttribute003(); + PreStringDataForParseAndCheckSchemaAttribute003(); + PreBoolDataForParseAndCheckSchemaAttribute003(); + + /** + * @tc.steps: step2. Call interface ParseAndCheckSchemaAttribute. + * @tc.expected: step2. Returns E_OK and parses correctly. + */ + for (auto &iter : g_schemaAttrDefTestDataDir) { + SchemaAttribute attributeOut; + EXPECT_EQ(SchemaUtils::ParseAndCheckSchemaAttribute(iter.first, attributeOut), E_OK); + CheckSchemaAttribute(iter.second, attributeOut); + } +} + +/** + * @tc.name: ParseAndCheckSchemaAttribute004 + * @tc.desc: Can correctly identify the meaning of the schema attribute field that is incorrectly parsed + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBSchemalTest, ParseAndCheckSchemaAttribute004, TestSize.Level1) +{ + /** + * @tc.steps: step1. Preset shcema attributes based on defining incorrect format and content. + */ + std::vector preData = { + "LONG,NOT NULL, DEFAULT '123'", + "STRING,DEFAULT true", + "INTEGER,NOT NULL,DEFAULT MAX+1", + "LONG,DEFAULT 0.0", + "INTEGER,NOT NULL,DEFAULT - 123", + "INTEGER,DEFAULT 12 3", + "LONG,NOT NULL,DEFAULT 0xFF", + "INTEGER,00", + "DOUBLE,DEFAULT 123a", + "DOUBLE,NOT NULL,DEFAULT 0..0", + "DOUBLE,DEFAULT 2e2", + "DOUBLE,NOT NULL,DEFAULT 1+1", + "DOUBLE,NOT NULL,DEFAULT .0", + "DOUBLE,DEFAULT MAX+1", + "STRING,DEFAULT 123", + "STRING,NOT NULL,DEFAULT 'ABC", + "BOOL,DEFAULT TRUE", + "INT", + "long", + "String", + "STRING DEFAULT 'a'a", + }; + + /** + * @tc.steps: step2. Call interface ParseAndCheckSchemaAttribute. + * @tc.expected: step2. Returns -E_SCHEMA_PARSE_FAIL. + */ + string overSize(4 * 1024 + 1, 'a'); + preData.push_back("STRING, DEFAULT '" + overSize + "'"); + LOGD("%s", preData[0].c_str()); + for (auto &iter : preData) { + SchemaAttribute attributeOut; + EXPECT_EQ(SchemaUtils::ParseAndCheckSchemaAttribute(iter, attributeOut), -E_SCHEMA_PARSE_FAIL); + } +} + +/** + * @tc.name: CheckFieldName001 + * @tc.desc: Correctly identify field names + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBSchemalTest, CheckFieldName001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Enter the preset correct string array into CheckFieldName and check. + * @tc.expected: step1. Returns E_OK. + */ + std::vector preData = { + "_abc", + "_123abc", + "a_123_", + }; + string overSize(64, 'a'); + preData.push_back(overSize); + for (auto &iter : preData) { + EXPECT_EQ(SchemaUtils::CheckFieldName(iter), E_OK); + } +} + +/** + * @tc.name: CheckFieldName002 + * @tc.desc: Identify illegal field name + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBSchemalTest, CheckFieldName002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Enter the preset incorrect string array into CheckFieldName and check. + * @tc.expected: step1. Returns -E_SCHEMA_PARSE_FAIL. + */ + std::vector preData = { + "123abc", + "$.LONG", + "", + " abc", + "\tabc" + }; + string overSize(65, 'a'); + preData.push_back(overSize); + for (auto &iter : preData) { + EXPECT_EQ(SchemaUtils::CheckFieldName(iter), -E_SCHEMA_PARSE_FAIL); + } +} + +/** + * @tc.name: ParseAndCheckFieldPath001 + * @tc.desc: Correctly identify and parse shema index fields + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBSchemalTest, ParseAndCheckFieldPath001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Enter the array of preset correct strings into ParseAndCheckFieldPath and check result. + * @tc.expected: step1. Returns E_OK and Parse correctly. + */ + vector > > testPreData { + // test + // ans + {"$.abc.def.fg", + {"abc", "def", "fg"}}, + + {"$.abc._def.fg", + {"abc", "_def", "fg"}}, + + {"$._.__.___", + {"_", "__", "___"}}, + + {"$._.__1234.abc455545", + {"_", "__1234", "abc455545"}}, + + {" $.abc._def.fg", + {"abc", "_def", "fg"}}, + + {" $.a.a.a.a", + {"a", "a", "a", "a"}}, + + {"$.abc._def.fg ", + {"abc", "_def", "fg"}}, + + {" $.abc._def.fg ", + {"abc", "_def", "fg"}}, + + {"\t$.abc.def.fg ", + {"abc", "def", "fg"}}, + + {" $.abc.def.fg\r\t", + {"abc", "def", "fg"}}, + + {"\r$.abc.def.fg\t\r", + {"abc", "def", "fg"}}, + }; + + for (auto &iter : testPreData) { + FieldPath ans; + EXPECT_EQ(SchemaUtils::ParseAndCheckFieldPath(iter.first, ans), E_OK); + EXPECT_EQ(ans, iter.second); + } +} + +/** + * @tc.name: ParseAndCheckFieldPath002 + * @tc.desc: Correctly identify illegal shema index fields + * @tc.type: FUNC + * @tc.require: AR000DR9K3 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBSchemalTest, ParseAndCheckFieldPath002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Enter the array of preset illegal strings into ParseAndCheckFieldPath and check result. + * @tc.expected: step1. Returns -E_SCHEMA_PARSE_FAIL. + */ + vector testPreData { + "", + "\t", + "\r", + "\r\t", + " ", + "$", + "$.", + " . ", + "$$", + "$.$", + "$.a.b.c.d.e", + "$..abc", + "$.abc..def.fg", + "$abc.def.fg.", + "$.123", + "$.abc123%", + "$.abc.\0.fg", + "$.abc.fg.\0", + "\"$.abc.def.fg\"", + "$.\"abc\".def.fg", + "$.\"abc\n.def.fg", + }; + + for (auto &iter : testPreData) { + FieldPath ans; + EXPECT_EQ(SchemaUtils::ParseAndCheckFieldPath(iter, ans), -E_SCHEMA_PARSE_FAIL); + } +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp b/mock/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d4db3a0c24c971da0340953bc5a45e82a34d461 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp @@ -0,0 +1,964 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "distributeddb_tools_unit_test.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "db_common.h" +#include "db_constant.h" +#include "generic_single_ver_kv_entry.h" +#include "platform_specific.h" +#include "single_ver_data_packet.h" +#include "value_hash_calc.h" + +using namespace DistributedDB; + +namespace DistributedDBUnitTest { +namespace { + const std::string CREATE_LOCAL_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS local_data(" \ + "key BLOB PRIMARY KEY," \ + "value BLOB," \ + "timestamp INT," \ + "hash_key BLOB);"; + + const std::string CREATE_META_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS meta_data(" \ + "key BLOB PRIMARY KEY NOT NULL," \ + "value BLOB);"; + + const std::string CREATE_SYNC_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS sync_data(" \ + "key BLOB NOT NULL," \ + "value BLOB," \ + "timestamp INT NOT NULL," \ + "flag INT NOT NULL," \ + "device BLOB," \ + "ori_device BLOB," \ + "hash_key BLOB PRIMARY KEY NOT NULL," \ + "w_timestamp INT);"; + + const std::string CREATE_SYNC_TABLE_INDEX_SQL = + "CREATE INDEX IF NOT EXISTS key_index ON sync_data (key);"; + + const std::string CREATE_TABLE_SQL = + "CREATE TABLE IF NOT EXISTS version_data(key BLOB, value BLOB, oper_flag INTEGER, version INTEGER, " \ + "timestamp INTEGER, ori_timestamp INTEGER, hash_key BLOB, " \ + "PRIMARY key(hash_key, version));"; + + const std::string CREATE_SQL = + "CREATE TABLE IF NOT EXISTS data(key BLOB PRIMARY key, value BLOB);"; + + bool CompareEntry(const DistributedDB::Entry &a, const DistributedDB::Entry &b) + { + return (a.key < b.key); + } +} + +// OpenDbProperties.uri do not need +int DistributedDBToolsUnitTest::CreateMockSingleDb(DatabaseInfo &dbInfo, OpenDbProperties &properties) +{ + std::string identifier = dbInfo.userId + "-" + dbInfo.appId + "-" + dbInfo.storeId; + std::string hashIdentifier = DBCommon::TransferHashString(identifier); + std::string identifierName = DBCommon::TransferStringToHex(hashIdentifier); + + if (OS::GetRealPath(dbInfo.dir, properties.uri) != E_OK) { + LOGE("Failed to canonicalize the path."); + return -E_INVALID_ARGS; + } + + int errCode = DBCommon::CreateStoreDirectory(dbInfo.dir, identifierName, DBConstant::SINGLE_SUB_DIR, true); + if (errCode != E_OK) { + return errCode; + } + + properties.uri = dbInfo.dir + "/" + identifierName + "/" + + DBConstant::SINGLE_SUB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + if (properties.sqls.empty()) { + std::vector defaultCreateTableSqls = { + CREATE_LOCAL_TABLE_SQL, + CREATE_META_TABLE_SQL, + CREATE_SYNC_TABLE_SQL, + CREATE_SYNC_TABLE_INDEX_SQL + }; + properties.sqls = defaultCreateTableSqls; + } + + sqlite3 *db = nullptr; + errCode = SQLiteUtils::OpenDatabase(properties, db); + if (errCode != E_OK) { + return errCode; + } + errCode = SQLiteUtils::SetUserVer(properties, dbInfo.dbUserVersion); + if (errCode != E_OK) { + return errCode; + } + + (void)sqlite3_close_v2(db); + db = nullptr; + return errCode; +} + +static int CreatMockMultiDb(OpenDbProperties &properties, DatabaseInfo &dbInfo) +{ + sqlite3 *db = nullptr; + (void)SQLiteUtils::OpenDatabase(properties, db); + int errCode = SQLiteUtils::SetUserVer(properties, dbInfo.dbUserVersion); + (void)sqlite3_close_v2(db); + db = nullptr; + if (errCode != E_OK) { + return errCode; + } + return errCode; +} + +int DistributedDBToolsUnitTest::OpenMockMultiDb(DatabaseInfo &dbInfo, OpenDbProperties &properties) +{ + std::string identifier = dbInfo.userId + "-" + dbInfo.appId + "-" + dbInfo.storeId; + std::string hashIdentifier = DBCommon::TransferHashString(identifier); + std::string identifierName = DBCommon::TransferStringToHex(hashIdentifier); + + OpenDbProperties commitProperties = properties; + commitProperties.uri = dbInfo.dir + "/" + identifierName + "/" + DBConstant::MULTI_SUB_DIR + + "/commit_logs" + DBConstant::SQLITE_DB_EXTENSION; + + commitProperties.sqls = {CREATE_SQL}; + + OpenDbProperties kvStorageProperties = commitProperties; + kvStorageProperties.uri = dbInfo.dir + "/" + identifierName + "/" + + DBConstant::MULTI_SUB_DIR + "/value_storage" + DBConstant::SQLITE_DB_EXTENSION; + OpenDbProperties metaStorageProperties = commitProperties; + metaStorageProperties.uri = dbInfo.dir + "/" + identifierName + "/" + + DBConstant::MULTI_SUB_DIR + "/meta_storage" + DBConstant::SQLITE_DB_EXTENSION; + + // test code, Don't needpay too much attention to exception handling + int errCode = CreatMockMultiDb(properties, dbInfo); + if (errCode != E_OK) { + return errCode; + } + + errCode = CreatMockMultiDb(kvStorageProperties, dbInfo); + if (errCode != E_OK) { + return errCode; + } + + errCode = CreatMockMultiDb(metaStorageProperties, dbInfo); + if (errCode != E_OK) { + return errCode; + } + + return errCode; +} + +// OpenDbProperties.uri do not need +int DistributedDBToolsUnitTest::CreateMockMultiDb(DatabaseInfo &dbInfo, OpenDbProperties &properties) +{ + std::string identifier = dbInfo.userId + "-" + dbInfo.appId + "-" + dbInfo.storeId; + std::string hashIdentifier = DBCommon::TransferHashString(identifier); + std::string identifierName = DBCommon::TransferStringToHex(hashIdentifier); + + if (OS::GetRealPath(dbInfo.dir, properties.uri) != E_OK) { + LOGE("Failed to canonicalize the path."); + return -E_INVALID_ARGS; + } + + int errCode = DBCommon::CreateStoreDirectory(dbInfo.dir, identifierName, DBConstant::MULTI_SUB_DIR, true); + if (errCode != E_OK) { + return errCode; + } + + properties.uri = dbInfo.dir + "/" + identifierName + "/" + DBConstant::MULTI_SUB_DIR + + "/" + DBConstant::MULTI_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + + if (properties.sqls.empty()) { + properties.sqls = {CREATE_TABLE_SQL}; + } + + OpenMockMultiDb(dbInfo, properties); + + return errCode; +} + +int DistributedDBToolsUnitTest::GetResourceDir(std::string& dir) +{ + int errCode = GetCurrentDir(dir); + if (errCode != E_OK) { + return -E_INVALID_PATH; + } + + return E_OK; +} + +int DistributedDBToolsUnitTest::GetCurrentDir(std::string &dir) +{ + static const int maxFileLength = 1024; + dir = ""; + char buffer[maxFileLength] = {0}; + int length = readlink("/proc/self/exe", buffer, maxFileLength); + if (length < 0 || length >= maxFileLength) { + LOGE("read directory err length:%d", length); + return -E_LENGTH_ERROR; + } + LOGD("DIR = %s", buffer); + dir = buffer; + if (std::string::npos == dir.rfind("/") && std::string::npos == dir.rfind("\\")) { + LOGE("current patch format err"); + return -E_INVALID_PATH; + } + + if (dir.rfind("/") != std::string::npos) { + dir.erase(dir.rfind("/") + 1); + } + return E_OK; +} + +void DistributedDBToolsUnitTest::TestDirInit(std::string& dir) +{ + if (GetCurrentDir(dir) != E_OK) { + dir = "/"; + } + + dir.append("testDbDir"); + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + if (OS::MakeDBDirectory(dir) != 0) { + LOGI("MakeDirectory err!"); + dir = "/"; + return; + } + } else { + closedir(dirTmp); + } +} + +int DistributedDBToolsUnitTest::RemoveTestDbFiles(const std::string& dir) +{ + bool isExisted = OS::CheckPathExistence(dir); + if (!isExisted) { + return E_OK; + } + + int nFile = 0; + std::string dirName; + struct dirent *direntPtr = nullptr; + DIR *dirPtr = opendir(dir.c_str()); + if (dirPtr == nullptr) { + LOGE("opendir error!"); + return -E_INVALID_PATH; + } + while (true) { + direntPtr = readdir(dirPtr); + // condition to exit the loop + if (direntPtr == nullptr) { + break; + } + // only remove all *.db files + std::string str(direntPtr->d_name); + if (str == "." || str == "..") { + continue; + } + dirName.clear(); + dirName.append(dir).append("/").append(str); + if (direntPtr->d_type == DT_DIR) { + RemoveTestDbFiles(dirName); + rmdir(dirName.c_str()); + } else if (remove(dirName.c_str()) != 0) { + LOGI("remove file: %s failed!", dirName.c_str()); + continue; + } + nFile++; + } + closedir(dirPtr); + LOGI("Total %d test db files are removed!", nFile); + return 0; +} + +void DistributedDBToolsUnitTest::KvStoreDelegateCallback( + DBStatus statusSrc, KvStoreDelegate *kvStoreSrc, DBStatus &statusDst, KvStoreDelegate *&kvStoreDst) +{ + statusDst = statusSrc; + kvStoreDst = kvStoreSrc; +} + +void DistributedDBToolsUnitTest::KvStoreNbDelegateCallback( + DBStatus statusSrc, KvStoreNbDelegate* kvStoreSrc, DBStatus &statusDst, KvStoreNbDelegate *&kvStoreDst) +{ + statusDst = statusSrc; + kvStoreDst = kvStoreSrc; +} +void DistributedDBToolsUnitTest::SnapshotDelegateCallback( + DBStatus statusSrc, KvStoreSnapshotDelegate* snapshot, DBStatus &statusDst, KvStoreSnapshotDelegate *&snapshotDst) +{ + statusDst = statusSrc; + snapshotDst = snapshot; +} + +void DistributedDBToolsUnitTest::ValueCallback( + DBStatus statusSrc, const Value &valueSrc, DBStatus &statusDst, Value &valueDst) +{ + statusDst = statusSrc; + valueDst = valueSrc; +} + +void DistributedDBToolsUnitTest::EntryVectorCallback(DBStatus statusSrc, const std::vector &entrySrc, + DBStatus &statusDst, unsigned long &matchSize, std::vector &entryDst) +{ + statusDst = statusSrc; + matchSize = static_cast(entrySrc.size()); + entryDst = entrySrc; +} + +// size need bigger than prefixkey length +std::vector DistributedDBToolsUnitTest::GetRandPrefixKey(const std::vector &prefixKey, uint32_t size) +{ + std::vector value; + if (size <= prefixKey.size()) { + return value; + } + DistributedDBToolsUnitTest::GetRandomKeyValue(value, size - prefixKey.size()); + std::vector res(prefixKey); + res.insert(res.end(), value.begin(), value.end()); + return res; +} + +void DistributedDBToolsUnitTest::GetRandomKeyValue(std::vector &value, uint32_t defaultSize) +{ + uint32_t randSize = 0; + if (defaultSize == 0) { + uint8_t simSize = 0; + RAND_bytes(&simSize, 1); + randSize = (simSize == 0) ? 1 : simSize; + } else { + randSize = defaultSize; + } + + value.resize(randSize); + RAND_bytes(value.data(), randSize); +} + +bool DistributedDBToolsUnitTest::IsValueEqual(const DistributedDB::Value &read, const DistributedDB::Value &origin) +{ + if (read != origin) { + DBCommon::PrintHexVector(read, __LINE__, "read"); + DBCommon::PrintHexVector(origin, __LINE__, "origin"); + return false; + } + + return true; +} + +bool DistributedDBToolsUnitTest::IsEntryEqual(const DistributedDB::Entry &entryOrg, + const DistributedDB::Entry &entryRet) +{ + if (entryOrg.key != entryRet.key) { + LOGD("key not equal, entryOrg key size is [%zu], entryRet key size is [%zu]", entryOrg.key.size(), + entryRet.key.size()); + return false; + } + + if (entryOrg.value != entryRet.value) { + LOGD("value not equal, entryOrg value size is [%zu], entryRet value size is [%zu]", entryOrg.value.size(), + entryRet.value.size()); + return false; + } + + return true; +} + +bool DistributedDBToolsUnitTest::IsEntriesEqual(const std::vector &entriesOrg, + const std::vector &entriesRet, bool needSort) +{ + LOGD("entriesOrg size is [%zu], entriesRet size is [%zu]", entriesOrg.size(), + entriesRet.size()); + + if (entriesOrg.size() != entriesRet.size()) { + return false; + } + std::vector entries1 = entriesOrg; + std::vector entries2 = entriesRet; + + if (needSort) { + sort(entries1.begin(), entries1.end(), CompareEntry); + sort(entries2.begin(), entries2.end(), CompareEntry); + } + + for (size_t i = 0; i < entries1.size(); i++) { + if (entries1[i].key != entries2[i].key) { + LOGE("IsEntriesEqual failed, key of index[%zu] not match", i); + return false; + } + if (entries1[i].value != entries2[i].value) { + LOGE("IsEntriesEqual failed, value of index[%zu] not match", i); + return false; + } + } + + return true; +} + +bool DistributedDBToolsUnitTest::CheckObserverResult(const std::vector &orgEntries, + const std::list &resultLst) +{ + LOGD("orgEntries.size() is [%zu], resultLst.size() is [%zu]", orgEntries.size(), + resultLst.size()); + + if (orgEntries.size() != resultLst.size()) { + return false; + } + + int index = 0; + for (auto &entry : resultLst) { + if (entry.key != orgEntries[index].key) { + LOGE("CheckObserverResult failed, key of index[%d] not match", index); + return false; + } + if (entry.value != orgEntries[index].value) { + LOGE("CheckObserverResult failed, value of index[%d] not match", index); + return false; + } + index++; + } + + return true; +} + +bool DistributedDBToolsUnitTest::IsEntryExist(const DistributedDB::Entry &entry, + const std::vector &entries) +{ + std::set> sets; + for (const auto &iter : entries) { + sets.insert(iter.key); + } + + if (entries.size() != sets.size()) { + return false; + } + sets.clear(); + bool isFound = false; + for (const auto &iter : entries) { + if (entry.key == iter.key) { + if (entry.value == iter.value) { + isFound = true; + } + break; + } + } + return isFound; +} + +bool DistributedDBToolsUnitTest::IsItemValueExist(const DistributedDB::DataItem &item, + const std::vector &items) +{ + std::set sets; + for (const auto &iter : items) { + sets.insert(iter.key); + } + + if (items.size() != sets.size()) { + return false; + } + sets.clear(); + bool isFound = false; + for (const auto &iter : items) { + if (item.key == iter.key) { + if (item.value == iter.value) { + isFound = true; + } + break; + } + } + return isFound; +} + +bool DistributedDBToolsUnitTest::IsKvEntryExist(const DistributedDB::Entry &entry, + const std::vector &entries) +{ + std::set> sets; + for (const auto &iter : entries) { + sets.insert(iter.key); + } + + if (entries.size() != sets.size()) { + return false; + } + sets.clear(); + bool isFound = false; + for (const auto &iter : entries) { + if (entry.key == iter.key) { + if (entry.value == iter.value) { + isFound = true; + } + break; + } + } + + return isFound; +} + +int DistributedDBToolsUnitTest::ModifyDatabaseFile(const std::string &fileDir, uint64_t modifyPos, + uint32_t modifyCnt, uint32_t value) +{ + LOGI("Modify database file:%s", fileDir.c_str()); + std::fstream dataFile(fileDir, std::fstream::binary | std::fstream::out | std::fstream::in); + if (!dataFile.is_open()) { + LOGD("Open the database file failed"); + return -E_UNEXPECTED_DATA; + } + + if (!dataFile.seekg(0, std::fstream::end)) { + return -E_UNEXPECTED_DATA; + } + + uint64_t fileSize; + std::ios::pos_type pos = dataFile.tellg(); + if (pos < 0) { + return -E_UNEXPECTED_DATA; + } else { + fileSize = static_cast(pos); + if (fileSize < 1024) { // the least page size is 1024 bytes. + LOGE("Invalid database file:%" PRIu64 ".", fileSize); + return -E_UNEXPECTED_DATA; + } + } + + if (fileSize <= modifyPos) { + return E_OK; + } + + if (!dataFile.seekp(modifyPos)) { + return -E_UNEXPECTED_DATA; + } + for (uint32_t i = 0; i < modifyCnt; i++) { + if (!dataFile.write(reinterpret_cast(&value), sizeof(uint32_t))) { + return -E_UNEXPECTED_DATA; + } + } + + dataFile.flush(); + return E_OK; +} + +int DistributedDBToolsUnitTest::GetSyncDataTest(const SyncInputArg &syncInputArg, SQLiteSingleVerNaturalStore *store, + std::vector &dataItems, ContinueToken &continueStmtToken) +{ + std::vector entries; + DataSizeSpecInfo syncDataSizeInfo = {syncInputArg.blockSize_, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + int errCode = store->GetSyncData(syncInputArg.begin_, syncInputArg.end_, entries, + continueStmtToken, syncDataSizeInfo); + + ConvertSingleVerEntryToItems(entries, dataItems); + return errCode; +} + +int DistributedDBToolsUnitTest::GetSyncDataNextTest(SQLiteSingleVerNaturalStore *store, uint32_t blockSize, + std::vector &dataItems, ContinueToken &continueStmtToken) +{ + std::vector entries; + DataSizeSpecInfo syncDataSizeInfo = {blockSize, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + int errCode = store->GetSyncDataNext(entries, continueStmtToken, syncDataSizeInfo); + + ConvertSingleVerEntryToItems(entries, dataItems); + return errCode; +} + +int DistributedDBToolsUnitTest::PutSyncDataTest(SQLiteSingleVerNaturalStore *store, + const std::vector &dataItems, const std::string &deviceName) +{ + QueryObject query(Query::Select()); + return PutSyncDataTest(store, dataItems, deviceName, query); +} + +int DistributedDBToolsUnitTest::PutSyncDataTest(SQLiteSingleVerNaturalStore *store, + const std::vector &dataItems, const std::string &deviceName, const QueryObject &query) +{ + std::vector entries; + std::vector items = dataItems; + for (auto &item : items) { + auto *entry = new (std::nothrow) GenericSingleVerKvEntry(); + if (entry == nullptr) { + ReleaseSingleVerEntry(entries); + return -E_OUT_OF_MEMORY; + } + entry->SetEntryData(std::move(item)); + entry->SetWriteTimestamp(entry->GetTimestamp()); + entries.push_back(entry); + } + + int errCode = store->PutSyncDataWithQuery(query, entries, deviceName); + ReleaseSingleVerEntry(entries); + return errCode; +} + +int DistributedDBToolsUnitTest::ConvertItemsToSingleVerEntry(const std::vector &dataItems, + std::vector &entries) +{ + std::vector items = dataItems; + for (auto &item : items) { + GenericSingleVerKvEntry *entry = new (std::nothrow) GenericSingleVerKvEntry(); + if (entry == nullptr) { + ReleaseSingleVerEntry(entries); + return -E_OUT_OF_MEMORY; + } + entry->SetEntryData(std::move(item)); + entries.push_back(entry); + } + return E_OK; +} + +void DistributedDBToolsUnitTest::ConvertSingleVerEntryToItems(std::vector &entries, + std::vector &dataItems) +{ + for (auto &itemEntry : entries) { + GenericSingleVerKvEntry *entry = reinterpret_cast(itemEntry); + if (entry != nullptr) { + DataItem item; + item.origDev = entry->GetOrigDevice(); + item.flag = entry->GetFlag(); + item.timestamp = entry->GetTimestamp(); + entry->GetKey(item.key); + entry->GetValue(item.value); + dataItems.push_back(item); + // clear vector entry + delete itemEntry; + itemEntry = nullptr; + } + } + entries.clear(); +} + +void DistributedDBToolsUnitTest::ReleaseSingleVerEntry(std::vector &entries) +{ + for (auto &item : entries) { + delete item; + item = nullptr; + } + entries.clear(); +} + +void DistributedDBToolsUnitTest::CalcHash(const std::vector &value, std::vector &hashValue) +{ + ValueHashCalc hashCalc; + hashCalc.Initialize(); + hashCalc.Update(value); + hashCalc.GetResult(hashValue); +} + +KvStoreObserverUnitTest::KvStoreObserverUnitTest() : callCount_(0), isCleared_(false) +{} + +void KvStoreObserverUnitTest::OnChange(const KvStoreChangedData& data) +{ + callCount_++; + inserted_ = data.GetEntriesInserted(); + updated_ = data.GetEntriesUpdated(); + deleted_ = data.GetEntriesDeleted(); + isCleared_ = data.IsCleared(); + LOGD("Onchangedata :%zu -- %zu -- %zu -- %d", inserted_.size(), updated_.size(), deleted_.size(), isCleared_); + LOGD("Onchange() called success!"); +} + +void KvStoreObserverUnitTest::ResetToZero() +{ + callCount_ = 0; + isCleared_ = false; + inserted_.clear(); + updated_.clear(); + deleted_.clear(); +} + +unsigned long KvStoreObserverUnitTest::GetCallCount() const +{ + return callCount_; +} + +const std::list &KvStoreObserverUnitTest::GetEntriesInserted() const +{ + return inserted_; +} + +const std::list &KvStoreObserverUnitTest::GetEntriesUpdated() const +{ + return updated_; +} + +const std::list &KvStoreObserverUnitTest::GetEntriesDeleted() const +{ + return deleted_; +} + +bool KvStoreObserverUnitTest::IsCleared() const +{ + return isCleared_; +} + +RelationalStoreObserverUnitTest::RelationalStoreObserverUnitTest() : callCount_(0) +{ +} + +unsigned long RelationalStoreObserverUnitTest::GetCallCount() const +{ + return callCount_; +} + +void RelationalStoreObserverUnitTest::OnChange(const StoreChangedData& data) +{ + callCount_++; + changeDevice_ = data.GetDataChangeDevice(); + data.GetStoreProperty(storeProperty_); + LOGD("Onchangedata : %s", changeDevice_.c_str()); + LOGD("Onchange() called success!"); +} + +void RelationalStoreObserverUnitTest::ResetToZero() +{ + callCount_ = 0; + changeDevice_.clear(); + storeProperty_ = {}; +} + +const std::string RelationalStoreObserverUnitTest::GetDataChangeDevice() const +{ + return changeDevice_; +} + +DistributedDB::StoreProperty RelationalStoreObserverUnitTest::GetStoreProperty() const +{ + return storeProperty_; +} + +DBStatus DistributedDBToolsUnitTest::SyncTest(KvStoreNbDelegate* delegate, + const std::vector& devices, SyncMode mode, + std::map& statuses, const Query &query) +{ + statuses.clear(); + DBStatus callStatus = delegate->Sync(devices, mode, + [&statuses, this](const std::map& statusMap) { + statuses = statusMap; + std::unique_lock innerlock(this->syncLock_); + this->syncCondVar_.notify_one(); + }, query, false); + + std::unique_lock lock(syncLock_); + syncCondVar_.wait(lock, [callStatus, &statuses]() { + if (callStatus != OK) { + return true; + } + return !statuses.empty(); + }); + return callStatus; +} + +DBStatus DistributedDBToolsUnitTest::SyncTest(KvStoreNbDelegate* delegate, + const std::vector& devices, SyncMode mode, + std::map& statuses, bool wait) +{ + statuses.clear(); + DBStatus callStatus = delegate->Sync(devices, mode, + [&statuses, this](const std::map& statusMap) { + statuses = statusMap; + std::unique_lock innerlock(this->syncLock_); + this->syncCondVar_.notify_one(); + }, wait); + if (!wait) { + std::unique_lock lock(syncLock_); + syncCondVar_.wait(lock, [callStatus, &statuses]() { + if (callStatus != OK) { + return true; + } + if (statuses.size() != 0) { + return true; + } + return false; + }); + } + return callStatus; +} + +void KvStoreCorruptInfo::CorruptCallBack(const std::string &appId, const std::string &userId, + const std::string &storeId) +{ + DatabaseInfo databaseInfo; + databaseInfo.appId = appId; + databaseInfo.userId = userId; + databaseInfo.storeId = storeId; + LOGD("appId :%s, userId:%s, storeId:%s", appId.c_str(), userId.c_str(), storeId.c_str()); + databaseInfoVect_.push_back(databaseInfo); +} + +size_t KvStoreCorruptInfo::GetDatabaseInfoSize() const +{ + return databaseInfoVect_.size(); +} + +bool KvStoreCorruptInfo::IsDataBaseCorrupted(const std::string &appId, const std::string &userId, + const std::string &storeId) const +{ + for (const auto &item : databaseInfoVect_) { + if (item.appId == appId && + item.userId == userId && + item.storeId == storeId) { + return true; + } + } + return false; +} + +void KvStoreCorruptInfo::Reset() +{ + databaseInfoVect_.clear(); +} + +int DistributedDBToolsUnitTest::GetRandInt(const int randMin, const int randMax) +{ + std::random_device randDev; + std::mt19937 genRand(randDev()); + std::uniform_int_distribution disRand(randMin, randMax); + return disRand(genRand); +} + +int64_t DistributedDBToolsUnitTest::GetRandInt64(const int64_t randMin, const int64_t randMax) +{ + std::random_device randDev; + std::mt19937_64 genRand(randDev()); + std::uniform_int_distribution disRand(randMin, randMax); + return disRand(genRand); +} + +void DistributedDBToolsUnitTest::PrintTestCaseInfo() +{ + testing::UnitTest *test = testing::UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const testing::TestInfo *testInfo = test->current_test_info(); + ASSERT_NE(testInfo, nullptr); + LOGI("Start unit test: %s.%s", testInfo->test_case_name(), testInfo->name()); +} + +int DistributedDBToolsUnitTest::BuildMessage(const DataSyncMessageInfo &messageInfo, + DistributedDB::Message *&message) +{ + auto packet = new (std::nothrow) DataRequestPacket; + if (packet == nullptr) { + return -E_OUT_OF_MEMORY; + } + message = new (std::nothrow) Message(messageInfo.messageId_); + if (message == nullptr) { + delete packet; + packet = nullptr; + return -E_OUT_OF_MEMORY; + } + packet->SetBasicInfo(messageInfo.sendCode_, messageInfo.version_, messageInfo.mode_); + packet->SetWaterMark(messageInfo.localMark_, messageInfo.peerMark_, messageInfo.deleteMark_); + std::vector reserved {messageInfo.packetId_}; + packet->SetReserved(reserved); + message->SetMessageType(messageInfo.messageType_); + message->SetSessionId(messageInfo.sessionId_); + message->SetSequenceId(messageInfo.sequenceId_); + message->SetExternalObject(packet); + return E_OK; +} + +sqlite3 *RelationalTestUtils::CreateDataBase(const std::string &dbUri) +{ + sqlite3 *db = nullptr; + if (int r = sqlite3_open_v2(dbUri.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr) != SQLITE_OK) { + LOGE("Open database [%s] failed. %d", dbUri.c_str(), r); + if (db != nullptr) { + (void)sqlite3_close_v2(db); + db = nullptr; + } + } + return db; +} + +int RelationalTestUtils::ExecSql(sqlite3 *db, const std::string &sql) +{ + if (db == nullptr || sql.empty()) { + return -E_INVALID_ARGS; + } + char *errMsg = nullptr; + int errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE("Execute sql failed. %d err: %s", errCode, errMsg); + } + sqlite3_free(errMsg); + return errCode; +} + +void RelationalTestUtils::CreateDeviceTable(sqlite3 *db, const std::string &table, const std::string &device) +{ + ASSERT_NE(db, nullptr); + std::string deviceTable = DBCommon::GetDistributedTableName(device, table); + TableInfo baseTbl; + ASSERT_EQ(SQLiteUtils::AnalysisSchema(db, table, baseTbl), E_OK); + EXPECT_EQ(SQLiteUtils::CreateSameStuTable(db, baseTbl, deviceTable), E_OK); + EXPECT_EQ(SQLiteUtils::CloneIndexes(db, table, deviceTable), E_OK); +} + +int RelationalTestUtils::CheckSqlResult(sqlite3 *db, const std::string &sql, bool &result) +{ + if (db == nullptr || sql.empty()) { + return -E_INVALID_ARGS; + } + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(stmt); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + result = true; + errCode = E_OK; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + result = false; + errCode = E_OK; + } +END: + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; +} + +int RelationalTestUtils::CheckTableRecords(sqlite3 *db, const std::string &table) +{ + if (db == nullptr || table.empty()) { + return -E_INVALID_ARGS; + } + int count = -1; + std::string sql = "select count(1) from " + table + ";"; + + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + goto END; + } + + errCode = SQLiteUtils::StepWithRetry(stmt); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + count = sqlite3_column_int(stmt, 0); + } +END: + SQLiteUtils::ResetStatement(stmt, true, errCode); + return count; +} +} // namespace DistributedDBUnitTest diff --git a/mock/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.h b/mock/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.h new file mode 100644 index 0000000000000000000000000000000000000000..02686a85ece08346c375c12aee5964200e0b1d27 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.h @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_TOOLS_UNIT_TEST_H +#define DISTRIBUTEDDB_TOOLS_UNIT_TEST_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "db_types.h" +#include "kv_store_changed_data.h" +#include "kv_store_delegate_impl.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_observer.h" +#include "kv_store_snapshot_delegate_impl.h" +#include "log_print.h" +#include "message.h" +#include "query.h" +#include "relational_store_sqlite_ext.h" +#include "store_observer.h" +#include "store_changed_data.h" +#include "single_ver_kv_entry.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_utils.h" +#include "sync_types.h" +#include "store_types.h" +namespace DistributedDBUnitTest { +struct DatabaseInfo { + std::string appId{}; + std::string userId{}; + std::string storeId{}; + std::string dir{}; + int dbUserVersion = 0; +}; + +struct SyncInputArg { + uint64_t begin_{}; + uint64_t end_{}; + uint32_t blockSize_{}; + SyncInputArg(uint64_t begin, uint64_t end, uint32_t blockSize) + : begin_(begin), end_(end), blockSize_(blockSize) + {} +}; + +struct DataSyncMessageInfo { + int messageId_ = DistributedDB::INVALID_MESSAGE_ID; + uint16_t messageType_ = DistributedDB::TYPE_INVALID; + uint32_t sequenceId_ = 0; + uint32_t sessionId_ = 0; + int sendCode_ = DistributedDB::E_OK; + uint32_t version_ = 0; + int32_t mode_ = DistributedDB::PUSH; + DistributedDB::WaterMark localMark_ = 0; + DistributedDB::WaterMark peerMark_ = 0; + DistributedDB::WaterMark deleteMark_ = 0; + uint64_t packetId_ = 0; +}; + +class DistributedDBToolsUnitTest final { +public: + DistributedDBToolsUnitTest() {} + ~DistributedDBToolsUnitTest() {} + + DistributedDBToolsUnitTest(const DistributedDBToolsUnitTest&) = delete; + DistributedDBToolsUnitTest& operator=(const DistributedDBToolsUnitTest&) = delete; + DistributedDBToolsUnitTest(DistributedDBToolsUnitTest&&) = delete; + DistributedDBToolsUnitTest& operator=(DistributedDBToolsUnitTest&&) = delete; + + // compare whether two vectors are equal. + template + static bool CompareVector(std::vector& vec1, std::vector& vec2) + { + if (vec1.size() != vec2.size()) { + return false; + } + for (size_t i = 0; i < vec2.size(); i++) { + if (vec1[i] != vec2[i]) { + return false; + } + } + return true; + } + + // compare whether two vectors are equal. + template + static bool CompareVectorN(std::vector& vec1, std::vector& vec2, uint32_t n) + { + if (n > std::min(vec1.size(), vec2.size())) { + return false; + } + for (uint32_t i = 0; i < n; i++) { + if (vec1[i] != vec2[i]) { + return false; + } + } + return true; + } + // init the test directory of dir. + static void TestDirInit(std::string&); + + // remove the test db files in the test directory of dir. + static int RemoveTestDbFiles(const std::string&); + + // callback function for get a KvStoreDelegate pointer. + static void KvStoreDelegateCallback(DistributedDB::DBStatus, DistributedDB::KvStoreDelegate*, + DistributedDB::DBStatus &, DistributedDB::KvStoreDelegate *&); + + // callback function for get a KvStoreDelegate pointer. + static void KvStoreNbDelegateCallback(DistributedDB::DBStatus, DistributedDB::KvStoreNbDelegate*, + DistributedDB::DBStatus &, DistributedDB::KvStoreNbDelegate *&); + + // callback function for get a KvStoreSnapshotDelegate pointer. + static void SnapshotDelegateCallback(DistributedDB::DBStatus, DistributedDB::KvStoreSnapshotDelegate*, + DistributedDB::DBStatus &, DistributedDB::KvStoreSnapshotDelegate *&); + + // callback function for get the value. + static void ValueCallback( + DistributedDB::DBStatus, const DistributedDB::Value &, DistributedDB::DBStatus &, DistributedDB::Value &); + + // callback function for get an entry vector. + static void EntryVectorCallback(DistributedDB::DBStatus, const std::vector &, + DistributedDB::DBStatus &, unsigned long &, std::vector &); + + // sync test helper + DistributedDB::DBStatus SyncTest(DistributedDB::KvStoreNbDelegate* delegate, + const std::vector& devices, DistributedDB::SyncMode mode, + std::map& statuses, bool wait = false); + + // sync test helper + DistributedDB::DBStatus SyncTest(DistributedDB::KvStoreNbDelegate* delegate, + const std::vector& devices, DistributedDB::SyncMode mode, + std::map& statuses, const DistributedDB::Query &query); + + static void GetRandomKeyValue(std::vector &value, uint32_t defaultSize = 0); + + static bool IsValueEqual(const DistributedDB::Value &read, const DistributedDB::Value &origin); + + static bool IsEntryEqual(const DistributedDB::Entry &entryOrg, const DistributedDB::Entry &entryRet); + + static bool IsEntriesEqual(const std::vector &entriesOrg, + const std::vector &entriesRet, bool needSort = false); + + static bool CheckObserverResult(const std::vector &orgEntries, + const std::list &resultLst); + + static bool IsItemValueExist(const DistributedDB::DataItem &item, + const std::vector &items); + + static bool IsEntryExist(const DistributedDB::Entry &entry, + const std::vector &entries); + + static bool IsKvEntryExist(const DistributedDB::Entry &entry, + const std::vector &entries); + + static void CalcHash(const std::vector &value, std::vector &hashValue); + + static int CreateMockSingleDb(DatabaseInfo &dbId, DistributedDB::OpenDbProperties &properties); + + static int CreateMockMultiDb(DatabaseInfo &dbInfo, DistributedDB::OpenDbProperties &properties); + + static int ModifyDatabaseFile(const std::string &fileDir, uint64_t modifyPos = 0, + uint32_t modifyCnt = 256, uint32_t value = 0x1F1F1F1F); + + static int GetSyncDataTest(const SyncInputArg &syncInputArg, DistributedDB::SQLiteSingleVerNaturalStore *store, + std::vector &dataItems, DistributedDB::ContinueToken &continueStmtToken); + + static int GetSyncDataNextTest(DistributedDB::SQLiteSingleVerNaturalStore *store, uint32_t blockSize, + std::vector &dataItems, DistributedDB::ContinueToken &continueStmtToken); + + static int PutSyncDataTest(DistributedDB::SQLiteSingleVerNaturalStore *store, + const std::vector &dataItems, const std::string &deviceName); + + static int PutSyncDataTest(DistributedDB::SQLiteSingleVerNaturalStore *store, + const std::vector &dataItems, const std::string &deviceName, + const DistributedDB::QueryObject &query); + + static int ConvertItemsToSingleVerEntry(const std::vector &dataItems, + std::vector &entries); + + static void ConvertSingleVerEntryToItems(std::vector &entries, + std::vector &dataItems); + + static void ReleaseSingleVerEntry(std::vector &entries); + + static std::vector GetRandPrefixKey(const std::vector &prefixKey, uint32_t size); + + static int GetCurrentDir(std::string& dir); + + static int GetResourceDir(std::string& dir); + + static int GetRandInt(const int randMin, const int randMax); + static int64_t GetRandInt64(const int64_t randMin, const int64_t randMax); + + static void PrintTestCaseInfo(); + + static int BuildMessage(const DataSyncMessageInfo &messageInfo, DistributedDB::Message *&message); + +private: + static int OpenMockMultiDb(DatabaseInfo &dbInfo, DistributedDB::OpenDbProperties &properties); + + std::mutex syncLock_{}; + std::condition_variable syncCondVar_{}; +}; + +class KvStoreObserverUnitTest : public DistributedDB::KvStoreObserver { +public: + KvStoreObserverUnitTest(); + ~KvStoreObserverUnitTest() {} + + KvStoreObserverUnitTest(const KvStoreObserverUnitTest&) = delete; + KvStoreObserverUnitTest& operator=(const KvStoreObserverUnitTest&) = delete; + KvStoreObserverUnitTest(KvStoreObserverUnitTest&&) = delete; + KvStoreObserverUnitTest& operator=(KvStoreObserverUnitTest&&) = delete; + + // callback function will be called when the db data is changed. + void OnChange(const DistributedDB::KvStoreChangedData&); + + // reset the callCount_ to zero. + void ResetToZero(); + + // get callback results. + unsigned long GetCallCount() const; + const std::list &GetEntriesInserted() const; + const std::list &GetEntriesUpdated() const; + const std::list &GetEntriesDeleted() const; + bool IsCleared() const; +private: + unsigned long callCount_; + bool isCleared_; + std::list inserted_; + std::list updated_; + std::list deleted_; +}; + +class RelationalStoreObserverUnitTest : public DistributedDB::StoreObserver { +public: + RelationalStoreObserverUnitTest(); + ~RelationalStoreObserverUnitTest() {} + + RelationalStoreObserverUnitTest(const RelationalStoreObserverUnitTest&) = delete; + RelationalStoreObserverUnitTest& operator=(const RelationalStoreObserverUnitTest&) = delete; + RelationalStoreObserverUnitTest(RelationalStoreObserverUnitTest&&) = delete; + RelationalStoreObserverUnitTest& operator=(RelationalStoreObserverUnitTest&&) = delete; + + // callback function will be called when the db data is changed. + void OnChange(const DistributedDB::StoreChangedData &data); + + // reset the callCount_ to zero. + void ResetToZero(); + + // get callback results. + unsigned long GetCallCount() const; + const std::string GetDataChangeDevice() const; + DistributedDB::StoreProperty GetStoreProperty() const; +private: + unsigned long callCount_; + std::string changeDevice_; + DistributedDB::StoreProperty storeProperty_; +}; + +class KvStoreCorruptInfo { +public: + KvStoreCorruptInfo() {} + ~KvStoreCorruptInfo() {} + + KvStoreCorruptInfo(const KvStoreCorruptInfo&) = delete; + KvStoreCorruptInfo& operator=(const KvStoreCorruptInfo&) = delete; + KvStoreCorruptInfo(KvStoreCorruptInfo&&) = delete; + KvStoreCorruptInfo& operator=(KvStoreCorruptInfo&&) = delete; + + // callback function will be called when the db data is changed. + void CorruptCallBack(const std::string &appId, const std::string &userId, const std::string &storeId); + size_t GetDatabaseInfoSize() const; + bool IsDataBaseCorrupted(const std::string &appId, const std::string &userId, const std::string &storeId) const; + void Reset(); +private: + std::vector databaseInfoVect_; +}; + +class RelationalTestUtils { +public: + static sqlite3 *CreateDataBase(const std::string &dbUri); + static int ExecSql(sqlite3 *db, const std::string &sql); + static void CreateDeviceTable(sqlite3 *db, const std::string &table, const std::string &device); + static int CheckSqlResult(sqlite3 *db, const std::string &sql, bool &result); + static int CheckTableRecords(sqlite3 *db, const std::string &table); +}; +} // namespace DistributedDBUnitTest + +#endif // DISTRIBUTEDDB_TOOLS_UNIT_TEST_H diff --git a/mock/distributeddb/test/unittest/common/common/evloop_timer_unit_test.cpp b/mock/distributeddb/test/unittest/common/common/evloop_timer_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9e48b9d5155b775086ae251c1f5dc535dbaf0a5 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/evloop_timer_unit_test.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "evloop/include/ievent.h" +#include "evloop/include/ievent_loop.h" +#include "log_print.h" +#include "platform_specific.h" + +using namespace testing::ext; +using namespace DistributedDB; + +namespace { + IEventLoop *g_loop = nullptr; + constexpr int MAX_RETRY_TIMES = 1000; + constexpr int RETRY_TIMES_5 = 5; + constexpr EventTime TIME_INACCURACY = 100LL; + constexpr EventTime TIME_PIECE_1 = 1LL; + constexpr EventTime TIME_PIECE_10 = 10LL; + constexpr EventTime TIME_PIECE_50 = 50LL; + constexpr EventTime TIME_PIECE_100 = 100LL; + constexpr EventTime TIME_PIECE_1000 = 1000LL; + constexpr EventTime TIME_PIECE_10000 = 10000LL; +} + +class TimerTester { +public: + static EventTime GetCurrentTime(); +}; + +EventTime TimerTester::GetCurrentTime() +{ + uint64_t now; + int errCode = OS::GetCurrentSysTimeInMicrosecond(now); + if (errCode != E_OK) { + LOGE("Get current time failed."); + return 0; + } + return now / 1000; // 1 ms equals to 1000 us +} + +class DistributedDBEventLoopTimerTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBEventLoopTimerTest::SetUpTestCase(void) {} + +void DistributedDBEventLoopTimerTest::TearDownTestCase(void) {} + +void DistributedDBEventLoopTimerTest::SetUp(void) +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: Create a loop object. + */ + if (g_loop == nullptr) { + int errCode = E_OK; + g_loop = IEventLoop::CreateEventLoop(errCode); + if (g_loop == nullptr) { + LOGE("Prepare loop in SetUp() failed."); + } + } +} + +void DistributedDBEventLoopTimerTest::TearDown(void) +{ + /** + * @tc.teardown: Destroy the loop object. + */ + if (g_loop != nullptr) { + g_loop->KillAndDecObjRef(g_loop); + g_loop = nullptr; + } +} + +/** + * @tc.name: EventLoopTimerTest001 + * @tc.desc: Create and destroy the event loop object. + * @tc.type: FUNC + * @tc.require: AR000CKRTB AR000CQE0C + * @tc.author: fangyi + */ +HWTEST_F(DistributedDBEventLoopTimerTest, EventLoopTimerTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create a loop. + * @tc.expected: step1. create successfully. + */ + int errCode = E_OK; + IEventLoop *loop = IEventLoop::CreateEventLoop(errCode); + ASSERT_EQ(loop != nullptr, true); + + /** + * @tc.steps: step2. destroy the loop. + * @tc.expected: step2. destroy successfully. + */ + bool finalized = false; + loop->OnLastRef([&finalized]() { finalized = true; }); + loop->DecObjRef(loop); + loop = nullptr; + EXPECT_EQ(finalized, true); +} + +/** + * @tc.name: EventLoopTimerTest002 + * @tc.desc: Start and stop the loop + * @tc.type: FUNC + * @tc.require: AR000CKRTB AR000CQE0C + * @tc.author: fangyi + */ +HWTEST_F(DistributedDBEventLoopTimerTest, EventLoopTimerTest002, TestSize.Level1) +{ + // ready data + ASSERT_EQ(g_loop != nullptr, true); + + /** + * @tc.steps: step1. create a loop. + * @tc.expected: step1. create successfully. + */ + std::atomic running(false); + EventTime delta = 0; + std::thread loopThread([&running, &delta]() { + running = true; + EventTime start = TimerTester::GetCurrentTime(); + g_loop->Run(); + EventTime end = TimerTester::GetCurrentTime(); + delta = end - start; + }); + while (!running) { + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PIECE_1)); + } + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PIECE_100)); + g_loop->KillObj(); + loopThread.join(); + EXPECT_EQ(delta > TIME_PIECE_50, true); +} + +/** + * @tc.name: EventLoopTimerTest003 + * @tc.desc: Create and destroy a timer object. + * @tc.type: FUNC + * @tc.require: AR000CKRTB AR000CQE0C + * @tc.author: fangyi + */ +HWTEST_F(DistributedDBEventLoopTimerTest, EventLoopTimerTest003, TestSize.Level0) +{ + /** + * @tc.steps: step1. create event(timer) object. + * @tc.expected: step1. create successfully. + */ + int errCode = E_OK; + IEvent *timer = IEvent::CreateEvent(TIME_PIECE_1, errCode); + ASSERT_EQ(timer != nullptr, true); + + /** + * @tc.steps: step2. destroy the event object. + * @tc.expected: step2. destroy successfully. + */ + bool finalized = false; + errCode = timer->SetAction([](EventsMask revents) -> int { + return E_OK; + }, [&finalized]() { + finalized = true; + }); + EXPECT_EQ(errCode, E_OK); + timer->KillAndDecObjRef(timer); + timer = nullptr; + EXPECT_EQ(finalized, true); +} + +/** + * @tc.name: EventLoopTimerTest004 + * @tc.desc: Start a timer + * @tc.type: FUNC + * @tc.require: AR000CKRTB AR000CQE0C + * @tc.author: fangyi + */ +HWTEST_F(DistributedDBEventLoopTimerTest, EventLoopTimerTest004, TestSize.Level1) +{ + // ready data + ASSERT_EQ(g_loop != nullptr, true); + + /** + * @tc.steps: step1. start the loop. + * @tc.expected: step1. start successfully. + */ + std::atomic running(false); + std::thread loopThread([&running]() { + running = true; + g_loop->Run(); + }); + + int tryCounter = 0; + while (!running && ++tryCounter < MAX_RETRY_TIMES) { + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PIECE_1)); + } + EXPECT_EQ(running, true); + + /** + * @tc.steps: step2. create and start a timer. + * @tc.expected: step2. start successfully. + */ + int errCode = E_OK; + IEvent *timer = IEvent::CreateEvent(TIME_PIECE_10, errCode); + ASSERT_EQ(timer != nullptr, true); + std::atomic counter(0); + errCode = timer->SetAction([&counter](EventsMask revents) -> int { ++counter; return E_OK; }, nullptr); + EXPECT_EQ(errCode, E_OK); + errCode = g_loop->Add(timer); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step3. wait and check. + * @tc.expected: step3. 'counter' increased by the timer. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PIECE_100)); + EXPECT_EQ(counter > 0, true); + g_loop->KillObj(); + loopThread.join(); + timer->DecObjRef(timer); +} + +/** + * @tc.name: EventLoopTimerTest005 + * @tc.desc: Stop a timer + * @tc.type: FUNC + * @tc.require: AR000CKRTB AR000CQE0C + * @tc.author: fangyi + */ +HWTEST_F(DistributedDBEventLoopTimerTest, EventLoopTimerTest005, TestSize.Level1) +{ + // ready data + ASSERT_EQ(g_loop != nullptr, true); + + /** + * @tc.steps: step1. start the loop. + * @tc.expected: step1. start successfully. + */ + std::atomic running(false); + std::thread loopThread([&running]() { + running = true; + g_loop->Run(); + }); + + int tryCounter = 0; + while (!running && ++tryCounter <= MAX_RETRY_TIMES) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + EXPECT_EQ(running, true); + + /** + * @tc.steps: step2. create and start a timer. + * @tc.expected: step2. start successfully. + */ + int errCode = E_OK; + IEvent *timer = IEvent::CreateEvent(10, errCode); + ASSERT_EQ(timer != nullptr, true); + std::atomic counter(0); + std::atomic finalize(false); + errCode = timer->SetAction( + [&counter](EventsMask revents) -> int { + ++counter; + return E_OK; + }, [&finalize]() { finalize = true; }); + EXPECT_EQ(errCode, E_OK); + errCode = g_loop->Add(timer); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step3. wait and check. + * @tc.expected: step3. 'counter' increased by the timer and the timer object finalized. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PIECE_100)); + timer->KillAndDecObjRef(timer); + timer = nullptr; + g_loop->KillObj(); + loopThread.join(); + EXPECT_EQ(counter > 0, true); + EXPECT_EQ(finalize, true); +} + +/** + * @tc.name: EventLoopTimerTest006 + * @tc.desc: Stop a timer + * @tc.type: FUNC + * @tc.require: AR000CKRTB AR000CQE0C + * @tc.author: fangyi + */ +HWTEST_F(DistributedDBEventLoopTimerTest, EventLoopTimerTest006, TestSize.Level1) +{ + // ready data + ASSERT_EQ(g_loop != nullptr, true); + + /** + * @tc.steps: step1. start the loop. + * @tc.expected: step1. start successfully. + */ + std::atomic running(false); + std::thread loopThread([&running]() { + running = true; + g_loop->Run(); + }); + + int tryCounter = 0; + while (!running && ++tryCounter <= MAX_RETRY_TIMES) { + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PIECE_10)); + } + EXPECT_EQ(running, true); + + /** + * @tc.steps: step2. create and start a timer. + * @tc.expected: step2. start successfully. + */ + int errCode = E_OK; + IEvent *timer = IEvent::CreateEvent(TIME_PIECE_10, errCode); + ASSERT_EQ(timer != nullptr, true); + std::atomic counter(0); + std::atomic finalize(false); + errCode = timer->SetAction([&counter](EventsMask revents) -> int { ++counter; return -E_STALE; }, + [&finalize]() { finalize = true; }); + EXPECT_EQ(errCode, E_OK); + errCode = g_loop->Add(timer); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step3. wait and check. + * @tc.expected: step3. 'counter' increased by the timer and the timer object finalized. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PIECE_100)); + g_loop->KillObj(); + loopThread.join(); + timer->DecObjRef(timer); + timer = nullptr; + EXPECT_EQ(finalize, true); + EXPECT_EQ(counter > 0, true); +} + +/** + * @tc.name: EventLoopTimerTest007 + * @tc.desc: Modify a timer + * @tc.type: FUNC + * @tc.require: AR000CKRTB AR000CQE0C + * @tc.author: fangyi + */ +HWTEST_F(DistributedDBEventLoopTimerTest, EventLoopTimerTest007, TestSize.Level2) +{ + // ready data + ASSERT_EQ(g_loop != nullptr, true); + + /** + * @tc.steps: step1. start the loop. + * @tc.expected: step1. start successfully. + */ + std::atomic running(false); + std::thread loopThread([&running]() { + running = true; + g_loop->Run(); + }); + + int tryCounter = 0; + while (!running && ++tryCounter <= MAX_RETRY_TIMES) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + EXPECT_EQ(running, true); + + /** + * @tc.steps: step2. create and start a timer. + * @tc.expected: step2. start successfully. + */ + int errCode = E_OK; + IEvent *timer = IEvent::CreateEvent(TIME_PIECE_1000, errCode); + ASSERT_EQ(timer != nullptr, true); + int counter = 1; // Interval: 1 * TIME_PIECE_100 + EventTime lastTime = TimerTester::GetCurrentTime(); + errCode = timer->SetAction( + [timer, &counter, &lastTime](EventsMask revents) -> int { + EventTime now = TimerTester::GetCurrentTime(); + EventTime delta = now - lastTime; + delta -= counter * TIME_PIECE_1000; + EXPECT_EQ(delta >= -TIME_INACCURACY && delta <= TIME_INACCURACY, true); + if (++counter > RETRY_TIMES_5) { + return -E_STALE; + } + lastTime = TimerTester::GetCurrentTime(); + int ret = timer->SetTimeout(counter * TIME_PIECE_1000); + EXPECT_EQ(ret, E_OK); + return E_OK; + }, nullptr); + EXPECT_EQ(errCode, E_OK); + errCode = g_loop->Add(timer); + EXPECT_EQ(errCode, E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PIECE_10000)); + g_loop->KillObj(); + loopThread.join(); + timer->DecObjRef(timer); +} diff --git a/mock/distributeddb/test/unittest/common/common/process_communicator_test_stub.h b/mock/distributeddb/test/unittest/common/common/process_communicator_test_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..1a31ba62bc29aac6445a4a792ddca1b84e7ef712 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/common/process_communicator_test_stub.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PROCESSCOMMUNICATOR_TEST_STUB_H_H +#define PROCESSCOMMUNICATOR_TEST_STUB_H_H + +#include +#include +#include +#include + +#include "iprocess_communicator.h" +#include "store_types.h" + +namespace DistributedDB { +class ProcessCommunicatorTestStub : public IProcessCommunicator { +public: + ProcessCommunicatorTestStub() {} + ~ProcessCommunicatorTestStub() override {} + + DBStatus Start(const std::string &processLabel) override + { + return OK; + } + + // The Stop should only be called after Start successfully + DBStatus Stop() override + { + return OK; + } + + DBStatus RegOnDeviceChange(const OnDeviceChange &callback) override + { + return OK; + } + DBStatus RegOnDataReceive(const OnDataReceive &callback) override + { + return OK; + } + + DBStatus SendData(const DeviceInfos &dstDevInfo, const uint8_t *data, uint32_t length) override + { + if (isCommErr) { + return COMM_FAILURE; + } + return OK; + } + + uint32_t GetMtuSize() override + { + return 1 * 1024 * 1024; // 1MB + } + + DeviceInfos GetLocalDeviceInfos() override + { + DeviceInfos info; + info.identifier = "default"; + return info; + } + + std::vector GetRemoteOnlineDeviceInfosList() override + { + std::vector info; + return info; + } + + bool IsSameProcessLabelStartedOnPeerDevice(const DeviceInfos &peerDevInfo) override + { + return true; + } + + void SetCommErr(bool commErr) + { + isCommErr = commErr; + } +private: + bool isCommErr = false; +}; +} // namespace DistributedDB + +#endif // PROCESSCOMMUNICATOR_TEST_STUB_H_H diff --git a/mock/distributeddb/test/unittest/common/communicator/adapter_stub.cpp b/mock/distributeddb/test/unittest/common/communicator/adapter_stub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6304efdd2a15bfc24ba95e84584b4208a6c25ecb --- /dev/null +++ b/mock/distributeddb/test/unittest/common/communicator/adapter_stub.cpp @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adapter_stub.h" +#include +#include "db_errno.h" +#include "endian_convert.h" +#include "frame_header.h" +#include "iprocess_communicator.h" +#include "log_print.h" +#include "distributeddb_communicator_common.h" + +using namespace DistributedDB; + +namespace { + const uint32_t STUB_MTU_SIZE = 5 * 1024 * 1024; // 5 M, 1024 is scale + const uint32_t STUB_TIME_OUT = 5 * 1000; // 5 S, 1000 is scale +} + +/* + * Override Part + */ +AdapterStub::~AdapterStub() +{ + // Do nothing +} + +int AdapterStub::StartAdapter() +{ + return E_OK; +} + +void AdapterStub::StopAdapter() +{ + // Do nothing +} + +uint32_t AdapterStub::GetMtuSize() +{ + return STUB_MTU_SIZE; +} + +uint32_t AdapterStub::GetMtuSize(const std::string &target) +{ + (void)target; + return GetMtuSize(); +} + +uint32_t AdapterStub::GetTimeout() +{ + return STUB_TIME_OUT; +} + +uint32_t AdapterStub::GetTimeout(const std::string &target) +{ + (void)target; + return GetTimeout(); +} + +int AdapterStub::GetLocalIdentity(std::string &outTarget) +{ + outTarget = localTarget_; + return E_OK; +} + +int AdapterStub::SendBytes(const std::string &dstTarget, const uint8_t *bytes, uint32_t length) +{ + LOGI("[UT][Stub][Send] Send length=%u to dstTarget=%s begin.", length, dstTarget.c_str()); + ApplySendBlock(); + + if (QuerySendRetry(dstTarget)) { + LOGI("[UT][Stub][Send] Retry for %s true.", dstTarget.c_str()); + return -E_WAIT_RETRY; + } + + if (QuerySendTotalLoss()) { + LOGI("[UT][Stub][Send] Total loss for %s true.", dstTarget.c_str()); + return E_OK; + } + + if (QuerySendPartialLoss()) { + LOGI("[UT][Stub][Send] Partial loss for %s true.", dstTarget.c_str()); + return E_OK; + } + + std::lock_guard onChangeLockGuard(onChangeMutex_); + if (targetMapAdapter_.count(dstTarget) == 0) { + LOGI("[UT][Stub][Send] dstTarget=%s not found.", dstTarget.c_str()); + return -E_NOT_FOUND; + } + + ApplySendBitError(bytes, length); + + AdapterStub *toAdapter = targetMapAdapter_[dstTarget]; + toAdapter->DeliverBytes(localTarget_, bytes, length); + LOGI("[UT][Stub][Send] Send to dstTarget=%s end.", dstTarget.c_str()); + return E_OK; +} + +int AdapterStub::RegBytesReceiveCallback(const BytesReceiveCallback &onReceive, const Finalizer &inOper) +{ + std::lock_guard onReceiveLockGuard(onReceiveMutex_); + return RegCallBack(onReceive, onReceiveHandle_, inOper, onReceiveFinalizer_); +} + +int AdapterStub::RegTargetChangeCallback(const TargetChangeCallback &onChange, const Finalizer &inOper) +{ + std::lock_guard onChangeLockGuard(onChangeMutex_); + return RegCallBack(onChange, onChangeHandle_, inOper, onChangeFinalizer_); +} + +int AdapterStub::RegSendableCallback(const SendableCallback &onSendable, const Finalizer &inOper) +{ + std::lock_guard onSendableLockGuard(onSendableMutex_); + return RegCallBack(onSendable, onSendableHandle_, inOper, onSendableFinalizer_); +} + + +bool AdapterStub::IsDeviceOnline(const std::string &device) +{ + (void)device; + return true; +} + +std::shared_ptr AdapterStub::GetExtendHeaderHandle(const ExtendInfo ¶mInfo) +{ + std::shared_ptr handle = std::make_shared(paramInfo); + return handle; +} +/* + * Extended Part + */ +void AdapterStub::ConnectAdapterStub(AdapterStub *thisStub, AdapterStub *thatStub) +{ + LOGI("[UT][Stub][ConnectAdapter] thisStub=%s, thatStub=%s.", thisStub->GetLocalTarget().c_str(), + thatStub->GetLocalTarget().c_str()); + thisStub->Connect(thatStub); + thatStub->Connect(thisStub); +} + +void AdapterStub::DisconnectAdapterStub(AdapterStub *thisStub, AdapterStub *thatStub) +{ + LOGI("[UT][Stub][DisconnectAdapter] thisStub=%s, thatStub=%s.", thisStub->GetLocalTarget().c_str(), + thatStub->GetLocalTarget().c_str()); + thisStub->Disconnect(thatStub); + thatStub->Disconnect(thisStub); +} + +AdapterStub::AdapterStub(const std::string &inLocalTarget) + : localTarget_(inLocalTarget) +{ +} + +const std::string &AdapterStub::GetLocalTarget() +{ + return localTarget_; +} + +void AdapterStub::Connect(AdapterStub *inStub) +{ + LOGI("[UT][Stub][Connect] thisStub=%s, thatStub=%s.", localTarget_.c_str(), inStub->GetLocalTarget().c_str()); + std::lock_guard onChangeLockGuard(onChangeMutex_); + targetMapAdapter_[inStub->GetLocalTarget()] = inStub; + if (onChangeHandle_) { + onChangeHandle_(inStub->GetLocalTarget(), true); + } +} + +void AdapterStub::Disconnect(AdapterStub *inStub) +{ + LOGI("[UT][Stub][Disconnect] thisStub=%s, thatStub=%s.", localTarget_.c_str(), inStub->GetLocalTarget().c_str()); + std::lock_guard onChangeLockGuard(onChangeMutex_); + targetMapAdapter_.erase(inStub->GetLocalTarget()); + if (onChangeHandle_) { + onChangeHandle_(inStub->GetLocalTarget(), false); + } +} + +void AdapterStub::DeliverBytes(const std::string &srcTarget, const uint8_t *bytes, uint32_t length) +{ + std::lock_guard onReceiveLockGuard(onReceiveMutex_); + if (onReceiveHandle_) { + uint32_t headLength = 0; + std::string userId; + CheckAndGetDataHeadInfo(bytes, length, headLength, userId); + onReceiveHandle_(srcTarget, bytes + headLength, length - headLength, userId); + } +} + +void AdapterStub::CheckAndGetDataHeadInfo(const uint8_t *data, uint32_t totalLen, uint32_t &headLength, + std::string &userId) +{ + auto info = reinterpret_cast(data); + NetToHost(info->magic); + if (info->magic == ExtendHeaderHandleTest::MAGIC_NUM) { + NetToHost(info->length); + NetToHost(info->version); + headLength = info->length; + std::string tmpUserId(BUFF_LEN, 0); + for (uint8_t i = 0; i < BUFF_LEN; i++) { + tmpUserId[i] = info->userId[i]; + } + userId = tmpUserId; + } else { + headLength = 0; + } +} + +/* + * Simulate Part + */ +void AdapterStub::SimulateSendBlock() +{ + LOGI("[UT][Stub][Block] Before Lock."); + block_.lock(); + LOGI("[UT][Stub][Block] After Lock."); +} + +void AdapterStub::SimulateSendBlockClear() +{ + LOGI("[UT][Stub][UnBlock] Before UnLock."); + block_.unlock(); + LOGI("[UT][Stub][UnBlock] After UnLock."); +} + +void AdapterStub::SimulateSendRetry(const std::string &dstTarget) +{ + std::lock_guard retryLockGuard(retryMutex_); + targetRetrySet_.insert(dstTarget); +} + +void AdapterStub::SimulateSendRetryClear(const std::string &dstTarget) +{ + bool isSetBefore = false; + { + std::lock_guard retryLockGuard(retryMutex_); + if (targetRetrySet_.count(dstTarget) == 0) { + return; + } + isSetBefore = true; + targetRetrySet_.erase(dstTarget); + } + if (isSetBefore) { + std::lock_guard onSendableLockGuard(onSendableMutex_); + if (onSendableHandle_) { + onSendableHandle_(dstTarget); + } + } +} + +void AdapterStub::SimulateSendPartialLoss() +{ + isPartialLossSimulated_ = true; +} + +void AdapterStub::SimulateSendPartialLossClear() +{ + isPartialLossSimulated_ = false; +} + +void AdapterStub::SimulateSendTotalLoss() +{ + isTotalLossSimulated_ = true; +} + +void AdapterStub::SimulateSendTotalLossClear() +{ + isTotalLossSimulated_ = false; +} + +void AdapterStub::SimulateSendBitErrorInMagicField(bool doFlag, uint16_t inMagic) +{ + doChangeMagicFlag_ = doFlag; + magicField_ = inMagic; +} + +void AdapterStub::SimulateSendBitErrorInVersionField(bool doFlag, uint16_t inVersion) +{ + doChangeVersionFlag_ = doFlag; + versionField_ = inVersion; +} + +void AdapterStub::SimulateSendBitErrorInCheckSumField(bool doFlag, uint64_t inCheckSum) +{ + doChangeCheckSumFlag_ = doFlag; + checkSumField_ = inCheckSum; +} + +void AdapterStub::SimulateSendBitErrorInPacketLenField(bool doFlag, uint32_t inPacketLen) +{ + doChangePacketLenFlag_ = doFlag; + packetLenField_ = inPacketLen; +} + +void AdapterStub::SimulateSendBitErrorInPacketTypeField(bool doFlag, uint8_t inPacketType) +{ + doChangePacketTypeFlag_ = doFlag; + packetTypeField_ = inPacketType; +} + +void AdapterStub::SimulateSendBitErrorInPaddingLenField(bool doFlag, uint8_t inPaddingLen) +{ + doChangePaddingLenFlag_ = doFlag; + paddingLenField_ = inPaddingLen; +} + +void AdapterStub::SimulateSendBitErrorInMessageIdField(bool doFlag, uint32_t inMessageId) +{ + doChangeMessageIdFlag_ = doFlag; + messageIdField_ = inMessageId; +} + +void AdapterStub::ApplySendBlock() +{ + LOGI("[UT][Stub][ApplyBlock] Before Lock&UnLock."); + block_.lock(); + block_.unlock(); + LOGI("[UT][Stub][ApplyBlock] After Lock&UnLock."); +} + +bool AdapterStub::QuerySendRetry(const std::string &dstTarget) +{ + std::lock_guard retryLockGuard(retryMutex_); + if (targetRetrySet_.count(dstTarget) == 0) { + return false; + } else { + return true; + } +} + +bool AdapterStub::QuerySendPartialLoss() +{ + if (isPartialLossSimulated_) { + uint64_t count = countForPartialLoss_.fetch_add(1, std::memory_order_seq_cst); + if (count % 2 == 0) { // 2 is half + return true; + } + } + return false; +} + +bool AdapterStub::QuerySendTotalLoss() +{ + return isTotalLossSimulated_; +} + +namespace { +uint64_t CalculateXorSum(const uint8_t *bytes, uint32_t length) +{ + if (length % sizeof(uint64_t) != 0) { + return 0; + } + int count = length / sizeof(uint64_t); + auto array = reinterpret_cast(bytes); + uint64_t outSum = 0; + for (int i = 0; i < count; i++) { + outSum ^= array[i]; + } + return outSum; +} +const uint32_t LENGTH_BEFORE_SUM_RANGE = sizeof(uint64_t) + sizeof(uint64_t); +} + +void AdapterStub::ApplySendBitError(const uint8_t *bytes, uint32_t length) +{ + // Change field in CommPhyHeader + if (length < sizeof(CommPhyHeader)) { + return; + } + auto edibleBytes = const_cast(bytes); + auto phyHeader = reinterpret_cast(edibleBytes); + if (doChangeMagicFlag_) { + phyHeader->magic = HostToNet(magicField_); + } + if (doChangeVersionFlag_) { + phyHeader->version = HostToNet(versionField_); + } + if (doChangeCheckSumFlag_) { + phyHeader->checkSum = HostToNet(checkSumField_); + } + if (doChangePacketLenFlag_) { + phyHeader->packetLen = HostToNet(packetLenField_); + } + if (doChangePacketTypeFlag_) { + phyHeader->packetType = HostToNet(packetTypeField_); + } + if (doChangePaddingLenFlag_) { + phyHeader->paddingLen = HostToNet(paddingLenField_); + } + // Change field in MessageHeader. Assumpt that no fragment + if (length < sizeof(CommPhyHeader) + sizeof(CommDivergeHeader) + sizeof(MessageHeader)) { + return; + } + edibleBytes += (sizeof(CommPhyHeader) + sizeof(CommDivergeHeader)); + auto msgHeader = reinterpret_cast(edibleBytes); + if (doChangeMessageIdFlag_) { + msgHeader->messageId = HostToNet(messageIdField_); + phyHeader->checkSum = HostToNet(CalculateXorSum(bytes + LENGTH_BEFORE_SUM_RANGE, + length - LENGTH_BEFORE_SUM_RANGE)); + } +} diff --git a/mock/distributeddb/test/unittest/common/communicator/adapter_stub.h b/mock/distributeddb/test/unittest/common/communicator/adapter_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..bc942a5a430733b3830883e266ef0d15a1c64e86 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/communicator/adapter_stub.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ADAPTER_STUB_H +#define ADAPTER_STUB_H + +#include +#include +#include +#include +#include +#include +#include "iadapter.h" + +namespace DistributedDB { +class AdapterStub : public IAdapter { +public: + /* + * Override Part + */ + ~AdapterStub() override; + + int StartAdapter() override; + void StopAdapter() override; + uint32_t GetMtuSize() override; + uint32_t GetMtuSize(const std::string &target) override; + uint32_t GetTimeout() override; + uint32_t GetTimeout(const std::string &target) override; + int GetLocalIdentity(std::string &outTarget) override; + + int SendBytes(const std::string &dstTarget, const uint8_t *bytes, uint32_t length) override; + + int RegBytesReceiveCallback(const BytesReceiveCallback &onReceive, const Finalizer &inOper) override; + int RegTargetChangeCallback(const TargetChangeCallback &onChange, const Finalizer &inOper) override; + int RegSendableCallback(const SendableCallback &onSendable, const Finalizer &inOper) override; + + bool IsDeviceOnline(const std::string &device) override; + + std::shared_ptr GetExtendHeaderHandle(const ExtendInfo ¶mInfo) override; + + void CheckAndGetDataHeadInfo(const uint8_t *data, uint32_t totalLen, uint32_t &headLength, + std::string &userId); + + /* + * Extended Part + */ + static void ConnectAdapterStub(AdapterStub *thisStub, AdapterStub *thatStub); + static void DisconnectAdapterStub(AdapterStub *thisStub, AdapterStub *thatStub); + + explicit AdapterStub(const std::string &inLocalTarget); + const std::string &GetLocalTarget(); + + void SimulateSendBlock(); + void SimulateSendBlockClear(); + + void SimulateSendRetry(const std::string &dstTarget); + void SimulateSendRetryClear(const std::string &dstTarget); + + void SimulateSendPartialLoss(); + void SimulateSendPartialLossClear(); + + void SimulateSendTotalLoss(); + void SimulateSendTotalLossClear(); + + void SimulateSendBitErrorInMagicField(bool doFlag, uint16_t inMagic); + void SimulateSendBitErrorInVersionField(bool doFlag, uint16_t inVersion); + void SimulateSendBitErrorInCheckSumField(bool doFlag, uint64_t inCheckSum); + void SimulateSendBitErrorInPacketLenField(bool doFlag, uint32_t inPacketLen); + void SimulateSendBitErrorInPacketTypeField(bool doFlag, uint8_t inPacketType); + void SimulateSendBitErrorInPaddingLenField(bool doFlag, uint8_t inPaddingLen); + void SimulateSendBitErrorInMessageIdField(bool doFlag, uint32_t inMessageId); +private: + void Connect(AdapterStub *inStub); + void Disconnect(AdapterStub *inStub); + void DeliverBytes(const std::string &srcTarget, const uint8_t *bytes, uint32_t length); + + void ApplySendBlock(); + bool QuerySendRetry(const std::string &dstTarget); + bool QuerySendPartialLoss(); + bool QuerySendTotalLoss(); + void ApplySendBitError(const uint8_t *bytes, uint32_t length); + + std::string localTarget_; + std::map targetMapAdapter_; + + BytesReceiveCallback onReceiveHandle_; + TargetChangeCallback onChangeHandle_; + SendableCallback onSendableHandle_; + Finalizer onReceiveFinalizer_; + Finalizer onChangeFinalizer_; + Finalizer onSendableFinalizer_; + std::mutex onReceiveMutex_; + std::mutex onChangeMutex_; + std::mutex onSendableMutex_; + + // Member for simulation + std::mutex block_; + + std::mutex retryMutex_; + std::set targetRetrySet_; + + std::atomic isPartialLossSimulated_{false}; + std::atomic countForPartialLoss_{0}; + + std::atomic isTotalLossSimulated_{false}; + + bool doChangeMagicFlag_ = false; + bool doChangeVersionFlag_ = false; + bool doChangeCheckSumFlag_ = false; + bool doChangePacketLenFlag_ = false; + bool doChangePacketTypeFlag_ = false; + bool doChangePaddingLenFlag_ = false; + bool doChangeMessageIdFlag_ = false; + uint16_t magicField_ = 0; + uint16_t versionField_ = 0; + uint64_t checkSumField_ = 0; + uint32_t packetLenField_ = 0; + uint8_t packetTypeField_ = 0; + uint8_t paddingLenField_ = 0; + uint32_t messageIdField_ = 0; +}; +} + +#endif // ADAPTER_STUB_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.cpp b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..27d86ff996bfaaae189ec656f8224e246b72c673 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "distributeddb_communicator_common.h" +#include +#include "db_errno.h" +#include "log_print.h" +#include "message_transform.h" +#include "securec.h" + +using namespace std; +using namespace DistributedDB; + +bool SetUpEnv(EnvHandle &inEnv, const string &inName) +{ + if (inEnv.adapterHandle != nullptr || inEnv.commAggrHandle != nullptr) { + LOGI("[UT][Common][SetUp] Already Setup for %s", inName.c_str()); + return false; + } + + inEnv.adapterHandle = new (nothrow) AdapterStub(inName); + if (inEnv.adapterHandle == nullptr) { + LOGI("[UT][Common][SetUp] Create AdapterStub fail for %s", inName.c_str()); + return false; + } + + inEnv.commAggrHandle = new (nothrow) CommunicatorAggregator(); + if (inEnv.commAggrHandle == nullptr) { + LOGI("[UT][Common][SetUp] Create CommunicatorAggregator fail for %s", inName.c_str()); + return false; + } + + int errCode = inEnv.commAggrHandle->Initialize(inEnv.adapterHandle); + if (errCode != E_OK) { + LOGI("[UT][Common][SetUp] Init CommunicatorAggregator fail for %s", inName.c_str()); + return false; + } + + return true; +} + +void TearDownEnv(EnvHandle &inEnv) +{ + if (inEnv.commAggrHandle != nullptr) { + inEnv.commAggrHandle->Finalize(); + inEnv.commAggrHandle->DecObjRef(inEnv.commAggrHandle); + inEnv.commAggrHandle = nullptr; + } + + if (inEnv.adapterHandle != nullptr) { + delete inEnv.adapterHandle; + inEnv.adapterHandle = nullptr; + } +} + +static void RegFuncForTinyMsg() +{ + TransformFunc funcForTinyMsg; + funcForTinyMsg.computeFunc = [](const Message *inMsg)->uint32_t{return TINY_SIZE;}; + funcForTinyMsg.serializeFunc = [](uint8_t *buffer, uint32_t length, const Message *inMsg)->int{ + const RegedTinyObject *outObj = inMsg->GetObject(); + EXPECT_NE(outObj, nullptr); + return E_OK; + }; + funcForTinyMsg.deserializeFunc = [](const uint8_t *buffer, uint32_t length, Message *inMsg)->int{ + int errCode = inMsg->SetCopiedObject(RegedTinyObject()); + EXPECT_EQ(errCode, E_OK); + return E_OK; + }; + + MessageTransform::RegTransformFunction(REGED_TINY_MSG_ID, funcForTinyMsg); +} + +static void RegFuncForHugeMsg() +{ + TransformFunc funcForHugeMsg; + funcForHugeMsg.computeFunc = [](const Message *inMsg)->uint32_t{return HUGE_SIZE;}; + funcForHugeMsg.serializeFunc = [](uint8_t *buffer, uint32_t length, const Message *inMsg)->int{ + const RegedHugeObject *outObj = inMsg->GetObject(); + EXPECT_NE(outObj, nullptr); + return E_OK; + }; + funcForHugeMsg.deserializeFunc = [](const uint8_t *buffer, uint32_t length, Message *inMsg)->int{ + int errCode = inMsg->SetCopiedObject(RegedHugeObject()); + EXPECT_EQ(errCode, E_OK); + return E_OK; + }; + + MessageTransform::RegTransformFunction(REGED_HUGE_MSG_ID, funcForHugeMsg); +} + +static void RegFuncForGiantMsg() +{ + TransformFunc funcForGiantMsg; + funcForGiantMsg.computeFunc = [](const Message *inMsg)->uint32_t{ + const RegedGiantObject *outObj = inMsg->GetObject(); + if (outObj == nullptr) { + return 0; + } + return outObj->rawData_.size(); + }; + funcForGiantMsg.serializeFunc = [](uint8_t *buffer, uint32_t length, const Message *inMsg)->int{ + const RegedGiantObject *outObj = inMsg->GetObject(); + if (outObj == nullptr) { + return -E_INVALID_ARGS; + } + if (outObj->rawData_.size() != length) { + return -E_LENGTH_ERROR; + } + errno_t errCode = memcpy_s(buffer, length, &(outObj->rawData_[0]), length); + if (errCode != EOK) { + return -E_SECUREC_ERROR; + } + return E_OK; + }; + funcForGiantMsg.deserializeFunc = [](const uint8_t *buffer, uint32_t length, Message *inMsg)->int{ + RegedGiantObject *obj = new (nothrow) RegedGiantObject(); + if (obj == nullptr) { + return -E_OUT_OF_MEMORY; + } + obj->rawData_.resize(length); + errno_t retCode = memcpy_s(&(obj->rawData_[0]), length, buffer, length); + if (retCode != EOK) { + delete obj; + return -E_SECUREC_ERROR; + } + int errCode = inMsg->SetExternalObject(obj); + if (errCode != E_OK) { + delete obj; + return errCode; + } + return E_OK; + }; + + MessageTransform::RegTransformFunction(REGED_GIANT_MSG_ID, funcForGiantMsg); +} + +static void RegFuncForOverSizeMsg() +{ + TransformFunc funcForOverSizeMsg; + funcForOverSizeMsg.computeFunc = [](const Message *inMsg)->uint32_t{return OVER_SIZE;}; + funcForOverSizeMsg.serializeFunc = [](uint8_t *buffer, uint32_t length, const Message *inMsg)->int{ + const RegedOverSizeObject *outObj = inMsg->GetObject(); + EXPECT_NE(outObj, nullptr); + return E_OK; + }; + funcForOverSizeMsg.deserializeFunc = [](const uint8_t *buffer, uint32_t length, Message *inMsg)->int{ + int errCode = inMsg->SetCopiedObject(RegedOverSizeObject()); + EXPECT_EQ(errCode, E_OK); + return E_OK; + }; + + MessageTransform::RegTransformFunction(REGED_OVERSIZE_MSG_ID, funcForOverSizeMsg); +} + +void DoRegTransformFunction() +{ + RegFuncForTinyMsg(); + RegFuncForHugeMsg(); + RegFuncForGiantMsg(); + RegFuncForOverSizeMsg(); +} + +Message *BuildRegedTinyMessage() +{ + RegedTinyObject *obj = new (nothrow) RegedTinyObject(); + if (obj == nullptr) { + return nullptr; + } + + Message *outMsg = new (nothrow) Message(REGED_TINY_MSG_ID); + if (outMsg == nullptr) { + delete obj; + obj = nullptr; + return nullptr; + } + + int errCode = outMsg->SetExternalObject(obj); + if (errCode != E_OK) { + delete obj; + obj = nullptr; + delete outMsg; + outMsg = nullptr; + return nullptr; + } + outMsg->SetMessageType(TYPE_REQUEST); + outMsg->SetSessionId(FIXED_SESSIONID); + outMsg->SetSequenceId(FIXED_SEQUENCEID); + + return outMsg; +} + +Message *BuildRegedHugeMessage() +{ + RegedHugeObject *obj = new (nothrow) RegedHugeObject(); + if (obj == nullptr) { + return nullptr; + } + + Message *outMsg = new (nothrow) Message(REGED_HUGE_MSG_ID); + if (outMsg == nullptr) { + delete obj; + return nullptr; + } + + int errCode = outMsg->SetExternalObject(obj); + if (errCode != E_OK) { + delete obj; + obj = nullptr; + delete outMsg; + outMsg = nullptr; + return nullptr; + } + outMsg->SetMessageType(TYPE_RESPONSE); + outMsg->SetSessionId(FIXED_SESSIONID); + outMsg->SetSequenceId(FIXED_SEQUENCEID); + + return outMsg; +} + +// length should be a multiple of four +Message *BuildRegedGiantMessage(uint32_t length) +{ + uint32_t count = length / sizeof(uint32_t); + if (count == 0) { + return nullptr; + } + + RegedGiantObject *obj = new (nothrow) RegedGiantObject(); + if (obj == nullptr) { + return nullptr; + } + + Message *outMsg = new (nothrow) Message(REGED_GIANT_MSG_ID); + if (outMsg == nullptr) { + delete obj; + obj = nullptr; + return nullptr; + } + + obj->rawData_.resize(count * sizeof(uint32_t)); + auto dataPtr = reinterpret_cast(&(obj->rawData_[0])); + uint32_t value = 0; + while (value < count) { + *dataPtr++ = value++; + } + + int errCode = outMsg->SetExternalObject(obj); + if (errCode != E_OK) { + delete obj; + obj = nullptr; + delete outMsg; + outMsg = nullptr; + return nullptr; + } + outMsg->SetMessageType(TYPE_NOTIFY); + outMsg->SetSessionId(FIXED_SESSIONID); + outMsg->SetSequenceId(FIXED_SEQUENCEID); + + return outMsg; +} + +Message *BuildRegedOverSizeMessage() +{ + RegedOverSizeObject *obj = new (nothrow) RegedOverSizeObject(); + if (obj == nullptr) { + return nullptr; + } + + Message *outMsg = new (nothrow) Message(REGED_OVERSIZE_MSG_ID); + if (outMsg == nullptr) { + delete obj; + return nullptr; + } + + int errCode = outMsg->SetExternalObject(obj); + if (errCode != E_OK) { + delete obj; + obj = nullptr; + delete outMsg; + outMsg = nullptr; + return nullptr; + } + outMsg->SetMessageType(TYPE_NOTIFY); + outMsg->SetSessionId(FIXED_SESSIONID); + outMsg->SetSequenceId(FIXED_SEQUENCEID); + + return outMsg; +} + +Message *BuildUnRegedTinyMessage() +{ + UnRegedTinyObject *obj = new (nothrow) UnRegedTinyObject(); + if (obj == nullptr) { + return nullptr; + } + + Message *outMsg = new (nothrow) Message(UNREGED_TINY_MSG_ID); + if (outMsg == nullptr) { + delete obj; + return nullptr; + } + + int errCode = outMsg->SetExternalObject(obj); + if (errCode != E_OK) { + delete obj; + obj = nullptr; + delete outMsg; + outMsg = nullptr; + return nullptr; + } + outMsg->SetMessageType(TYPE_NOTIFY); + outMsg->SetSessionId(FIXED_SESSIONID); + outMsg->SetSequenceId(FIXED_SEQUENCEID); + + return outMsg; +} + +bool RegedGiantObject::CheckEqual(const RegedGiantObject &inLeft, const RegedGiantObject &inRight) +{ + if (inLeft.rawData_.size() != inRight.rawData_.size()) { + return false; + } + uint32_t index = 0; + for (auto &left : inLeft.rawData_) { + uint8_t right = inRight.rawData_[index]; + if (left != right) { + LOGE("[RegedGiantObject][CheckEqual] RawData unequal at index=%u", index); + return false; + } + index++; + } + return true; +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.h b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.h new file mode 100644 index 0000000000000000000000000000000000000000..11be295afdb9763a8771963019dddbd37a7a488b --- /dev/null +++ b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_COMMUNICATOR_COMMON_H +#define DISTRIBUTEDDB_COMMUNICATOR_COMMON_H + +#include +#include +#include +#include "endian_convert.h" +#include "message.h" +#include "adapter_stub.h" +#include "frame_header.h" +#include "iprocess_communicator.h" +#include "communicator_aggregator.h" +#include "store_types.h" + +struct EnvHandle { + DistributedDB::AdapterStub *adapterHandle = nullptr; + DistributedDB::CommunicatorAggregator *commAggrHandle = nullptr; +}; + +struct OnOfflineDevice { + std::set onlineDevices; + std::string latestOnlineDevice; + std::string latestOfflineDevice; +}; + +bool SetUpEnv(EnvHandle &inEnv, const std::string &inName); +void TearDownEnv(EnvHandle &inEnv); + +struct RegedTinyObject { + uint32_t placeHolder_ = 0; +}; + +struct RegedHugeObject { + uint32_t placeHolder_ = 0; +}; + +struct RegedGiantObject { + std::vector rawData_; + static bool CheckEqual(const RegedGiantObject &inLeft, const RegedGiantObject &inRight); +}; + +struct RegedOverSizeObject { + uint32_t placeHolder_ = 0; +}; + +struct UnRegedTinyObject { + uint32_t placeHolder_ = 0; +}; + +const uint32_t BUFF_LEN = 16; +struct ExtendHeadInfo { + uint32_t magic = 0; + uint32_t length = 0; + uint32_t version = 0; + uint8_t userId[BUFF_LEN] = {0}; +}; + +class ExtendHeaderHandleTest : public DistributedDB::ExtendHeaderHandle { +public: + explicit ExtendHeaderHandleTest(const DistributedDB::ExtendInfo &info) : headSize_(0) + { + localDbProperty_.appId = info.appId; + localDbProperty_.storeId = info.storeId; + localDbProperty_.userId = info.userId; + localDbProperty_.dstTarget = info.dstTarget; + }; + ~ExtendHeaderHandleTest() {}; + // headSize should be 8 byte align + // return OK and headSize = 0 if no need to fill Head Data + // return OK and headSize > 0 if permit sync and will call FillHeadData + // return NO_PERMISSION if not permit sync + DistributedDB::DBStatus GetHeadDataSize(uint32_t &headSize) override + { + headSize_ = sizeof(ExtendHeadInfo); + headSize_ = BYTE_8_ALIGN(headSize_); + headSize = headSize_; + return DistributedDB::OK; + }; + + DistributedDB::DBStatus FillHeadData(uint8_t *data, uint32_t headSize, uint32_t totalLen) override + { + ExtendHeadInfo info = {MAGIC_NUM, headSize_, 0}; + DistributedDB::HostToNet(info.magic); + DistributedDB::HostToNet(info.length); + DistributedDB::HostToNet(info.version); + for (uint8_t i = 0; i < BUFF_LEN; i++) { + info.userId[i] = localDbProperty_.userId[i]; + } + auto errCode = memcpy_s(data, totalLen, &info, sizeof(ExtendHeadInfo)); + if (errCode != EOK) { + return DistributedDB::DB_ERROR; + } + return DistributedDB::OK; + }; + static constexpr int MAGIC_NUM = 0xF2; +private: + DistributedDB::ExtendInfo localDbProperty_; + uint32_t headSize_; +}; + +const std::string DEVICE_NAME_A = "DeviceA"; +const std::string DEVICE_NAME_B = "DeviceB"; +const std::string DEVICE_NAME_C = "DeviceC"; +constexpr uint64_t LABEL_A = 1234; +constexpr uint64_t LABEL_B = 2345; +constexpr uint64_t LABEL_C = 3456; +constexpr uint32_t REGED_TINY_MSG_ID = 1111; +constexpr uint32_t REGED_HUGE_MSG_ID = 2222; +constexpr uint32_t REGED_GIANT_MSG_ID = 3333; +constexpr uint32_t REGED_OVERSIZE_MSG_ID = 4444; +constexpr uint32_t UNREGED_TINY_MSG_ID = 5555; +constexpr uint32_t FIXED_SESSIONID = 98765; +constexpr uint32_t FIXED_SEQUENCEID = 87654; +constexpr uint32_t TINY_SIZE = 100; // 100 Bytes +constexpr uint32_t HUGE_SIZE = 4 * 1024 * 1024; // 4 MBytes, 1024 is scale +constexpr uint32_t OVER_SIZE = 100 * 1024 * 1024; // 100 MBytes, 1024 is scale +constexpr uint32_t HEADER_SIZE = sizeof(DistributedDB::CommPhyHeader) + sizeof(DistributedDB::CommDivergeHeader) + + sizeof(DistributedDB::MessageHeader); // 96 Bytes For Header, 32 phyHeader, 40 divergeHeader, 24 msgHeader +constexpr uint32_t MAX_CAPACITY = 64 * 1024 * 1024; // 64 MBytes, 1024 is scale + +void DoRegTransformFunction(); + +DistributedDB::Message *BuildRegedTinyMessage(); +DistributedDB::Message *BuildRegedHugeMessage(); +DistributedDB::Message *BuildRegedGiantMessage(uint32_t length); +DistributedDB::Message *BuildRegedOverSizeMessage(); +DistributedDB::Message *BuildUnRegedTinyMessage(); + +#define ASSERT_NOT_NULL_AND_ACTIVATE(communicator) \ +{ \ + ASSERT_NE(communicator, nullptr); \ + (communicator)->Activate(); \ +} + +#endif // DISTRIBUTEDDB_COMMUNICATOR_COMMON_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_deep_test.cpp b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_deep_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5bf3155565c5cc42d5278a3fdb2ef3ac7bb19ca6 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_deep_test.cpp @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "db_errno.h" +#include "distributeddb_communicator_common.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "message.h" +#include "serial_buffer.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; + +namespace { + EnvHandle g_envDeviceA; + EnvHandle g_envDeviceB; + EnvHandle g_envDeviceC; + ICommunicator *g_commAA = nullptr; + ICommunicator *g_commAB = nullptr; + ICommunicator *g_commBB = nullptr; + ICommunicator *g_commBC = nullptr; + ICommunicator *g_commCC = nullptr; + ICommunicator *g_commCA = nullptr; +} + +class DistributedDBCommunicatorDeepTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBCommunicatorDeepTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Create and init CommunicatorAggregator and AdapterStub + */ + LOGI("[UT][DeepTest][SetUpTestCase] Enter."); + bool errCode = SetUpEnv(g_envDeviceA, DEVICE_NAME_A); + ASSERT_EQ(errCode, true); + errCode = SetUpEnv(g_envDeviceB, DEVICE_NAME_B); + ASSERT_EQ(errCode, true); + errCode = SetUpEnv(g_envDeviceC, DEVICE_NAME_C); + ASSERT_EQ(errCode, true); + DoRegTransformFunction(); + CommunicatorAggregator::EnableCommunicatorNotFoundFeedback(false); +} + +void DistributedDBCommunicatorDeepTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Finalize and release CommunicatorAggregator and AdapterStub + */ + LOGI("[UT][DeepTest][TearDownTestCase] Enter."); + std::this_thread::sleep_for(std::chrono::seconds(7)); // Wait 7 s to make sure all thread quiet and memory released + TearDownEnv(g_envDeviceA); + TearDownEnv(g_envDeviceB); + TearDownEnv(g_envDeviceC); + CommunicatorAggregator::EnableCommunicatorNotFoundFeedback(true); +} + +namespace { +void AllocAllCommunicator() +{ + int errorNo = E_OK; + g_commAA = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(g_commAA); + g_commAB = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_B, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(g_commAB); + g_commBB = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_B, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(g_commBB); + g_commBC = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_C, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(g_commBC); + g_commCC = g_envDeviceC.commAggrHandle->AllocCommunicator(LABEL_C, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(g_commCC); + g_commCA = g_envDeviceC.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(g_commCA); +} + +void ReleaseAllCommunicator() +{ + g_envDeviceA.commAggrHandle->ReleaseCommunicator(g_commAA); + g_commAA = nullptr; + g_envDeviceA.commAggrHandle->ReleaseCommunicator(g_commAB); + g_commAB = nullptr; + g_envDeviceB.commAggrHandle->ReleaseCommunicator(g_commBB); + g_commBB = nullptr; + g_envDeviceB.commAggrHandle->ReleaseCommunicator(g_commBC); + g_commBC = nullptr; + g_envDeviceC.commAggrHandle->ReleaseCommunicator(g_commCC); + g_commCC = nullptr; + g_envDeviceC.commAggrHandle->ReleaseCommunicator(g_commCA); + g_commCA = nullptr; +} +} + +void DistributedDBCommunicatorDeepTest::SetUp() +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: Alloc communicator AA, AB, BB, BC, CC, CA + */ + AllocAllCommunicator(); +} + +void DistributedDBCommunicatorDeepTest::TearDown() +{ + /** + * @tc.teardown: Release communicator AA, AB, BB, BC, CC, CA + */ + ReleaseAllCommunicator(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); // Wait 200 ms to make sure all thread quiet +} + +/** + * @tc.name: WaitAndRetrySend 001 + * @tc.desc: Test send retry semantic + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorDeepTest, WaitAndRetrySend001, TestSize.Level2) +{ + // Preset + Message *msgForBB = nullptr; + g_commBB->RegOnMessageCallback([&msgForBB](const std::string &srcTarget, Message *inMsg) { + msgForBB = inMsg; + }, nullptr); + + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); // Wait 200 ms to make sure quiet + + /** + * @tc.steps: step2. device A simulate send retry + */ + g_envDeviceA.adapterHandle->SimulateSendRetry(DEVICE_NAME_B); + + /** + * @tc.steps: step3. device A send message to device B using communicator AB + * @tc.expected: step3. communicator BB received no message + */ + Message *msgForAB = BuildRegedTinyMessage(); + ASSERT_NE(msgForAB, nullptr); + SendConfig conf = {true, false, 0}; + int errCode = g_commAB->SendMessage(DEVICE_NAME_B, msgForAB, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait 100 ms + EXPECT_EQ(msgForBB, nullptr); + + /** + * @tc.steps: step4. device A simulate sendable feedback + * @tc.expected: step4. communicator BB received the message + */ + g_envDeviceA.adapterHandle->SimulateSendRetryClear(DEVICE_NAME_B); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait 100 ms + EXPECT_NE(msgForBB, nullptr); + delete msgForBB; + msgForBB = nullptr; + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +static int CreateBufferThenAddIntoScheduler(SendTaskScheduler &scheduler, const std::string &dstTarget, Priority inPrio) +{ + SerialBuffer *eachBuff = new (std::nothrow) SerialBuffer(); + if (eachBuff == nullptr) { + return -E_OUT_OF_MEMORY; + } + int errCode = eachBuff->AllocBufferByTotalLength(100, 0); // 100 totallen without header + if (errCode != E_OK) { + delete eachBuff; + eachBuff = nullptr; + return errCode; + } + SendTask task{eachBuff, dstTarget}; + errCode = scheduler.AddSendTaskIntoSchedule(task, inPrio); + if (errCode != E_OK) { + delete eachBuff; + eachBuff = nullptr; + return errCode; + } + return E_OK; +} + +/** + * @tc.name: SendSchedule 001 + * @tc.desc: Test schedule in Priority order than in send order + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorDeepTest, SendSchedule001, TestSize.Level2) +{ + // Preset + SendTaskScheduler scheduler; + scheduler.Initialize(); + + /** + * @tc.steps: step1. Add low priority target A buffer to schecduler + */ + int errCode = CreateBufferThenAddIntoScheduler(scheduler, DEVICE_NAME_A, Priority::LOW); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. Add low priority target B buffer to schecduler + */ + errCode = CreateBufferThenAddIntoScheduler(scheduler, DEVICE_NAME_B, Priority::LOW); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step3. Add normal priority target B buffer to schecduler + */ + errCode = CreateBufferThenAddIntoScheduler(scheduler, DEVICE_NAME_B, Priority::NORMAL); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step4. Add normal priority target C buffer to schecduler + */ + errCode = CreateBufferThenAddIntoScheduler(scheduler, DEVICE_NAME_C, Priority::NORMAL); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step5. Add high priority target C buffer to schecduler + */ + errCode = CreateBufferThenAddIntoScheduler(scheduler, DEVICE_NAME_C, Priority::HIGH); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step6. Add high priority target A buffer to schecduler + */ + errCode = CreateBufferThenAddIntoScheduler(scheduler, DEVICE_NAME_A, Priority::HIGH); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step7. schedule out buffers one by one + * @tc.expected: step7. the order is: high priority target C + * high priority target A + * normal priority target B + * normal priority target C + * low priority target A + * low priority target B + */ + SendTask outTask; + SendTaskInfo outTaskInfo; + // high priority target C + errCode = scheduler.ScheduleOutSendTask(outTask, outTaskInfo); + ASSERT_EQ(errCode, E_OK); + EXPECT_EQ(outTask.dstTarget, DEVICE_NAME_C); + EXPECT_EQ(outTaskInfo.taskPrio, Priority::HIGH); + scheduler.FinalizeLastScheduleTask(); + // high priority target A + errCode = scheduler.ScheduleOutSendTask(outTask, outTaskInfo); + ASSERT_EQ(errCode, E_OK); + EXPECT_EQ(outTask.dstTarget, DEVICE_NAME_A); + EXPECT_EQ(outTaskInfo.taskPrio, Priority::HIGH); + scheduler.FinalizeLastScheduleTask(); + // normal priority target B + errCode = scheduler.ScheduleOutSendTask(outTask, outTaskInfo); + ASSERT_EQ(errCode, E_OK); + EXPECT_EQ(outTask.dstTarget, DEVICE_NAME_B); + EXPECT_EQ(outTaskInfo.taskPrio, Priority::NORMAL); + scheduler.FinalizeLastScheduleTask(); + // normal priority target C + errCode = scheduler.ScheduleOutSendTask(outTask, outTaskInfo); + ASSERT_EQ(errCode, E_OK); + EXPECT_EQ(outTask.dstTarget, DEVICE_NAME_C); + EXPECT_EQ(outTaskInfo.taskPrio, Priority::NORMAL); + scheduler.FinalizeLastScheduleTask(); + // low priority target A + errCode = scheduler.ScheduleOutSendTask(outTask, outTaskInfo); + ASSERT_EQ(errCode, E_OK); + EXPECT_EQ(outTask.dstTarget, DEVICE_NAME_A); + EXPECT_EQ(outTaskInfo.taskPrio, Priority::LOW); + scheduler.FinalizeLastScheduleTask(); + // low priority target B + errCode = scheduler.ScheduleOutSendTask(outTask, outTaskInfo); + ASSERT_EQ(errCode, E_OK); + EXPECT_EQ(outTask.dstTarget, DEVICE_NAME_B); + EXPECT_EQ(outTaskInfo.taskPrio, Priority::LOW); + scheduler.FinalizeLastScheduleTask(); +} + +/** + * @tc.name: Fragment 001 + * @tc.desc: Test fragmentation in send and receive + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorDeepTest, Fragment001, TestSize.Level2) +{ + // Preset + Message *recvMsgForBB = nullptr; + g_commBB->RegOnMessageCallback([&recvMsgForBB](const std::string &srcTarget, Message *inMsg) { + recvMsgForBB = inMsg; + }, nullptr); + + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step2. device A send message(registered and giant) to device B using communicator AB + * @tc.expected: step2. communicator BB received the message + */ + const uint32_t dataLength = 13 * 1024 * 1024; // 13 MB, 1024 is scale + Message *sendMsgForAB = BuildRegedGiantMessage(dataLength); + ASSERT_NE(sendMsgForAB, nullptr); + SendConfig conf = {false, false, 0}; + int errCode = g_commAB->SendMessage(DEVICE_NAME_B, sendMsgForAB, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(2600)); // Wait 2600 ms to make sure send done + ASSERT_NE(recvMsgForBB, nullptr); + ASSERT_EQ(recvMsgForBB->GetMessageId(), REGED_GIANT_MSG_ID); + + /** + * @tc.steps: step3. Compare received data with send data + * @tc.expected: step3. equal + */ + Message *oriMsgForAB = BuildRegedGiantMessage(dataLength); + ASSERT_NE(oriMsgForAB, nullptr); + const RegedGiantObject *oriObjForAB = oriMsgForAB->GetObject(); + ASSERT_NE(oriObjForAB, nullptr); + const RegedGiantObject *recvObjForBB = recvMsgForBB->GetObject(); + ASSERT_NE(recvObjForBB, nullptr); + bool isEqual = RegedGiantObject::CheckEqual(*oriObjForAB, *recvObjForBB); + EXPECT_EQ(isEqual, true); + + // CleanUp + delete oriMsgForAB; + oriMsgForAB = nullptr; + delete recvMsgForBB; + recvMsgForBB = nullptr; + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Fragment 002 + * @tc.desc: Test fragmentation in partial loss + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorDeepTest, Fragment002, TestSize.Level2) +{ + // Preset + Message *recvMsgForCC = nullptr; + g_commCC->RegOnMessageCallback([&recvMsgForCC](const std::string &srcTarget, Message *inMsg) { + recvMsgForCC = inMsg; + }, nullptr); + + /** + * @tc.steps: step1. connect device B with device C + */ + AdapterStub::ConnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); // Wait 200 ms to make sure quiet + + /** + * @tc.steps: step2. device B simulate partial loss + */ + g_envDeviceB.adapterHandle->SimulateSendPartialLoss(); + + /** + * @tc.steps: step3. device B send message(registered and giant) to device C using communicator BC + * @tc.expected: step3. communicator CC not receive the message + */ + uint32_t dataLength = 13 * 1024 * 1024; // 13 MB, 1024 is scale + Message *sendMsgForBC = BuildRegedGiantMessage(dataLength); + ASSERT_NE(sendMsgForBC, nullptr); + SendConfig conf = {false, false, 0}; + int errCode = g_commBC->SendMessage(DEVICE_NAME_C, sendMsgForBC, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(2600)); // Wait 2600 ms to make sure send done + EXPECT_EQ(recvMsgForCC, nullptr); + + /** + * @tc.steps: step4. device B not simulate partial loss + */ + g_envDeviceB.adapterHandle->SimulateSendPartialLossClear(); + + /** + * @tc.steps: step5. device B send message(registered and giant) to device C using communicator BC + * @tc.expected: step5. communicator CC received the message, the length equal to the one that is second send + */ + dataLength = 17 * 1024 * 1024; // 17 MB, 1024 is scale + Message *resendMsgForBC = BuildRegedGiantMessage(dataLength); + ASSERT_NE(resendMsgForBC, nullptr); + errCode = g_commBC->SendMessage(DEVICE_NAME_C, resendMsgForBC, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(3400)); // Wait 3400 ms to make sure send done + ASSERT_NE(recvMsgForCC, nullptr); + ASSERT_EQ(recvMsgForCC->GetMessageId(), REGED_GIANT_MSG_ID); + const RegedGiantObject *recvObjForCC = recvMsgForCC->GetObject(); + ASSERT_NE(recvObjForCC, nullptr); + EXPECT_EQ(dataLength, recvObjForCC->rawData_.size()); + + // CleanUp + delete recvMsgForCC; + recvMsgForCC = nullptr; + AdapterStub::DisconnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); +} + +/** + * @tc.name: Fragment 003 + * @tc.desc: Test fragmentation simultaneously + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorDeepTest, Fragment003, TestSize.Level3) +{ + // Preset + std::atomic count {0}; + OnMessageCallback callback = [&count](const std::string &srcTarget, Message *inMsg) { + delete inMsg; + inMsg = nullptr; + count.fetch_add(1, std::memory_order_seq_cst); + }; + g_commBB->RegOnMessageCallback(callback, nullptr); + g_commBC->RegOnMessageCallback(callback, nullptr); + + /** + * @tc.steps: step1. connect device A with device B, then device B with device C + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + AdapterStub::ConnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(400)); // Wait 400 ms to make sure quiet + + /** + * @tc.steps: step2. device A and device C simulate send block + */ + g_envDeviceA.adapterHandle->SimulateSendBlock(); + g_envDeviceC.adapterHandle->SimulateSendBlock(); + + /** + * @tc.steps: step3. device A send message(registered and giant) to device B using communicator AB + */ + uint32_t dataLength = 23 * 1024 * 1024; // 23 MB, 1024 is scale + Message *sendMsgForAB = BuildRegedGiantMessage(dataLength); + ASSERT_NE(sendMsgForAB, nullptr); + SendConfig conf = {false, false, 0}; + int errCode = g_commAB->SendMessage(DEVICE_NAME_B, sendMsgForAB, conf); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step4. device C send message(registered and giant) to device B using communicator CC + */ + Message *sendMsgForCC = BuildRegedGiantMessage(dataLength); + ASSERT_NE(sendMsgForCC, nullptr); + errCode = g_commCC->SendMessage(DEVICE_NAME_B, sendMsgForCC, conf); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step5. device A and device C not simulate send block + * @tc.expected: step5. communicator BB and BV received the message + */ + g_envDeviceA.adapterHandle->SimulateSendBlockClear(); + g_envDeviceC.adapterHandle->SimulateSendBlockClear(); + std::this_thread::sleep_for(std::chrono::milliseconds(9200)); // Wait 9200 ms to make sure send done + EXPECT_EQ(count, 2); // 2 combined message received + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + AdapterStub::DisconnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); +} + +namespace { +void ClearPreviousTestCaseInfluence() +{ + ReleaseAllCommunicator(); + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + AdapterStub::ConnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); + AdapterStub::ConnectAdapterStub(g_envDeviceC.adapterHandle, g_envDeviceA.adapterHandle); + std::this_thread::sleep_for(std::chrono::seconds(10)); // Wait 10 s to make sure all thread quiet + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + AdapterStub::DisconnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); + AdapterStub::DisconnectAdapterStub(g_envDeviceC.adapterHandle, g_envDeviceA.adapterHandle); + AllocAllCommunicator(); +} +} + +/** + * @tc.name: ReliableOnline 001 + * @tc.desc: Test device online reliability + * @tc.type: FUNC + * @tc.require: AR000BVDGJ AR000CQE0N + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorDeepTest, ReliableOnline001, TestSize.Level2) +{ + // Preset + ClearPreviousTestCaseInfluence(); + std::atomic count {0}; + OnConnectCallback callback = [&count](const std::string &target, bool isConnect) { + if (isConnect) { + count.fetch_add(1, std::memory_order_seq_cst); + } + }; + g_commAA->RegOnConnectCallback(callback, nullptr); + g_commAB->RegOnConnectCallback(callback, nullptr); + g_commBB->RegOnConnectCallback(callback, nullptr); + g_commBC->RegOnConnectCallback(callback, nullptr); + g_commCC->RegOnConnectCallback(callback, nullptr); + g_commCA->RegOnConnectCallback(callback, nullptr); + + /** + * @tc.steps: step1. device A and device B and device C simulate send total loss + */ + g_envDeviceA.adapterHandle->SimulateSendTotalLoss(); + g_envDeviceB.adapterHandle->SimulateSendTotalLoss(); + g_envDeviceC.adapterHandle->SimulateSendTotalLoss(); + + /** + * @tc.steps: step2. connect device A with device B, device B with device C, device C with device A + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + AdapterStub::ConnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); + AdapterStub::ConnectAdapterStub(g_envDeviceC.adapterHandle, g_envDeviceA.adapterHandle); + + /** + * @tc.steps: step3. wait a long time + * @tc.expected: step3. no communicator received the online callback + */ + std::this_thread::sleep_for(std::chrono::seconds(7)); // Wait 7 s to make sure quiet + EXPECT_EQ(count, 0); // no online callback received + + /** + * @tc.steps: step4. device A and device B and device C not simulate send total loss + */ + g_envDeviceA.adapterHandle->SimulateSendTotalLossClear(); + g_envDeviceB.adapterHandle->SimulateSendTotalLossClear(); + g_envDeviceC.adapterHandle->SimulateSendTotalLossClear(); + std::this_thread::sleep_for(std::chrono::seconds(7)); // Wait 7 s to make sure send done + EXPECT_EQ(count, 6); // 6 online callback received in total + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + AdapterStub::DisconnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); + AdapterStub::DisconnectAdapterStub(g_envDeviceC.adapterHandle, g_envDeviceA.adapterHandle); +} diff --git a/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_send_receive_test.cpp b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_send_receive_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea0d11a237dac22b7561606c59a140ab770e6631 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_send_receive_test.cpp @@ -0,0 +1,748 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "db_errno.h" +#include "distributeddb_communicator_common.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "message.h" +#include "protocol_proto.h" +#include "time_sync.h" +#include "sync_types.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; + +namespace { + constexpr int SEND_COUNT_GOAL = 20; // Send 20 times + + EnvHandle g_envDeviceA; + EnvHandle g_envDeviceB; + ICommunicator *g_commAA = nullptr; + ICommunicator *g_commBA = nullptr; + ICommunicator *g_commBB = nullptr; +} + +class DistributedDBCommunicatorSendReceiveTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBCommunicatorSendReceiveTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Create and init CommunicatorAggregator and AdapterStub + */ + LOGI("[UT][SendRecvTest][SetUpTestCase] Enter."); + bool errCode = SetUpEnv(g_envDeviceA, DEVICE_NAME_A); + ASSERT_EQ(errCode, true); + errCode = SetUpEnv(g_envDeviceB, DEVICE_NAME_B); + ASSERT_EQ(errCode, true); + DoRegTransformFunction(); + CommunicatorAggregator::EnableCommunicatorNotFoundFeedback(false); +} + +void DistributedDBCommunicatorSendReceiveTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Finalize and release CommunicatorAggregator and AdapterStub + */ + LOGI("[UT][SendRecvTest][TearDownTestCase] Enter."); + std::this_thread::sleep_for(std::chrono::seconds(7)); // Wait 7 s to make sure all thread quiet and memory released + TearDownEnv(g_envDeviceA); + TearDownEnv(g_envDeviceB); + CommunicatorAggregator::EnableCommunicatorNotFoundFeedback(true); +} + +void DistributedDBCommunicatorSendReceiveTest::SetUp() +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: Alloc communicator AA, BA, BB + */ + int errorNo = E_OK; + g_commAA = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + ASSERT_EQ(errorNo, E_OK); + ASSERT_NOT_NULL_AND_ACTIVATE(g_commAA); + + errorNo = E_OK; + g_commBA = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + ASSERT_EQ(errorNo, E_OK); + ASSERT_NOT_NULL_AND_ACTIVATE(g_commBA); + + errorNo = E_OK; + g_commBB = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_B, errorNo); + ASSERT_EQ(errorNo, E_OK); + ASSERT_NOT_NULL_AND_ACTIVATE(g_commBB); +} + +void DistributedDBCommunicatorSendReceiveTest::TearDown() +{ + /** + * @tc.teardown: Release communicator AA, BA, BB + */ + g_envDeviceA.commAggrHandle->ReleaseCommunicator(g_commAA); + g_commAA = nullptr; + g_envDeviceB.commAggrHandle->ReleaseCommunicator(g_commBA); + g_commBA = nullptr; + g_envDeviceB.commAggrHandle->ReleaseCommunicator(g_commBB); + g_commBA = nullptr; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); // Wait 200 ms to make sure all thread quiet +} + +static Message *BuildAppLayerFrameMessage() +{ + DistributedDBUnitTest::DataSyncMessageInfo info; + info.messageId_ = DistributedDB::TIME_SYNC_MESSAGE; + info.messageType_ = TYPE_REQUEST; + DistributedDB::Message *message = nullptr; + DistributedDBUnitTest::DistributedDBToolsUnitTest::BuildMessage(info, message); + return message; +} + +/** + * @tc.name: Send And Receive 001 + * @tc.desc: Test send and receive based on equipment communicator + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, SendAndReceive001, TestSize.Level1) +{ + // Preset + string srcTargetForAA; + Message *recvMsgForAA = nullptr; + string srcTargetForBA; + Message *recvMsgForBA = nullptr; + string srcTargetForBB; + Message *recvMsgForBB = nullptr; + g_commAA->RegOnMessageCallback([&srcTargetForAA, &recvMsgForAA](const std::string &srcTarget, Message *inMsg) { + srcTargetForAA = srcTarget; + recvMsgForAA = inMsg; + }, nullptr); + g_commBA->RegOnMessageCallback([&srcTargetForBA, &recvMsgForBA](const std::string &srcTarget, Message *inMsg) { + srcTargetForBA = srcTarget; + recvMsgForBA = inMsg; + }, nullptr); + g_commBB->RegOnMessageCallback([&srcTargetForBB, &recvMsgForBB](const std::string &srcTarget, Message *inMsg) { + srcTargetForBB = srcTarget; + recvMsgForBB = inMsg; + }, nullptr); + + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step2. device A send message(registered and tiny) to device B using communicator AA + * @tc.expected: step2. communicator BA received the message + */ + Message *msgForAA = BuildRegedTinyMessage(); + ASSERT_NE(msgForAA, nullptr); + SendConfig conf = {false, false, 0}; + int errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); // sleep 200 ms + EXPECT_EQ(recvMsgForBB, nullptr); + EXPECT_EQ(srcTargetForBA, DEVICE_NAME_A); + ASSERT_NE(recvMsgForBA, nullptr); + EXPECT_EQ(recvMsgForBA->GetMessageId(), REGED_TINY_MSG_ID); + EXPECT_EQ(recvMsgForBA->GetMessageType(), TYPE_REQUEST); + EXPECT_EQ(recvMsgForBA->GetSessionId(), FIXED_SESSIONID); + EXPECT_EQ(recvMsgForBA->GetSequenceId(), FIXED_SEQUENCEID); + EXPECT_EQ(recvMsgForBA->GetErrorNo(), NO_ERROR); + delete recvMsgForBA; + recvMsgForBA = nullptr; + + /** + * @tc.steps: step3. device B send message(registered and tiny) to device A using communicator BB + * @tc.expected: step3. communicator AA did not receive the message + */ + Message *msgForBB = BuildRegedTinyMessage(); + ASSERT_NE(msgForBB, nullptr); + conf = {true, 0}; + errCode = g_commBB->SendMessage(DEVICE_NAME_A, msgForBB, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(srcTargetForAA, ""); + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Send And Receive 002 + * @tc.desc: Test send oversize message will fail + * @tc.type: FUNC + * @tc.require: AR000BVDGK AR000CQE0O + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, SendAndReceive002, TestSize.Level1) +{ + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step2. device A send message(registered and oversize) to device B using communicator AA + * @tc.expected: step2. send fail + */ + Message *msgForAA = BuildRegedOverSizeMessage(); + ASSERT_NE(msgForAA, nullptr); + SendConfig conf = {true, false, 0}; + int errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, conf); + EXPECT_NE(errCode, E_OK); + delete msgForAA; + msgForAA = nullptr; + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Send And Receive 003 + * @tc.desc: Test send unregistered message will fail + * @tc.type: FUNC + * @tc.require: AR000BVDGK AR000CQE0O + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, SendAndReceive003, TestSize.Level1) +{ + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step2. device A send message(unregistered and tiny) to device B using communicator AA + * @tc.expected: step2. send fail + */ + Message *msgForAA = BuildUnRegedTinyMessage(); + ASSERT_NE(msgForAA, nullptr); + SendConfig conf = {true, false, 0}; + int errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, conf); + EXPECT_NE(errCode, E_OK); + delete msgForAA; + msgForAA = nullptr; + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Send Flow Control 001 + * @tc.desc: Test send in nonblock way + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, SendFlowControl001, TestSize.Level1) +{ + // Preset + int countForBA = 0; + int countForBB = 0; + g_commBA->RegOnSendableCallback([&countForBA](){ countForBA++; }, nullptr); + g_commBB->RegOnSendableCallback([&countForBB](){ countForBB++; }, nullptr); + + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait 100 ms to make sure send cause by online done + countForBA = 0; + countForBB = 0; + + /** + * @tc.steps: step2. device B simulates send block + */ + g_envDeviceB.adapterHandle->SimulateSendBlock(); + + /** + * @tc.steps: step3. device B send as much as possible message(unregistered and huge) in nonblock way + * to device A using communicator BA until send fail; + * @tc.expected: step3. send fail will happen. + */ + int sendCount = 0; + while (true) { + Message *msgForBA = BuildRegedHugeMessage(); + ASSERT_NE(msgForBA, nullptr); + SendConfig conf = {true, false, 0}; + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); + if (errCode == E_OK) { + sendCount++; + } else { + delete msgForBA; + msgForBA = nullptr; + break; + } + } + + /** + * @tc.steps: step4. device B simulates send block terminate + * @tc.expected: step4. send count before fail is equal as expected. sendable callback happened. + */ + g_envDeviceB.adapterHandle->SimulateSendBlockClear(); + int expectSendCount = MAX_CAPACITY / (HUGE_SIZE + HEADER_SIZE) + + (MAX_CAPACITY % (HUGE_SIZE + HEADER_SIZE) == 0 ? 0 : 1); + EXPECT_EQ(sendCount, expectSendCount); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + EXPECT_GE(countForBA, 1); + EXPECT_GE(countForBB, 1); + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Send Flow Control 002 + * @tc.desc: Test send in block(without timeout) way + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, SendFlowControl002, TestSize.Level1) +{ + // Preset + int cntForBA = 0; + int cntForBB = 0; + g_commBA->RegOnSendableCallback([&cntForBA](){ cntForBA++; }, nullptr); + g_commBB->RegOnSendableCallback([&cntForBB](){ cntForBB++; }, nullptr); + + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait 100 ms to make sure send cause by online done + cntForBA = 0; + cntForBB = 0; + + /** + * @tc.steps: step2. device B simulates send block + */ + g_envDeviceB.adapterHandle->SimulateSendBlock(); + + /** + * @tc.steps: step3. device B send a certain message(unregistered and huge) in block way + * without timeout to device A using communicator BA; + */ + int sendCount = 0; + int sendFailCount = 0; + std::thread sendThread([&sendCount, &sendFailCount]() { + while (sendCount < SEND_COUNT_GOAL) { + Message *msgForBA = BuildRegedHugeMessage(); + ASSERT_NE(msgForBA, nullptr); + SendConfig conf = {false, false, 0}; + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); + if (errCode != E_OK) { + delete msgForBA; + msgForBA = nullptr; + sendFailCount++; + } + sendCount++; + } + }); + + /** + * @tc.steps: step4. device B simulates send block terminate + * @tc.expected: step4. send fail count is zero. sendable callback happened. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + g_envDeviceB.adapterHandle->SimulateSendBlockClear(); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + sendThread.join(); + EXPECT_EQ(sendCount, SEND_COUNT_GOAL); + EXPECT_EQ(sendFailCount, 0); + EXPECT_GE(cntForBA, 1); + EXPECT_GE(cntForBB, 1); + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Send Flow Control 003 + * @tc.desc: Test send in block(with timeout) way + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, SendFlowControl003, TestSize.Level1) +{ + // Preset + int cntsForBA = 0; + int cntsForBB = 0; + g_commBA->RegOnSendableCallback([&cntsForBA](){ cntsForBA++; }, nullptr); + g_commBB->RegOnSendableCallback([&cntsForBB](){ cntsForBB++; }, nullptr); + + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + cntsForBA = 0; + cntsForBB = 0; + + /** + * @tc.steps: step2. device B simulates send block + */ + g_envDeviceB.adapterHandle->SimulateSendBlock(); + + /** + * @tc.steps: step3. device B send a certain message(unregistered and huge) in block way + * with timeout to device A using communicator BA; + */ + int sendCnt = 0; + int sendFailCnt = 0; + std::thread sendThread([&sendCnt, &sendFailCnt]() { + while (sendCnt < SEND_COUNT_GOAL) { + Message *msgForBA = BuildRegedHugeMessage(); + ASSERT_NE(msgForBA, nullptr); + SendConfig conf = {false, false, 100}; + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); // 100 ms timeout + if (errCode != E_OK) { + delete msgForBA; + msgForBA = nullptr; + sendFailCnt++; + } + sendCnt++; + } + }); + + /** + * @tc.steps: step4. device B simulates send block terminate + * @tc.expected: step4. send fail count is no more than expected. sendable callback happened. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(300)); // wait 300 ms + g_envDeviceB.adapterHandle->SimulateSendBlockClear(); + std::this_thread::sleep_for(std::chrono::milliseconds(1200)); // wait 1200 ms + sendThread.join(); + EXPECT_EQ(sendCnt, SEND_COUNT_GOAL); + EXPECT_LE(sendFailCnt, 4); + EXPECT_GE(cntsForBA, 1); + EXPECT_GE(cntsForBB, 1); + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Receive Check 001 + * @tc.desc: Receive packet field check + * @tc.type: FUNC + * @tc.require: AR000BVRNU AR000CQE0J + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, ReceiveCheck001, TestSize.Level1) +{ + // Preset + int recvCount = 0; + g_commAA->RegOnMessageCallback([&recvCount](const std::string &srcTarget, Message *inMsg) { + recvCount++; + if (inMsg != nullptr) { + delete inMsg; + inMsg = nullptr; + } + }, nullptr); + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step1. create packet with magic field error + * @tc.expected: step1. no message callback + */ + g_envDeviceB.adapterHandle->SimulateSendBitErrorInMagicField(true, 0xFFFF); + Message *msgForBA = BuildRegedTinyMessage(); + SendConfig conf = {true, false, 0}; + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(recvCount, 0); + g_envDeviceB.adapterHandle->SimulateSendBitErrorInMagicField(false, 0); + + /** + * @tc.steps: step2. create packet with version field error + * @tc.expected: step2. no message callback + */ + g_envDeviceB.adapterHandle->SimulateSendBitErrorInVersionField(true, 0xFFFF); + msgForBA = BuildRegedTinyMessage(); + errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(recvCount, 0); + g_envDeviceB.adapterHandle->SimulateSendBitErrorInVersionField(false, 0); + + /** + * @tc.steps: step3. create packet with checksum field error + * @tc.expected: step3. no message callback + */ + g_envDeviceB.adapterHandle->SimulateSendBitErrorInCheckSumField(true, 0xFFFF); + msgForBA = BuildRegedTinyMessage(); + errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(recvCount, 0); + g_envDeviceB.adapterHandle->SimulateSendBitErrorInCheckSumField(false, 0); + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Receive Check 002 + * @tc.desc: Receive packet field check + * @tc.type: FUNC + * @tc.require: AR000BVRNU AR000CQE0J + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, ReceiveCheck002, TestSize.Level1) +{ + // Preset + int recvCount = 0; + g_commAA->RegOnMessageCallback([&recvCount](const std::string &srcTarget, Message *inMsg) { + recvCount++; + if (inMsg != nullptr) { + delete inMsg; + inMsg = nullptr; + } + }, nullptr); + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step1. create packet with packetLen field error + * @tc.expected: step1. no message callback + */ + g_envDeviceB.adapterHandle->SimulateSendBitErrorInPacketLenField(true, 0xFFFF); + Message *msgForBA = BuildRegedTinyMessage(); + SendConfig conf = {true, false, 0}; + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(recvCount, 0); + g_envDeviceB.adapterHandle->SimulateSendBitErrorInPacketLenField(false, 0); + + /** + * @tc.steps: step1. create packet with packetType field error + * @tc.expected: step1. no message callback + */ + g_envDeviceB.adapterHandle->SimulateSendBitErrorInPacketTypeField(true, 0xFF); + msgForBA = BuildRegedTinyMessage(); + errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(recvCount, 0); + g_envDeviceB.adapterHandle->SimulateSendBitErrorInPacketTypeField(false, 0); + + /** + * @tc.steps: step1. create packet with paddingLen field error + * @tc.expected: step1. no message callback + */ + g_envDeviceB.adapterHandle->SimulateSendBitErrorInPaddingLenField(true, 0xFF); + msgForBA = BuildRegedTinyMessage(); + errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(recvCount, 0); + g_envDeviceB.adapterHandle->SimulateSendBitErrorInPaddingLenField(false, 0); + + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Send Result Notify 001 + * @tc.desc: Test send result notify + * @tc.type: FUNC + * @tc.require: AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, SendResultNotify001, TestSize.Level1) +{ + // preset + std::vector sendResult; + auto sendResultNotifier = [&sendResult](int result) { + sendResult.push_back(result); + }; + + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step2. device A send message to device B using communicator AA + * @tc.expected: step2. notify send done and success + */ + Message *msgForAA = BuildRegedTinyMessage(); + ASSERT_NE(msgForAA, nullptr); + SendConfig conf = {false, false, 0}; + int errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, conf, sendResultNotifier); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + ASSERT_EQ(sendResult.size(), static_cast(1)); // 1 notify + EXPECT_EQ(sendResult[0], E_OK); + + /** + * @tc.steps: step3. disconnect device A with device B + */ + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step4. device A send message to device B using communicator AA + * @tc.expected: step2. notify send done and fail + */ + msgForAA = BuildRegedTinyMessage(); + ASSERT_NE(msgForAA, nullptr); + errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, conf, sendResultNotifier); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + ASSERT_EQ(sendResult.size(), static_cast(2)); // 2 notify + EXPECT_NE(sendResult[1], E_OK); // 1 for second element +} + +#define REG_MESSAGE_CALLBACK(src, label) \ + string srcTargetFor##src##label; \ + Message *recvMsgFor##src##label = nullptr; \ + g_comm##src##label->RegOnMessageCallback( \ + [&srcTargetFor##src##label, &recvMsgFor##src##label](const std::string &srcTarget, Message *inMsg) { \ + srcTargetFor##src##label = srcTarget; \ + recvMsgFor##src##label = inMsg; \ + }, nullptr); + +/** + * @tc.name: Message Feedback 001 + * @tc.desc: Test feedback not support messageid and communicator not found + * @tc.type: FUNC + * @tc.require: AR000CQE0M + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, MessageFeedback001, TestSize.Level1) +{ + CommunicatorAggregator::EnableCommunicatorNotFoundFeedback(true); + // preset + REG_MESSAGE_CALLBACK(A, A); + REG_MESSAGE_CALLBACK(B, A); + REG_MESSAGE_CALLBACK(B, B); + + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step2. device B send message to device A using communicator BB + * @tc.expected: step2. communicator BB receive communicator not found feedback + */ + Message *msgForBB = BuildRegedTinyMessage(); + ASSERT_NE(msgForBB, nullptr); + SendConfig conf = {false, false, 0}; + int errCode = g_commBB->SendMessage(DEVICE_NAME_A, msgForBB, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + ASSERT_NE(recvMsgForBB, nullptr); + EXPECT_EQ(srcTargetForBB, DEVICE_NAME_A); + EXPECT_EQ(recvMsgForBB->GetMessageId(), REGED_TINY_MSG_ID); + EXPECT_EQ(recvMsgForBB->GetMessageType(), TYPE_RESPONSE); + EXPECT_EQ(recvMsgForBB->GetSessionId(), FIXED_SESSIONID); + EXPECT_EQ(recvMsgForBB->GetSequenceId(), FIXED_SEQUENCEID); + EXPECT_EQ(recvMsgForBB->GetErrorNo(), static_cast(E_FEEDBACK_COMMUNICATOR_NOT_FOUND)); + EXPECT_EQ(recvMsgForBB->GetObject(), nullptr); + delete recvMsgForBB; + recvMsgForBB = nullptr; + + /** + * @tc.steps: step3. simulate messageid not registered + */ + g_envDeviceB.adapterHandle->SimulateSendBitErrorInMessageIdField(true, UNREGED_TINY_MSG_ID); + + /** + * @tc.steps: step4. device B send message to device A using communicator BA + * @tc.expected: step4. communicator BA receive messageid not register feedback + */ + Message *msgForBA = BuildRegedTinyMessage(); + ASSERT_NE(msgForBA, nullptr); + errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + ASSERT_NE(recvMsgForBA, nullptr); + EXPECT_EQ(srcTargetForBA, DEVICE_NAME_A); + EXPECT_EQ(recvMsgForBA->GetMessageId(), UNREGED_TINY_MSG_ID); + EXPECT_EQ(recvMsgForBA->GetMessageType(), TYPE_RESPONSE); + EXPECT_EQ(recvMsgForBA->GetSessionId(), FIXED_SESSIONID); + EXPECT_EQ(recvMsgForBA->GetSequenceId(), FIXED_SEQUENCEID); + EXPECT_EQ(recvMsgForBA->GetErrorNo(), static_cast(E_FEEDBACK_UNKNOWN_MESSAGE)); + EXPECT_EQ(recvMsgForBA->GetObject(), nullptr); + delete recvMsgForBA; + recvMsgForBA = nullptr; + + // CleanUp + g_envDeviceB.adapterHandle->SimulateSendBitErrorInMessageIdField(false, 0); + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + CommunicatorAggregator::EnableCommunicatorNotFoundFeedback(false); +} + +/** + * @tc.name: SendAndReceiveWithExtendHead001 + * @tc.desc: Test fill extendHead func + * @tc.type: FUNC + * @tc.require: AR000BVDGI AR000CQE0M + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBCommunicatorSendReceiveTest, SendAndReceiveWithExtendHead001, TestSize.Level1) +{ + // Preset + string srcTargetForAA; + Message *recvMsgForAA = nullptr; + string srcTargetForBA; + Message *recvMsgForBA = nullptr; + TimeSync::RegisterTransformFunc(); + g_commAA->RegOnMessageCallback([&srcTargetForAA, &recvMsgForAA](const std::string &srcTarget, Message *inMsg) { + srcTargetForAA = srcTarget; + recvMsgForAA = inMsg; + }, nullptr); + g_commBA->RegOnMessageCallback([&srcTargetForBA, &recvMsgForBA](const std::string &srcTarget, Message *inMsg) { + srcTargetForBA = srcTarget; + recvMsgForBA = inMsg; + }, nullptr); + + /** + * @tc.steps: step1. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + + /** + * @tc.steps: step2. device A send ApplayerFrameMessage to device B using communicator AA with extednHead + * @tc.expected: step2. communicator BA received the message + */ + Message *msgForAA = BuildAppLayerFrameMessage(); + ASSERT_NE(msgForAA, nullptr); + SendConfig conf = {false, true, 0, {"appId", "storeId", "userId", "deviceB"}}; + int errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); // sleep 200 ms + EXPECT_EQ(srcTargetForBA, DEVICE_NAME_A); + ASSERT_NE(recvMsgForBA, nullptr); + delete recvMsgForBA; + recvMsgForBA = nullptr; + DistributedDB::ProtocolProto::UnRegTransformFunction(DistributedDB::TIME_SYNC_MESSAGE); + // CleanUp + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_test.cpp b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ab7e1b0163b0d2aad04c800b4c5331c0023af985 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/communicator/distributeddb_communicator_test.cpp @@ -0,0 +1,764 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "db_errno.h" +#include "distributeddb_communicator_common.h" +#include "distributeddb_tools_unit_test.h" +#include "endian_convert.h" +#include "log_print.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; + +namespace { + EnvHandle g_envDeviceA; + EnvHandle g_envDeviceB; + EnvHandle g_envDeviceC; +} + +static void HandleConnectChange(OnOfflineDevice &onlines, const std::string &target, bool isConnect) +{ + if (isConnect) { + onlines.onlineDevices.insert(target); + onlines.latestOnlineDevice = target; + onlines.latestOfflineDevice.clear(); + } else { + onlines.onlineDevices.erase(target); + onlines.latestOnlineDevice.clear(); + onlines.latestOfflineDevice = target; + } +} + +class DistributedDBCommunicatorTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBCommunicatorTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Create and init CommunicatorAggregator and AdapterStub + */ + LOGI("[UT][Test][SetUpTestCase] Enter."); + bool errCode = SetUpEnv(g_envDeviceA, DEVICE_NAME_A); + ASSERT_EQ(errCode, true); + errCode = SetUpEnv(g_envDeviceB, DEVICE_NAME_B); + ASSERT_EQ(errCode, true); + DoRegTransformFunction(); + CommunicatorAggregator::EnableCommunicatorNotFoundFeedback(false); +} + +void DistributedDBCommunicatorTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Finalize and release CommunicatorAggregator and AdapterStub + */ + LOGI("[UT][Test][TearDownTestCase] Enter."); + std::this_thread::sleep_for(std::chrono::seconds(7)); // Wait 7 s to make sure all thread quiet and memory released + TearDownEnv(g_envDeviceA); + TearDownEnv(g_envDeviceB); + CommunicatorAggregator::EnableCommunicatorNotFoundFeedback(true); +} + +void DistributedDBCommunicatorTest::SetUp() +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBCommunicatorTest::TearDown() +{ + /** + * @tc.teardown: Wait 100 ms to make sure all thread quiet + */ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait 100 ms +} + +/** + * @tc.name: Communicator Management 001 + * @tc.desc: Test alloc and release communicator + * @tc.type: FUNC + * @tc.require: AR000BVDGG AR000CQE0L + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorTest, CommunicatorManagement001, TestSize.Level1) +{ + /** + * @tc.steps: step1. alloc communicator A using label A + * @tc.expected: step1. alloc return OK. + */ + int errorNo = E_OK; + ICommunicator *commA = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + EXPECT_EQ(errorNo, E_OK); + EXPECT_NE(commA, nullptr); + + /** + * @tc.steps: step2. alloc communicator B using label B + * @tc.expected: step2. alloc return OK. + */ + errorNo = E_OK; + ICommunicator *commB = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_B, errorNo); + EXPECT_EQ(errorNo, E_OK); + EXPECT_NE(commA, nullptr); + + /** + * @tc.steps: step3. alloc communicator C using label A + * @tc.expected: step3. alloc return not OK. + */ + errorNo = E_OK; + ICommunicator *commC = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + EXPECT_NE(errorNo, E_OK); + EXPECT_EQ(commC, nullptr); + + /** + * @tc.steps: step4. release communicator A and communicator B + */ + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commA); + commA = nullptr; + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commB); + commB = nullptr; + + /** + * @tc.steps: step5. alloc communicator D using label A + * @tc.expected: step5. alloc return OK. + */ + errorNo = E_OK; + ICommunicator *commD = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + EXPECT_EQ(errorNo, E_OK); + EXPECT_NE(commD, nullptr); + + /** + * @tc.steps: step6. release communicator D + */ + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commD); + commD = nullptr; +} + +static void ConnectWaitDisconnect() +{ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Online And Offline 001 + * @tc.desc: Test functionality triggered by physical devices online and offline + * @tc.type: FUNC + * @tc.require: AR000BVRNS AR000CQE0H + * @tc.author: wudongxing + */ +HWTEST_F(DistributedDBCommunicatorTest, OnlineAndOffline001, TestSize.Level1) +{ + /** + * @tc.steps: step1. device A alloc communicator AA using label A and register callback + * @tc.expected: step1. no callback. + */ + int errorNo = E_OK; + ICommunicator *commAA = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(commAA); + OnOfflineDevice onlineForAA; + commAA->RegOnConnectCallback([&onlineForAA](const std::string &target, bool isConnect) { + HandleConnectChange(onlineForAA, target, isConnect);}, nullptr); + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(0)); + + /** + * @tc.steps: step2. connect device A with device B and then disconnect + * @tc.expected: step2. no callback. + */ + ConnectWaitDisconnect(); + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(0)); + + /** + * @tc.steps: step3. device B alloc communicator BB using label B and register callback + * @tc.expected: step3. no callback. + */ + ICommunicator *commBB = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_B, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(commBB); + OnOfflineDevice onlineForBB; + commBB->RegOnConnectCallback([&onlineForBB](const std::string &target, bool isConnect) { + HandleConnectChange(onlineForBB, target, isConnect);}, nullptr); + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(0)); + + /** + * @tc.steps: step4. connect device A with device B and then disconnect + * @tc.expected: step4. no callback. + */ + ConnectWaitDisconnect(); + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(0)); + + /** + * @tc.steps: step5. device B alloc communicator BA using label A and register callback + * @tc.expected: step5. no callback. + */ + ICommunicator *commBA = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(commBA); + OnOfflineDevice onlineForBA; + commBA->RegOnConnectCallback([&onlineForBA](const std::string &target, bool isConnect) { + HandleConnectChange(onlineForBA, target, isConnect);}, nullptr); + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForBA.onlineDevices.size(), static_cast(0)); + + /** + * @tc.steps: step6. connect device A with device B + * @tc.expected: step6. communicator AA has callback of device B online; + * communicator BA has callback of device A online; + * communicator BB no callback + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForBA.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForAA.latestOnlineDevice, DEVICE_NAME_B); + EXPECT_EQ(onlineForBA.latestOnlineDevice, DEVICE_NAME_A); + + /** + * @tc.steps: step7. disconnect device A with device B + * @tc.expected: step7. communicator AA has callback of device B offline; + * communicator BA has callback of device A offline; + * communicator BB no callback + */ + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForBA.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForAA.latestOfflineDevice, DEVICE_NAME_B); + EXPECT_EQ(onlineForBA.latestOfflineDevice, DEVICE_NAME_A); + + // Clean up + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAA); + g_envDeviceB.commAggrHandle->ReleaseCommunicator(commBB); + g_envDeviceB.commAggrHandle->ReleaseCommunicator(commBA); +} + +#define REG_CONNECT_CALLBACK(communicator, online) \ +{ \ + communicator->RegOnConnectCallback([&online](const std::string &target, bool isConnect) { \ + HandleConnectChange(online, target, isConnect); \ + }, nullptr); \ +} + +#define CONNECT_AND_WAIT(waitTime) \ +{ \ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); \ + std::this_thread::sleep_for(std::chrono::milliseconds(waitTime)); \ +} + +/** + * @tc.name: Online And Offline 002 + * @tc.desc: Test functionality triggered by alloc and release communicator + * @tc.type: FUNC + * @tc.require: AR000BVRNT AR000CQE0I + * @tc.author: wudongxing + */ +HWTEST_F(DistributedDBCommunicatorTest, OnlineAndOffline002, TestSize.Level1) +{ + /** + * @tc.steps: step1. connect device A with device B + */ + CONNECT_AND_WAIT(200); // Sleep 200 ms + + /** + * @tc.steps: step2. device A alloc communicator AA using label A and register callback + * @tc.expected: step2. no callback. + */ + int errorNo = E_OK; + ICommunicator *commAA = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(commAA); + OnOfflineDevice onlineForAA; + REG_CONNECT_CALLBACK(commAA, onlineForAA); + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(0)); + + /** + * @tc.steps: step3. device B alloc communicator BB using label B and register callback + * @tc.expected: step3. no callback. + */ + ICommunicator *commBB = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_B, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(commBB); + OnOfflineDevice onlineForBB; + REG_CONNECT_CALLBACK(commBB, onlineForBB); + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(0)); + + /** + * @tc.steps: step4. device B alloc communicator BA using label A and register callback + * @tc.expected: step4. communicator AA has callback of device B online; + * communicator BA has callback of device A online; + * communicator BB no callback. + */ + ICommunicator *commBA = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_A, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(commBA); + OnOfflineDevice onlineForBA; + REG_CONNECT_CALLBACK(commBA, onlineForBA); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(onlineForAA.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForBA.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForAA.latestOnlineDevice, DEVICE_NAME_B); + EXPECT_EQ(onlineForBA.latestOnlineDevice, DEVICE_NAME_A); + + /** + * @tc.steps: step5. device A alloc communicator AB using label B and register callback + * @tc.expected: step5. communicator AB has callback of device B online; + * communicator BB has callback of device A online; + */ + ICommunicator *commAB = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_B, errorNo); + ASSERT_NOT_NULL_AND_ACTIVATE(commAB); + OnOfflineDevice onlineForAB; + REG_CONNECT_CALLBACK(commAB, onlineForAB); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(onlineForAB.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForAB.latestOnlineDevice, DEVICE_NAME_B); + EXPECT_EQ(onlineForBB.latestOnlineDevice, DEVICE_NAME_A); + + /** + * @tc.steps: step6. device A release communicator AA + * @tc.expected: step6. communicator BA has callback of device A offline; + * communicator AB and BB no callback; + */ + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAA); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(onlineForBA.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForAB.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForBA.latestOfflineDevice, DEVICE_NAME_A); + + /** + * @tc.steps: step7. device B release communicator BA + * @tc.expected: step7. communicator AB and BB no callback; + */ + g_envDeviceB.commAggrHandle->ReleaseCommunicator(commBA); + EXPECT_EQ(onlineForAB.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForBB.onlineDevices.size(), static_cast(1)); + + /** + * @tc.steps: step8. device B release communicator BB + * @tc.expected: step8. communicator AB has callback of device B offline; + */ + g_envDeviceB.commAggrHandle->ReleaseCommunicator(commBB); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(onlineForAB.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForAB.latestOfflineDevice, DEVICE_NAME_B); + + // Clean up + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAB); + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: Report Device Connect Change 001 + * @tc.desc: Test CommunicatorAggregator support report device connect change event + * @tc.type: FUNC + * @tc.require: AR000DR9KV + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorTest, ReportDeviceConnectChange001, TestSize.Level1) +{ + /** + * @tc.steps: step1. device A and device B register connect callback to CommunicatorAggregator + */ + OnOfflineDevice onlineForA; + int errCode = g_envDeviceA.commAggrHandle->RegOnConnectCallback( + [&onlineForA](const std::string &target, bool isConnect) { + HandleConnectChange(onlineForA, target, isConnect); + }, nullptr); + EXPECT_EQ(errCode, E_OK); + OnOfflineDevice onlineForB; + errCode = g_envDeviceB.commAggrHandle->RegOnConnectCallback( + [&onlineForB](const std::string &target, bool isConnect) { + HandleConnectChange(onlineForB, target, isConnect); + }, nullptr); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. connect device A with device B + * @tc.expected: step2. device A callback B online; device B callback A online; + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + EXPECT_EQ(onlineForA.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForB.onlineDevices.size(), static_cast(1)); + EXPECT_EQ(onlineForA.latestOnlineDevice, DEVICE_NAME_B); + EXPECT_EQ(onlineForB.latestOnlineDevice, DEVICE_NAME_A); + + /** + * @tc.steps: step3. connect device A with device B + * @tc.expected: step3. device A callback B offline; device B callback A offline; + */ + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + EXPECT_EQ(onlineForA.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForB.onlineDevices.size(), static_cast(0)); + EXPECT_EQ(onlineForA.latestOfflineDevice, DEVICE_NAME_B); + EXPECT_EQ(onlineForB.latestOfflineDevice, DEVICE_NAME_A); + + // Clean up + g_envDeviceA.commAggrHandle->RegOnConnectCallback(nullptr, nullptr); + g_envDeviceB.commAggrHandle->RegOnConnectCallback(nullptr, nullptr); +} + +namespace { +LabelType ToLabelType(uint64_t commLabel) +{ + uint64_t netOrderLabel = HostToNet(commLabel); + uint8_t *eachByte = reinterpret_cast(&netOrderLabel); + std::vector realLabel(COMM_LABEL_LENGTH, 0); + for (int i = 0; i < static_cast(sizeof(uint64_t)); i++) { + realLabel[i] = eachByte[i]; + } + return realLabel; +} +} + +/** + * @tc.name: Report Communicator Not Found 001 + * @tc.desc: Test CommunicatorAggregator support report communicator not found event + * @tc.type: FUNC + * @tc.require: AR000DR9KV + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorTest, ReportCommunicatorNotFound001, TestSize.Level1) +{ + /** + * @tc.steps: step1. device B register communicator not found callback to CommunicatorAggregator + */ + std::vector lackLabels; + int errCode = g_envDeviceB.commAggrHandle->RegCommunicatorLackCallback( + [&lackLabels](const LabelType &commLabel, const std::string &userId)->int { + lackLabels.push_back(commLabel); + return -E_NOT_FOUND; + }, nullptr); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + + /** + * @tc.steps: step3. device A alloc communicator AA using label A and send message to B + * @tc.expected: step3. device B callback that label A not found. + */ + ICommunicator *commAA = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_A, errCode); + ASSERT_NOT_NULL_AND_ACTIVATE(commAA); + Message *msgForAA = BuildRegedTinyMessage(); + ASSERT_NE(msgForAA, nullptr); + SendConfig conf = {true, false, 0}; + errCode = commAA->SendMessage(DEVICE_NAME_B, msgForAA, conf); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + ASSERT_EQ(lackLabels.size(), static_cast(1)); + EXPECT_EQ(lackLabels[0], ToLabelType(LABEL_A)); + + /** + * @tc.steps: step4. device B alloc communicator BA using label A and register message callback + * @tc.expected: step4. communicator BA will not receive message. + */ + ICommunicator *commBA = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_A, errCode); + ASSERT_NE(commBA, nullptr); + Message *recvMsgForBA = nullptr; + commBA->RegOnMessageCallback([&recvMsgForBA](const std::string &srcTarget, Message *inMsg) { + recvMsgForBA = inMsg; + }, nullptr); + commBA->Activate(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + EXPECT_EQ(recvMsgForBA, nullptr); + + // Clean up + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAA); + g_envDeviceB.commAggrHandle->ReleaseCommunicator(commBA); + g_envDeviceB.commAggrHandle->RegCommunicatorLackCallback(nullptr, nullptr); + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +#define DO_SEND_MESSAGE(src, dst, label, session) \ +{ \ + Message *msgFor##src##label = BuildRegedTinyMessage(); \ + ASSERT_NE(msgFor##src##label, nullptr); \ + msgFor##src##label->SetSessionId(session); \ + SendConfig conf = {true, false, 0}; \ + errCode = comm##src##label->SendMessage(DEVICE_NAME_##dst, msgFor##src##label, conf); \ + EXPECT_EQ(errCode, E_OK); \ +} + +#define DO_SEND_GIANT_MESSAGE(src, dst, label, size) \ +{ \ + Message *msgFor##src##label = BuildRegedGiantMessage(size); \ + ASSERT_NE(msgFor##src##label, nullptr); \ + SendConfig conf = {false, false, 0}; \ + errCode = comm##src##label->SendMessage(DEVICE_NAME_##dst, msgFor##src##label, conf); \ + EXPECT_EQ(errCode, E_OK); \ +} + +#define ALLOC_AND_SEND_MESSAGE(src, dst, label, session) \ + ICommunicator *comm##src##label = g_envDevice##src.commAggrHandle->AllocCommunicator(LABEL_##label, errCode); \ + ASSERT_NOT_NULL_AND_ACTIVATE(comm##src##label); \ + DO_SEND_MESSAGE(src, dst, label, session) + +#define REG_MESSAGE_CALLBACK(src, label) \ + string srcTargetFor##src##label; \ + Message *recvMsgFor##src##label = nullptr; \ + comm##src##label->RegOnMessageCallback( \ + [&srcTargetFor##src##label, &recvMsgFor##src##label](const std::string &srcTarget, Message *inMsg) { \ + srcTargetFor##src##label = srcTarget; \ + recvMsgFor##src##label = inMsg; \ + }, nullptr); + +/** + * @tc.name: ReDeliver Message 001 + * @tc.desc: Test CommunicatorAggregator support redeliver message + * @tc.type: FUNC + * @tc.require: AR000DR9KV + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorTest, ReDeliverMessage001, TestSize.Level1) +{ + /** + * @tc.steps: step1. device B register communicator not found callback to CommunicatorAggregator + */ + std::vector lackLabels; + int errCode = g_envDeviceB.commAggrHandle->RegCommunicatorLackCallback( + [&lackLabels](const LabelType &commLabel, const std::string &userId)->int { + lackLabels.push_back(commLabel); + return E_OK; + }, nullptr); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + + /** + * @tc.steps: step3. device A alloc communicator AA using label A and send message to B + * @tc.expected: step3. device B callback that label A not found. + */ + ALLOC_AND_SEND_MESSAGE(A, B, A, 100); // session id 100 + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + ASSERT_EQ(lackLabels.size(), static_cast(1)); + EXPECT_EQ(lackLabels[0], ToLabelType(LABEL_A)); + + /** + * @tc.steps: step4. device A alloc communicator AB using label B and send message to B + * @tc.expected: step4. device B callback that label B not found. + */ + ALLOC_AND_SEND_MESSAGE(A, B, B, 200); // session id 200 + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + ASSERT_EQ(lackLabels.size(), static_cast(2)); + EXPECT_EQ(lackLabels[1], ToLabelType(LABEL_B)); // 1 for second element + + /** + * @tc.steps: step5. device B alloc communicator BA using label A and register message callback + * @tc.expected: step5. communicator BA will receive message. + */ + ICommunicator *commBA = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_A, errCode); + ASSERT_NE(commBA, nullptr); + REG_MESSAGE_CALLBACK(B, A); + commBA->Activate(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + EXPECT_EQ(srcTargetForBA, DEVICE_NAME_A); + ASSERT_NE(recvMsgForBA, nullptr); + EXPECT_EQ(recvMsgForBA->GetSessionId(), 100U); // session id 100 + delete recvMsgForBA; + recvMsgForBA = nullptr; + + /** + * @tc.steps: step6. device B alloc communicator BB using label B and register message callback + * @tc.expected: step6. communicator BB will receive message. + */ + ICommunicator *commBB = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_B, errCode); + ASSERT_NE(commBB, nullptr); + REG_MESSAGE_CALLBACK(B, B); + commBB->Activate(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + EXPECT_EQ(srcTargetForBB, DEVICE_NAME_A); + ASSERT_NE(recvMsgForBB, nullptr); + EXPECT_EQ(recvMsgForBB->GetSessionId(), 200U); // session id 200 + delete recvMsgForBB; + recvMsgForBB = nullptr; + + // Clean up + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAA); + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAB); + g_envDeviceB.commAggrHandle->ReleaseCommunicator(commBA); + g_envDeviceB.commAggrHandle->ReleaseCommunicator(commBB); + g_envDeviceB.commAggrHandle->RegCommunicatorLackCallback(nullptr, nullptr); + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} + +/** + * @tc.name: ReDeliver Message 002 + * @tc.desc: Test CommunicatorAggregator support redeliver message by order + * @tc.type: FUNC + * @tc.require: AR000DR9KV + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorTest, ReDeliverMessage002, TestSize.Level1) +{ + /** + * @tc.steps: step1. device C create CommunicatorAggregator and initialize + */ + bool step1 = SetUpEnv(g_envDeviceC, DEVICE_NAME_C); + ASSERT_EQ(step1, true); + + /** + * @tc.steps: step2. device B register communicator not found callback to CommunicatorAggregator + */ + int errCode = g_envDeviceB.commAggrHandle->RegCommunicatorLackCallback([](const LabelType &commLabel, + const std::string &userId)->int { + return E_OK; + }, nullptr); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step3. connect device A with device B, then device B with device C + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + AdapterStub::ConnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + + /** + * @tc.steps: step4. device A alloc communicator AA using label A and send message to B + */ + ALLOC_AND_SEND_MESSAGE(A, B, A, 100); // session id 100 + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + + /** + * @tc.steps: step5. device C alloc communicator CA using label A and send message to B + */ + ALLOC_AND_SEND_MESSAGE(C, B, A, 200); // session id 200 + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + DO_SEND_MESSAGE(A, B, A, 300); // session id 300 + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + DO_SEND_MESSAGE(C, B, A, 400); // session id 400 + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + + /** + * @tc.steps: step6. device B alloc communicator BA using label A and register message callback + * @tc.expected: step6. communicator BA will receive message in order of sessionid 100, 200, 300, 400. + */ + ICommunicator *commBA = g_envDeviceB.commAggrHandle->AllocCommunicator(LABEL_A, errCode); + ASSERT_NE(commBA, nullptr); + std::vector> msgCallbackForBA; + commBA->RegOnMessageCallback([&msgCallbackForBA](const std::string &srcTarget, Message *inMsg) { + msgCallbackForBA.push_back({srcTarget, inMsg}); + }, nullptr); + commBA->Activate(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + ASSERT_EQ(msgCallbackForBA.size(), static_cast(4)); // total 4 callback + EXPECT_EQ(msgCallbackForBA[0].first, DEVICE_NAME_A); // the 0 order element + EXPECT_EQ(msgCallbackForBA[1].first, DEVICE_NAME_C); // the 1 order element + EXPECT_EQ(msgCallbackForBA[2].first, DEVICE_NAME_A); // the 2 order element + EXPECT_EQ(msgCallbackForBA[3].first, DEVICE_NAME_C); // the 3 order element + for (uint32_t i = 0; i < msgCallbackForBA.size(); i++) { + EXPECT_EQ(msgCallbackForBA[i].second->GetSessionId(), static_cast((i + 1) * 100)); // 1 sessionid 100 + delete msgCallbackForBA[i].second; + msgCallbackForBA[i].second = nullptr; + } + + // Clean up + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAA); + g_envDeviceC.commAggrHandle->ReleaseCommunicator(commCA); + g_envDeviceB.commAggrHandle->ReleaseCommunicator(commBA); + g_envDeviceB.commAggrHandle->RegCommunicatorLackCallback(nullptr, nullptr); + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + AdapterStub::DisconnectAdapterStub(g_envDeviceB.adapterHandle, g_envDeviceC.adapterHandle); + TearDownEnv(g_envDeviceC); +} + +/** + * @tc.name: ReDeliver Message 003 + * @tc.desc: For observe memory in unusual scenario + * @tc.type: FUNC + * @tc.require: AR000DR9KV + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBCommunicatorTest, ReDeliverMessage003, TestSize.Level2) +{ + /** + * @tc.steps: step1. device B register communicator not found callback to CommunicatorAggregator + */ + int errCode = g_envDeviceB.commAggrHandle->RegCommunicatorLackCallback([](const LabelType &commLabel, + const std::string &userId)->int { + return E_OK; + }, nullptr); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. connect device A with device B + */ + AdapterStub::ConnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + + /** + * @tc.steps: step3. device A alloc communicator AA,AB,AC using label A,B,C + */ + ICommunicator *commAA = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_A, errCode); + ASSERT_NOT_NULL_AND_ACTIVATE(commAA); + ICommunicator *commAB = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_B, errCode); + ASSERT_NOT_NULL_AND_ACTIVATE(commAB); + ICommunicator *commAC = g_envDeviceA.commAggrHandle->AllocCommunicator(LABEL_C, errCode); + ASSERT_NOT_NULL_AND_ACTIVATE(commAC); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep 100 ms + + /** + * @tc.steps: step4. device A Continuously send tiny message to B using communicator AA,AB,AC + */ + for (int turn = 0; turn < 11; turn++) { // Total 11 turns + DO_SEND_MESSAGE(A, B, A, 0); + DO_SEND_MESSAGE(A, B, B, 0); + DO_SEND_MESSAGE(A, B, C, 0); + } + + /** + * @tc.steps: step5. device A Continuously send giant message to B using communicator AA,AB,AC + */ + for (int turn = 0; turn < 5; turn++) { // Total 5 turns + DO_SEND_GIANT_MESSAGE(A, B, A, (3 * 1024 * 1024)); // 3 MBytes, 1024 is scale + DO_SEND_GIANT_MESSAGE(A, B, B, (6 * 1024 * 1024)); // 6 MBytes, 1024 is scale + DO_SEND_GIANT_MESSAGE(A, B, C, (7 * 1024 * 1024)); // 7 MBytes, 1024 is scale + } + DO_SEND_GIANT_MESSAGE(A, B, A, (30 * 1024 * 1024)); // 30 MBytes, 1024 is scale + + /** + * @tc.steps: step6. wait a long time then send last frame + */ + for (int sec = 0; sec < 15; sec++) { // Total 15 s + std::this_thread::sleep_for(std::chrono::seconds(1)); // Sleep 1 s + LOGI("[UT][Test][ReDeliverMessage003] Sleep and wait=%d.", sec); + } + DO_SEND_MESSAGE(A, B, A, 0); + std::this_thread::sleep_for(std::chrono::seconds(1)); // Sleep 1 s + + // Clean up + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAA); + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAB); + g_envDeviceA.commAggrHandle->ReleaseCommunicator(commAC); + g_envDeviceB.commAggrHandle->RegCommunicatorLackCallback(nullptr, nullptr); + AdapterStub::DisconnectAdapterStub(g_envDeviceA.adapterHandle, g_envDeviceB.adapterHandle); +} diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_auto_launch_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_auto_launch_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3772f70c34052bbfb9b60d83a2c8cfae213fd2f --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_auto_launch_test.cpp @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "kv_store_delegate_manager.h" +#include "kvdb_manager.h" +#include "kvdb_pragma.h" +#include "runtime_context.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + // define some variables to init a KvStoreDelegateManager object. + const std::string APP_ID1 = "app1"; + const std::string USER_ID1 = "user1"; + const Key KEY_1 = {'K', '1'}; + KvStoreDelegateManager g_mgr(APP_ID1, USER_ID1); + std::string g_testDir; + + constexpr int MAX_AUTO_LAUNCH_NUM = 8; + constexpr uint32_t AUTO_LAUNCH_CYCLE_TIME = 6000; + constexpr uint32_t AUTO_LAUNCH_CHECK_TIME = (AUTO_LAUNCH_CYCLE_TIME / 2) + 500; // 500ms more than half. + constexpr int WAIT_FOR_RESPONSE_TIME = 200; + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvStoreStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvStore = nullptr; + auto g_kvNbDelegateCallback = std::bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + std::placeholders::_1, std::placeholders::_2, std::ref(g_kvStoreStatus), std::ref(g_kvStore)); + + const std::string SCHEMA_DEFINE1 = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"INTEGER, NOT NULL\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\"]}"; + const std::string SCHEMA_DEFINE2 = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name3\":\"INTEGER, NOT NULL\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\"]}"; + class StoreCommunicatorAggregator : public ICommunicatorAggregator { + public: + // Return 0 as success. Return negative as error + int Initialize(IAdapter *inAdapter) override + { + return E_OK; + } + + void Finalize() override + {} + + // If not success, return nullptr and set outErrorNo + ICommunicator *AllocCommunicator(uint64_t commLabel, int &outErrorNo) override + { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + ICommunicator *AllocCommunicator(const LabelType &commLabel, int &outErrorNo) override + { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + + void ReleaseCommunicator(ICommunicator *inCommunicator) override + {} + + int RegCommunicatorLackCallback(const CommunicatorLackCallback &onCommLack, const Finalizer &inOper) override + { + lackCallback_ = onCommLack; + return E_OK; + } + int RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) override + { + return E_OK; + } + + void PutCommLackInfo(const std::string &identifier) const + { + if (lackCallback_) { + std::vector vect(identifier.begin(), identifier.end()); + lackCallback_(vect, USER_ID1); + } + } + + int GetLocalIdentity(std::string &outTarget) const override + { + return E_OK; + } + private: + CommunicatorLackCallback lackCallback_; + }; + + struct AutoLaunchNotifyInfo { + void Reset() + { + triggerTimes = 0; + } + int triggerTimes = 0; + std::string userId; + std::string appId; + std::string storeId; + AutoLaunchStatus status = WRITE_CLOSED; + }; + AutoLaunchNotifyInfo g_autoLaunchNotifyInfo; + + void AutoLaunchNotifierCallback(AutoLaunchNotifyInfo &info, const std::string &userId, const std::string &appId, + const std::string &storeId, AutoLaunchStatus status) + { + info.triggerTimes++; + info.userId = userId; + info.appId = appId; + info.storeId = storeId; + info.status = status; + } + + auto g_autoLaunchNotifyFunc = std::bind(&AutoLaunchNotifierCallback, std::ref(g_autoLaunchNotifyInfo), + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); + + StoreCommunicatorAggregator *g_aggregator = nullptr; +} + +class DistributedDBInterfacesAutoLaunchTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesAutoLaunchTest::SetUpTestCase(void) +{ + LOGI("Start test interface auto launch test"); + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + KvStoreConfig config; + config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(config); + g_aggregator = new (std::nothrow) StoreCommunicatorAggregator; + ASSERT_NE(g_aggregator, nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_aggregator); +} + +void DistributedDBInterfacesAutoLaunchTest::TearDownTestCase(void) +{ + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesAutoLaunchTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvStoreStatus = INVALID_ARGS; + g_kvStore = nullptr; +} + +void DistributedDBInterfacesAutoLaunchTest::TearDown(void) +{ + g_autoLaunchNotifyInfo.Reset(); +} +#if !defined(OMIT_ENCRYPT) && !defined(OMIT_JSON) +/** + * @tc.name: EnableKvStoreAutoLaunch001 + * @tc.desc: Enable the kvstore with the diff parameters. + * @tc.type: FUNC + * @tc.require: AR000DR9KU + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, EnableKvStoreAutoLaunch001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create the kv store with passwd and no schema. + * @tc.expected: step1. Returns a non-null kvstore. + */ + CipherPassword passwd; + std::vector passwdVect = {'p', 's', 'd', '1'}; + passwd.SetValue(passwdVect.data(), passwdVect.size()); + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, passwd, SCHEMA_DEFINE1, false}; + std::string storeId = "test1"; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvStore != nullptr); + EXPECT_TRUE(g_kvStoreStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvStore), OK); + + /** + * @tc.steps: step2. Enable the kv store with different password. + * @tc.expected: step2. Returns INVALID_PASSWD_OR_CORRUPTED_DB. + */ + passwdVect = {'p', 's', 'd', '2'}; + CipherPassword passwdOther; + passwdOther.SetValue(passwdVect.data(), passwdVect.size()); + AutoLaunchOption launchOption = {true, true, CipherType::DEFAULT, passwdOther, "", false, g_testDir, nullptr}; + DBStatus status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, + launchOption, nullptr); + EXPECT_NE(status, OK); + + /** + * @tc.steps: step3. Enable the kv store with different schema. + * @tc.expected: step3. Returns not OK. + */ + launchOption.passwd = passwd; + launchOption.schema = SCHEMA_DEFINE2; + status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, + launchOption, nullptr); + EXPECT_NE(status, OK); + + /** + * @tc.steps: step4. Enable the kv store with correct parameter. + * @tc.expected: step4. Returns OK. + */ + launchOption.passwd = passwd; + launchOption.schema = SCHEMA_DEFINE1; + status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, + launchOption, nullptr); + EXPECT_EQ(status, OK); + KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId); + g_mgr.DeleteKvStore(storeId); +} +#endif +/** + * @tc.name: EnableKvStoreAutoLaunch002 + * @tc.desc: Enable the kv store auto launch for the change of createIfNecessary. + * @tc.type: FUNC + * @tc.require: AR000DR9KU + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, EnableKvStoreAutoLaunch002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Enable the kv store with createIfNecessary is false. + * @tc.expected: step1. Returns not OK. + */ + CipherPassword passwd; + std::string storeId = "test2"; + AutoLaunchOption launchOption = {false, false, CipherType::DEFAULT, passwd, "", false, g_testDir, nullptr}; + DBStatus status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, + launchOption, nullptr); + EXPECT_NE(status, OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), NOT_FOUND); + + /** + * @tc.steps: step2. Enable the kv store with createIfNecessary is true. + * @tc.expected: step2. Returns OK. + */ + launchOption.createIfNecessary = true; + status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, + launchOption, nullptr); + EXPECT_EQ(status, OK); + EXPECT_EQ(KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); +} + +namespace { +IKvDB *GetKvDB(const std::string &storeId) +{ + KvDBProperties prop; + prop.SetStringProp(KvDBProperties::USER_ID, USER_ID1); + prop.SetStringProp(KvDBProperties::APP_ID, APP_ID1); + prop.SetStringProp(KvDBProperties::STORE_ID, storeId); + std::string identifier = DBCommon::TransferHashString(USER_ID1 + "-" + APP_ID1 + "-" + storeId); + + prop.SetStringProp(KvDBProperties::IDENTIFIER_DATA, identifier); + std::string identifierDir = DBCommon::TransferStringToHex(identifier); + prop.SetStringProp(KvDBProperties::IDENTIFIER_DIR, identifierDir); + prop.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + prop.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + prop.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + int errCode = E_OK; + return KvDBManager::OpenDatabase(prop, errCode); +} + +void PutSyncData(const std::string &storeId, const Key &key, const Value &value, bool isCover) +{ + auto kvStore = static_cast(GetKvDB(storeId)); + ASSERT_NE(kvStore, nullptr); + int errCode; + auto *connection = kvStore->GetDBConnection(errCode); + ASSERT_NE(connection, nullptr); + if (kvStore != nullptr) { + std::vector vect; + Timestamp time = 100; // initial valid timestamp. + kvStore->GetMaxTimestamp(time); + if (isCover) { + time += 10; // add the diff for 10. + } else { + time -= 10; // add the diff for -10. + } + vect.push_back({key, value, time, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(kvStore, vect, "deviceB"), E_OK); + } + RefObject::DecObjRef(kvStore); + connection->Close(); + connection = nullptr; +} + +void GetSyncData(const std::string &storeId) +{ + auto kvStore = static_cast(GetKvDB(storeId)); + ASSERT_NE(kvStore, nullptr); + int errCode; + auto *connection = kvStore->GetDBConnection(errCode); + ASSERT_NE(connection, nullptr); + + std::vector entries; + ContinueToken token = nullptr; + DataSizeSpecInfo syncDataSizeInfo = {DBConstant::MAX_VALUE_SIZE, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + kvStore->GetSyncData(0, UINT64_MAX / 2, entries, token, syncDataSizeInfo); // half of the max timestamp. + SingleVerKvEntry::Release(entries); + if (token != nullptr) { + kvStore->ReleaseContinueToken(token); + } + + RefObject::DecObjRef(kvStore); + connection->Close(); + connection = nullptr; +} + +void PrePutDataIntoDatabase(const std::string &storeId) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvStore != nullptr); + EXPECT_TRUE(g_kvStoreStatus == OK); + + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + + EXPECT_EQ(g_kvStore->Put(KEY_1, value), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvStore), OK); +} + +void TriggerAutoLaunch(const std::string &storeId, bool isWriteCovered) +{ + /** + * @tc.steps: step1. Enable the auto launch of the database. + * @tc.expected: step1. Returns OK. + */ + PrePutDataIntoDatabase(storeId); + CipherPassword passwd; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_NE(observer, nullptr); + + AutoLaunchOption launchOption = {true, false, CipherType::DEFAULT, passwd, "", false, g_testDir, observer}; + DBStatus status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, launchOption, + g_autoLaunchNotifyFunc); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step2. Trigger the auto launch of the database. + */ + std::string identifier = DBCommon::TransferHashString(USER_ID1 + "-" + APP_ID1 + "-" + storeId); + g_aggregator->PutCommLackInfo(identifier); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + PutSyncData(storeId, KEY_1, value, isWriteCovered); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + /** + * @tc.steps: step3. Check the notifier and the observer. + */ + if (!isWriteCovered) { + EXPECT_EQ(g_autoLaunchNotifyInfo.triggerTimes, 0); + } else { + EXPECT_GT(g_autoLaunchNotifyInfo.triggerTimes, 0); + EXPECT_EQ(g_autoLaunchNotifyInfo.status, WRITE_OPENED); + EXPECT_GT(observer->GetCallCount(), 0UL); + } + + EXPECT_EQ(KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + delete observer; + observer = nullptr; + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); +} +} + +/** + * @tc.name: EnableKvStoreAutoLaunch003 + * @tc.desc: test the data change and the notifier of the auto open for no data changed. + * @tc.type: FUNC + * @tc.require: AR000DR9KU + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, EnableKvStoreAutoLaunch003, TestSize.Level2) +{ + /** + * @tc.steps: step1. Enable the auto launch of the database. + * @tc.steps: step2. Trigger the auto launch of the database. + * @tc.steps: step3. Put the data which would be dispatched into the database by sync. + * @tc.steps: step4. Check the notifier and the observer change. + * @tc.expected: step1. Returns OK. + * @tc.expected: step4. The notifier and the observer wouldn't be triggered. + */ + TriggerAutoLaunch("test3", false); +} + +/** + * @tc.name: EnableKvStoreAutoLaunch004 + * @tc.desc: test the data change and the notifier of the auto open for data changed. + * @tc.type: FUNC + * @tc.require: AR000DR9KU + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, EnableKvStoreAutoLaunch004, TestSize.Level2) +{ + /** + * @tc.steps: step1. Enable the auto launch of the database. + * @tc.steps: step2. Trigger the auto launch of the database. + * @tc.steps: step3. Put the data which would overwrite into the database by sync. + * @tc.steps: step4. Check the notifier and the observer change. + * @tc.expected: step1. Returns OK. + * @tc.expected: step4. The notifier and the observer would be triggered. + */ + TriggerAutoLaunch("test4", true); +} + +/** + * @tc.name: EnableKvStoreAutoLaunch005 + * @tc.desc: Test enable the same database twice. + * @tc.type: FUNC + * @tc.require: AR000DR9KU + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, EnableKvStoreAutoLaunch005, TestSize.Level1) +{ + /** + * @tc.steps: step1. Enable the kv store auto launch. + * @tc.expected: step1. Returns OK. + */ + std::string storeId = "test5"; + CipherPassword passwd; + AutoLaunchOption launchOption = {true, false, CipherType::DEFAULT, passwd, "", false, g_testDir, nullptr}; + DBStatus status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, launchOption, + nullptr); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step2. Ee-enable the kv store auto launch. + * @tc.expected: step2. Returns not OK. + */ + status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, launchOption, + nullptr); + EXPECT_NE(status, OK); + EXPECT_EQ(KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); +} + +/** + * @tc.name: EnableKvStoreAutoLaunch005 + * @tc.desc: test the over limits for the enable list. + * @tc.type: FUNC + * @tc.require: AR000DR9KU + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, EnableKvStoreAutoLaunch006, TestSize.Level2) +{ + /** + * @tc.steps: step1. Enable the 8 kv store auto launch. + * @tc.expected: step1. Returns OK. + */ + CipherPassword passwd; + AutoLaunchOption launchOption = {true, false, CipherType::DEFAULT, passwd, "", false, g_testDir, nullptr}; + for (int i = 0; i < MAX_AUTO_LAUNCH_NUM; i++) { + std::string storeId = "store_" + std::to_string(i + 1); + DBStatus status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, + launchOption, nullptr); + EXPECT_EQ(status, OK); + } + + /** + * @tc.steps: step2. Enable the 9th kv store auto launch. + * @tc.expected: step2. Returns OK. + */ + DBStatus status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, "store_9", + launchOption, nullptr); + EXPECT_EQ(status, OVER_MAX_LIMITS); + + /** + * @tc.steps: step3. Disable the 1th kv store auto launch. + * @tc.expected: step3. Returns OK. + */ + EXPECT_EQ(KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, "store_1"), OK); + /** + * @tc.steps: step4. Enable the 9th kv store auto launch. + * @tc.expected: step4. Returns OK. + */ + status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, "store_9", + launchOption, nullptr); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step5. Disable all the kv stores auto launched. + * @tc.expected: step5. Returns OK. + */ + for (int i = 1; i <= MAX_AUTO_LAUNCH_NUM; i++) { + std::string storeId = "store_" + std::to_string(i + 1); + EXPECT_EQ(KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId), OK); + } + /** + * @tc.steps: step6. Disable the kv stores which is not enabled. + * @tc.expected: step6. Returns NOT_FOUND. + */ + EXPECT_EQ(KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, "store_1"), NOT_FOUND); +} + +namespace { +void SetAutoLaunchLifeCycleTime(const std::string &storeId, uint32_t time) +{ + LOGI("SetAutoLifeTime:%u", time); + auto kvStore = static_cast(GetKvDB(storeId)); + ASSERT_NE(kvStore, nullptr); + int errCode; + auto *connection = kvStore->GetDBConnection(errCode); + ASSERT_NE(connection, nullptr); + EXPECT_EQ(connection->Pragma(PRAGMA_SET_AUTO_LIFE_CYCLE, static_cast(&time)), E_OK); + RefObject::DecObjRef(kvStore); + connection->Close(); + connection = nullptr; +} +} +/** + * @tc.name: EnableKvStoreAutoLaunch007 + * @tc.desc: test the over limits for the enable list. + * @tc.type: FUNC + * @tc.require: AR000DR9KU + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, DisableKvStoreAutoLaunch001, TestSize.Level3) +{ + /** + * @tc.steps: step1. Enable the auto launch for 'test7'. + * @tc.expected: step1. Returns OK. + */ + CipherPassword passwd; + AutoLaunchOption launchOption = {true, false, CipherType::DEFAULT, passwd, "", false, g_testDir, nullptr}; + std::string storeId = "test7"; + DBStatus status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, launchOption, + g_autoLaunchNotifyFunc); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. Disable the auto launch for 'test7'. + * @tc.expected: step2. Returns OK. + */ + EXPECT_EQ(KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + /** + * @tc.steps: step3. Trigger the auto launch and check the status of the database. + * @tc.expected: step3. The database was not auto launched. + */ + std::string identifier = DBCommon::TransferHashString(USER_ID1 + "-" + APP_ID1 + "-" + storeId); + g_aggregator->PutCommLackInfo(identifier); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + SetAutoLaunchLifeCycleTime(storeId, AUTO_LAUNCH_CYCLE_TIME); + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + PutSyncData(storeId, KEY_1, value, true); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + EXPECT_EQ(g_autoLaunchNotifyInfo.triggerTimes, 0); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); +} + +/** + * @tc.name: AutoLaunchLifeCycle001 + * @tc.desc: test the auto closed for the database auto launched by the msg. + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, AutoLaunchLifeCycle001, TestSize.Level3) +{ + /** + * @tc.steps: step1. Enable the auto launch for 'test8'. + * @tc.expected: step1. Returns OK. + */ + CipherPassword passwd; + AutoLaunchOption launchOption = {true, false, CipherType::DEFAULT, passwd, "", false, g_testDir, nullptr}; + std::string storeId = "test8"; + DBStatus status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, launchOption, + g_autoLaunchNotifyFunc); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step2. Trigger the auto launch. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + std::string identifier = DBCommon::TransferHashString(USER_ID1 + "-" + APP_ID1 + "-" + storeId); + g_aggregator->PutCommLackInfo(identifier); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + SetAutoLaunchLifeCycleTime(storeId, AUTO_LAUNCH_CYCLE_TIME); + /** + * @tc.steps: step3. Put data into the database by sync. + */ + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + PutSyncData(storeId, KEY_1, value, true); + std::this_thread::sleep_for(std::chrono::milliseconds(AUTO_LAUNCH_CHECK_TIME)); + /** + * @tc.steps: step4. Check the notifier. + * @tc.expected: step4. notifier is triggered for the opened change. + */ + EXPECT_GT(g_autoLaunchNotifyInfo.triggerTimes, 0); + EXPECT_NE(g_mgr.DeleteKvStore(storeId), OK); + g_autoLaunchNotifyInfo.Reset(); + std::this_thread::sleep_for(std::chrono::milliseconds(AUTO_LAUNCH_CHECK_TIME)); + /** + * @tc.steps: step5. Check the notifier for waiting for more than the life time of the auto launched database. + * @tc.expected: step5. notifier is triggered for the closed change. + */ + EXPECT_GT(g_autoLaunchNotifyInfo.triggerTimes, 0); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + + EXPECT_EQ(KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); +} + +namespace { +void DelayAutoLaunchCycle(const std::string &storeId, bool isWrite) +{ + CipherPassword passwd; + AutoLaunchOption launchOption = {true, false, CipherType::DEFAULT, passwd, "", false, g_testDir, nullptr}; + /** + * @tc.steps: step1. Enable the auto launch for 'test8'. + * @tc.expected: step1. Returns OK. + */ + DBStatus status = KvStoreDelegateManager::EnableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId, launchOption, + nullptr); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step2. Trigger the auto launch. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + std::string identifier = DBCommon::TransferHashString(USER_ID1 + "-" + APP_ID1 + "-" + storeId); + g_aggregator->PutCommLackInfo(identifier); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_FOR_RESPONSE_TIME)); + SetAutoLaunchLifeCycleTime(storeId, AUTO_LAUNCH_CYCLE_TIME); + + /** + * @tc.steps: step3. Write/Read the data into/from the database by sync. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(AUTO_LAUNCH_CHECK_TIME)); + EXPECT_NE(g_mgr.DeleteKvStore(storeId), OK); + if (isWrite) { + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + PutSyncData(storeId, KEY_1, value, true); + } else { + GetSyncData(storeId); + } + + /** + * @tc.steps: step5. Check the status of the auto launched database. + * @tc.expected: step5. the life cycle of the auto launched database is prolonged by the sync operation. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(AUTO_LAUNCH_CHECK_TIME)); + EXPECT_NE(g_mgr.DeleteKvStore(storeId), OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(AUTO_LAUNCH_CHECK_TIME)); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + + EXPECT_EQ(KvStoreDelegateManager::DisableKvStoreAutoLaunch(USER_ID1, APP_ID1, storeId), OK); +} +} + +/** + * @tc.name: AutoLaunchLifeCycle002 + * @tc.desc: test the over limits for the enable list. + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, AutoLaunchLifeCycle002, TestSize.Level3) +{ + /** + * @tc.steps: step1. Enable the auto launch for 'test_9'. + * @tc.steps: step2. Trigger the auto launch. + * @tc.steps: step3. Trigger the sync writing operation. + * @tc.steps: step4. Check the status of the auto launched database. + * @tc.expected: step1. Returns OK. + * @tc.expected: step4. The life cycle is prolonged for the writing operation. + */ + DelayAutoLaunchCycle("test_9", true); +} + +/** + * @tc.name: AutoLaunchLifeCycle003 + * @tc.desc: test the life cycle of the auto launched database in the sync reading scene. + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesAutoLaunchTest, AutoLaunchLifeCycle003, TestSize.Level3) +{ + /** + * @tc.steps: step1. Enable the auto launch for 'test_10'. + * @tc.steps: step2. Trigger the auto launch. + * @tc.steps: step3. Trigger the sync reading operation. + * @tc.steps: step4. Check the status of the auto launched database. + * @tc.expected: step1. Returns OK. + * @tc.expected: step4. The life cycle is prolonged for the reading operation. + */ + DelayAutoLaunchCycle("test_10", false); +} diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_syncdb_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_syncdb_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f44a1c9848aa613f6401cc17fd1e21b44f3f187 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_syncdb_test.cpp @@ -0,0 +1,1381 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "db_common.h" +#include "db_errno.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "sqlite_single_ver_natural_store.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const bool LOCAL_ONLY = false; + const string STORE_ID = STORE_ID_SYNC; + const int OBSERVER_SLEEP_TIME = 100; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + // define the g_snapshotDelegateCallback, used to get some information when open a kv snapshot. + DBStatus g_snapshotDelegateStatus = INVALID_ARGS; + KvStoreSnapshotDelegate *g_snapshotDelegatePtr = nullptr; + // the type of g_snapshotDelegateCallback is function + auto g_snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_snapshotDelegateStatus), std::ref(g_snapshotDelegatePtr)); + + // define the g_valueCallback, used to query a value object data from the kvdb. + DBStatus g_valueStatus = INVALID_ARGS; + Value g_value; + // the type of g_valueCallback is function + auto g_valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(g_valueStatus), std::ref(g_value)); + + // define the g_entryVectorCallback, used to query a vector object data from the kvdb. + DBStatus g_entryVectorStatus = INVALID_ARGS; + unsigned long g_matchSize = 0; + std::vector g_entriesVector; + // the type of g_entryVectorCallback is function)> + auto g_entryVectorCallback = bind(&DistributedDBToolsUnitTest::EntryVectorCallback, placeholders::_1, + placeholders::_2, std::ref(g_entryVectorStatus), std::ref(g_matchSize), std::ref(g_entriesVector)); + + const uint32_t MAX_KEY_SIZE = 1024; + const uint32_t MAX_VAL_SIZE = 4194304; + const uint32_t INVALID_KEY_SIZE = 1025; + + Entry g_entryA; + Entry g_entryB; + Entry g_entryC; + Entry g_entryD; + + void GetSnapshotUnitTest() + { + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, g_snapshotDelegateCallback); + EXPECT_TRUE(g_snapshotDelegateStatus == OK); + ASSERT_TRUE(g_snapshotDelegatePtr != nullptr); + } +} + +class DistributedDBInterfacesDataOperationSyncDBTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesDataOperationSyncDBTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesDataOperationSyncDBTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesDataOperationSyncDBTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + // init values. + g_valueStatus = INVALID_ARGS; + g_value.clear(); + g_entryVectorStatus = INVALID_ARGS; + g_matchSize = 0; + + /* + * Here, we create STORE_ID.db before test, + * and it will be closed in TearDown(). + */ + CipherPassword passwd; + KvStoreDelegate::Option option = {true, LOCAL_ONLY, false, CipherType::DEFAULT, passwd}; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); +} + +void DistributedDBInterfacesDataOperationSyncDBTest::TearDown(void) +{ + if (g_kvDelegatePtr != nullptr && g_snapshotDelegatePtr != nullptr) { + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; + } + + if (g_kvDelegatePtr != nullptr) { + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + } + + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID), OK); +} + +/** + * @tc.name: Put001 + * @tc.desc: Put a data(non-empty key, non-empty value) into an exist distributed db + * @tc.type: FUNC + * @tc.require: AR000CQDTM AR000CQS3R + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, Put001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data(non-empty key and non-empty value) into the database. + * @tc.expected: step1. Put returns OK. + */ + Key keyTmp; + keyTmp.push_back(1); + Value valueTmp; + valueTmp.push_back('7'); + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == OK); + /** + * @tc.steps: step2. Get the value according the key through the snapshot. + * @tc.expected: step2. Get returns OK. + */ + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); +} + +/** + * @tc.name: Put002 + * @tc.desc: Put a data(empty key) into an exist distributed db + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, Put002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data(empty key) into the database. + * @tc.expected: step1. Put returns INVALID_ARGS. + */ + Key keyTmp; + Value valueTmp; + valueTmp.push_back('7'); + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == INVALID_ARGS); +} + +/** + * @tc.name: Put003 + * @tc.desc: Put a data(non-empty key, empty value) into an exist distributed db + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, Put003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data(empty value) into the database. + * @tc.expected: step1. Put returns OK. + */ + Key keyTmp; + keyTmp.push_back(1); + Value valueTmp; + + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == OK); +} + +/** + * @tc.name: Put004 + * @tc.desc: Put data into the multiversion database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, Put004, TestSize.Level1) +{ + /** + * @tc.steps: step1. clear the database. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + + Key keyTmp; + keyTmp.push_back(1); + + Value valueTmp; + valueTmp.push_back('7'); + + /** + * @tc.steps: step2. Put one data into the database. + * @tc.expected: step2. Put returns OK. + */ + Value valueTest; + valueTest.push_back('9'); + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == OK); + + /** + * @tc.steps: step3. Get the data from the database. + * @tc.expected: step3. Get returns OK and the read value is equal to the value put before. + */ + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); + EXPECT_TRUE(g_value.size() > 0); + if (g_value.size() > 0) { + EXPECT_TRUE(g_value.front() == '7'); + } + + /** + * @tc.steps: step4. Change the value, and Put the data into the database. + * @tc.expected: step4. Put returns OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTest) == OK); + + if (g_kvDelegatePtr != nullptr && g_snapshotDelegatePtr != nullptr) { + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; + } + GetSnapshotUnitTest(); + /** + * @tc.steps: step5. Get the data from the database. + * @tc.expected: step5. Get returns OK and the read value is equal to the new put value. + */ + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); + EXPECT_TRUE(g_value.size() > 0); + if (g_value.size() > 0) { + EXPECT_TRUE(g_value.front() == '9'); + } +} + +/** + * @tc.name: Clear001 + * @tc.desc: Clear the data from an exist distributed db + * @tc.type: FUNC + * @tc.require: AR000CQDTM AR000CQS3R + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, Clear001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the valid data into the database. + */ + Key keyTmp; + DistributedDBToolsUnitTest::GetRandomKeyValue(keyTmp); + Value valueTmp; + DistributedDBToolsUnitTest::GetRandomKeyValue(valueTmp); + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == OK); + /** + * @tc.steps: step2. Clear the database. + * @tc.expected: step2. Clear returns OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + /** + * @tc.steps: step3. Get the data from the database according the inserted key before clear. + * @tc.expected: step3. Get returns NOT_FOUND. + */ + Key key; + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_EQ(g_valueStatus, NOT_FOUND); +} + +/** + * @tc.name: PutBatch001 + * @tc.desc: Putbatch data into the multiversion database + * @tc.type: FUNC + * @tc.require: AR000CQDTM AR000CQS3R + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, PutBatch001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the prepared data. + */ + vector entries; + for (int i = 1; i < 10; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + } + /** + * @tc.steps: step2. PutBatch the prepared data. + * @tc.expected: step2. PutBatch returns OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + /** + * @tc.steps: step3. Get the data from the database. + * @tc.expected: step3. Get returns OK and the get value is equal to the inserted value before. + */ + GetSnapshotUnitTest(); + for (int i = 1; i < 10; i++) { + Key keyTmp; + keyTmp.push_back(i); + + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); + EXPECT_TRUE(g_value.size() > 0); + if (g_value.size() > 0) { + EXPECT_TRUE(g_value.front() == '8'); + } + } +} + +/** + * @tc.name: PutBatch002 + * @tc.desc: PutBatch modified data into the multiversion database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, PutBatch002, TestSize.Level1) +{ + /** + * @tc.steps: step1. prepare the batch data. + */ + vector entries; + for (int i = 1; i < 10; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('2'); + entries.push_back(entry); + } + /** + * @tc.steps: step2. PutBatch the prepared batch data. + * @tc.expected: step2. PutBatch returns OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + /** + * @tc.steps: step3. Get data from the database according the inserted keys. + * @tc.expected: step3. Get returns OK and the read value is equal to the inserted value. + */ + GetSnapshotUnitTest(); + for (int i = 1; i < 10; i++) { + Key keyTmp; + keyTmp.push_back(i); + + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); + EXPECT_TRUE(g_value.size() > 0); + if (g_value.size() > 0) { + EXPECT_TRUE(g_value.front() == '2'); + } + } +} + +/** + * @tc.name: Delete001 + * @tc.desc: Delete existed data from the multiversion database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, Delete001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the prepared data. + */ + Key keyTmp; + keyTmp.push_back(1); + Value valueTmp; + valueTmp.push_back(3); + EXPECT_EQ(g_kvDelegatePtr->Put(keyTmp, valueTmp), OK); + /** + * @tc.steps: step2. Delete the existed data from the database. + * @tc.expected: step2. Delete returns OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Delete(keyTmp), OK); + /** + * @tc.steps: step3. Get the deleted data from the database. + * @tc.expected: step3. Get returns NOT_FOUND. + */ + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == NOT_FOUND); +} + +/** + * @tc.name: Delete002 + * @tc.desc: Delete non-existed data from the multiversion database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, Delete002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Clear the database. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + + /** + * @tc.steps: step2. Delete the non-existed data from the database. + * @tc.expected: step2. Delete returns OK. + */ + Key keyTmp; + keyTmp.push_back(1); + EXPECT_EQ(g_kvDelegatePtr->Delete(keyTmp), OK); +} + +/** + * @tc.name: DeleteBatch001 + * @tc.desc: Delete the existed batch data from the multiversion database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, DeleteBatch001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the batch data into the database. + */ + vector entries; + for (int i = 1; i < 4; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('2'); + entries.push_back(entry); + } + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + /** + * @tc.steps: step2. Delete the batch data from the database. + * @tc.steps: step2. DeleteBatch returns OK. + */ + vector keys; + for (int i = 1; i < 4; i++) { + Key key; + key.push_back(i); + keys.push_back(key); + } + EXPECT_TRUE(g_kvDelegatePtr->DeleteBatch(keys) == OK); + + /** + * @tc.steps: step3. Get all the data from the database. + * @tc.steps: step3. GetEntries result NOT_FOUND. + */ + Key keyTmp; + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->GetEntries(keyTmp, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, NOT_FOUND); +} + +/** + * @tc.name: DeleteBatch002 + * @tc.desc: Delete the non-existed batch data from the multiversion database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, DeleteBatch002, TestSize.Level1) +{ + /** + * @tc.steps: step1. clear the database. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + /** + * @tc.steps: step2. Delete the batch non-existed data from the database. + * @tc.expected: step2. DeleteBatch returns OK + */ + vector keys; + for (int i = 1; i < 10; i++) { + Key key; + key.push_back(i); + keys.push_back(key); + } + EXPECT_TRUE(g_kvDelegatePtr->DeleteBatch(keys) == OK); +} + +/** + * @tc.name: GetEntries001 + * @tc.desc: Get the batch data from the non-empty database by the prefix key. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetEntries001, TestSize.Level1) +{ + /** + * @tc.steps: step1. insert batch data into the database. + */ + vector entries; + for (int i = 1; i <= 10; i++) { + Entry entry; + for (int j = 1; j <= i; j++) { + entry.key.push_back(j); + } + entry.value.push_back(i); + entries.push_back(entry); + } + + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + Key keyPrefix; + for (int j = 1; j <= 5; j++) { + keyPrefix.push_back(j); + } + /** + * @tc.steps: step2. Get batch data from the database using the prefix key. + * @tc.expected: step2. GetEntries results OK and the result entries size is the match size. + */ + unsigned long matchSize = 6; + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->GetEntries(keyPrefix, g_entryVectorCallback); + ASSERT_TRUE(g_matchSize == matchSize); + EXPECT_TRUE(g_entryVectorStatus == OK); +} + +/** + * @tc.name: GetEntries002 + * @tc.desc: Get all the data(empty prefixkey) from the empty database. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetEntries002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get all the data from the empty database. + * @tc.expected: step1. GetEntries results NOT_FOUND. + */ + Key keyPrefix; + for (int j = 1; j <= 5; j++) { + keyPrefix.push_back('a'); + } + + unsigned long matchSize = 0; + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->GetEntries(keyPrefix, g_entryVectorCallback); + ASSERT_TRUE(g_matchSize == matchSize); + EXPECT_TRUE(g_entryVectorStatus == NOT_FOUND); +} + +/** + * @tc.name: GetEntries003 + * @tc.desc: Get all the data(empty prefixkey) from the database. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetEntries003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put batch data into the database. + */ + vector entries; + const unsigned long entriesSize = 10; + for (unsigned long i = 1; i <= entriesSize; i++) { + Entry entry; + for (unsigned long j = 1; j <= i; j++) { + entry.key.push_back(j); + } + entry.value.push_back(i); + entries.push_back(entry); + } + + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + /** + * @tc.steps: step2. Get all the data from the database using the empty prefix key. + * @tc.expected: step2. GetEntries results OK and the entries size is the put batch data size. + */ + Key keyPrefix; + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->GetEntries(keyPrefix, g_entryVectorCallback); + ASSERT_EQ(g_matchSize, entriesSize); + EXPECT_TRUE(g_entryVectorStatus == OK); +} + +static void TestSnapshotCreateAndRelease() +{ + DBStatus status; + KvStoreSnapshotDelegate *snapshot = nullptr; + KvStoreObserver *observer = nullptr; + auto snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshot)); + + /** + * @tc.steps: step1. Obtain the snapshot object snapshot through + * the GetKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. Returns a non-empty snapshot. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallback); + + EXPECT_TRUE(status == OK); + EXPECT_NE(snapshot, nullptr); + + /** + * @tc.steps: step2. Release the obtained snapshot through + * the ReleaseKvStoreSnapshot interface of the delegate. + * @tc.expected: step2. Release successfully. + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot), OK); +} + +/** + * @tc.name: GetSnapshot001 + * @tc.desc: Get observer is empty, whether you get the snapshot. + * @tc.type: FUNC + * @tc.require: AR000BVRNF AR000CQDTI + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetSnapshot001, TestSize.Level1) +{ + /** + * @tc.steps: step1.Obtain the snapshot object whose observer is null + * by using the GetKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. The obtained snapshot is not empty. + */ + TestSnapshotCreateAndRelease(); +} + +/** + * @tc.name: GetSnapshot002 + * @tc.desc: Get observer is not empty, whether you get the snapshot. + * @tc.type: FUNC + * @tc.require: AR000BVRNF AR000CQDTI + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetSnapshot002, TestSize.Level1) +{ + /** + * @tc.steps: step1.Obtain the snapshot object whose observer is null + * by using the GetKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. The obtained snapshot is not empty. + */ + DBStatus status; + KvStoreSnapshotDelegate *snapshot = nullptr; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_NE(observer, nullptr); + auto snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshot)); + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallback); + + EXPECT_TRUE(status == OK); + EXPECT_NE(snapshot, nullptr); + + /** + * @tc.steps: step2. Release the snapshot get before. + * @tc.expected: step2. ReleaseKvStoreSnapshot returns OK. + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot), OK); + delete observer; + observer = nullptr; +} + +/** + * @tc.name: ReleaseSnapshot001 + * @tc.desc: To test the function of releasing an empty snapshot. + * @tc.type: FUNC + * @tc.require: AR000BVRNF AR000CQDTI + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, ReleaseSnapshot001, TestSize.Level1) +{ + /** + * @tc.steps: step1.Release the null pointer snapshot through + * the ReleaseKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. Return ERROR. + */ + KvStoreSnapshotDelegate *snapshot = nullptr; + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot), DB_ERROR); +} + +/** + * @tc.name: ReleaseSnapshot002 + * @tc.desc: Release the obtained snapshot object that is not empty. + * @tc.type: FUNC + * @tc.require: AR000BVRNF AR000CQDTI + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, ReleaseSnapshot002, TestSize.Level1) +{ + TestSnapshotCreateAndRelease(); +} + +static void TestSnapshotEntryPut() +{ + KvStoreObserverUnitTest *observer = nullptr; + DBStatus status; + KvStoreSnapshotDelegate *snapshotA = nullptr; + auto snapshotDelegateCallbackA = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotA)); + + /** + * @tc.steps: step1.Release the null pointer snapshot through + * the ReleaseKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. Return not empty snapshot. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallbackA); + ASSERT_NE(snapshotA, nullptr); + Key keyA; + Value valueA; + Value valueB; + DistributedDBToolsUnitTest::GetRandomKeyValue(keyA); + DistributedDBToolsUnitTest::GetRandomKeyValue(valueA); + DistributedDBToolsUnitTest::GetRandomKeyValue(valueB); + + /** + * @tc.steps: step2. Obtain the keyA data through the Get interface of the snapshotA. + * @tc.expected: step2. Return NOT_FOUND. + */ + snapshotA->Get(keyA, g_valueCallback); + EXPECT_EQ(g_valueStatus, NOT_FOUND); + + /** + * @tc.steps: step3. Insert the data of keyA and valueA through the Put interface of the delegate. + */ + g_kvDelegatePtr->Put(keyA, valueA); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + KvStoreSnapshotDelegate *snapshotB = nullptr; + auto snapshotDelegateCallbackB = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotB)); + + /** + * @tc.steps: step5. Obtain the snapshot object snapshotB through the GetKvStoreSnapshot + * interface of the delegate. Obtain the keyA data through the Get interface of the snapshotB. + * @tc.expected: step5. Return a non-empty snapshot. The value of keyA is valueA.. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallbackB); + ASSERT_NE(snapshotA, nullptr); + + /** + * @tc.steps: step4. Obtain the keyA data through the Get interface of the snapshotA. + * @tc.expected: step4. Return NOT_FOUND. + */ + snapshotA->Get(keyA, g_valueCallback); + EXPECT_EQ(g_valueStatus, NOT_FOUND); + snapshotB->Get(keyA, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(g_value, valueA), true); + + /** + * @tc.steps: step6. Insert the data of keyA and valueB through the Put interface of the delegate.. + */ + g_kvDelegatePtr->Put(keyA, valueB); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + KvStoreSnapshotDelegate *snapshotC = nullptr; + auto snapshotDelegateCallbackC = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotC)); + + /** + * @tc.steps: step7. Obtain the snapshotC through the GetKvStoreSnapshot interface + * of the delegate and obtain the data of the keyA through the Get interface. + * @tc.expected: step7. Return a non-empty snapshot. The value of keyA is valueB. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallbackC); + ASSERT_NE(snapshotC, nullptr); + + /** + * @tc.steps: step8. Obtain the keyA data through the Get interface of the snapshotB. + * @tc.expected: step8. Return OK, and the value of keyA is valueA.. + */ + snapshotB->Get(keyA, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(g_value, valueA), true); + snapshotC->Get(keyA, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(g_value, valueB), true); + + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotA); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotB); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotC); +} + +static void TestSnapshotEntryDelete() +{ + KvStoreObserverUnitTest *observer = nullptr; + DBStatus status; + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + + g_kvDelegatePtr->Put(key, value); + KvStoreSnapshotDelegate *snapshotA = nullptr; + auto snapshotDelegateCallbackA = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotA)); + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallbackA); + ASSERT_NE(snapshotA, nullptr); + snapshotA->Get(key, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(g_value, value), true); + + /** + * @tc.steps: step9. Delete the keyA data through + * the Delete interface of the delegate. + */ + g_kvDelegatePtr->Delete(key); + KvStoreSnapshotDelegate *snapshotB = nullptr; + auto snapshotDelegateCallbackB = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotB)); + + /** + * @tc.steps:step10 Obtain the snapshot object snapshotB through the GetKvStoreSnapshot interface of the delegate. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallbackB); + ASSERT_NE(snapshotB, nullptr); + + /** + * @tc.steps: step11. Obtain the value of keyA through the Get interface of snapshotB. + * @tc.expected: step11. Return NOT_FOUND. + */ + snapshotB->Get(key, g_valueCallback); + EXPECT_EQ(g_valueStatus, NOT_FOUND); + + /** + * @tc.steps: step12. Obtain the value of keyA through the Get interface of snapshotA. + * @tc.expected: step12. Return OK, the value of keyA is valueB. + */ + snapshotA->Get(key, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(g_value, value), true); + + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotA); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotB); +} + +/** + * @tc.name: get_snapshot_entry_001 + * @tc.desc: Obtain data from the obtained snapshot object and test the impact of the write + * database on the snapshot obtained after the snapshot is obtained. + * @tc.type: FUNC + * @tc.require: AR000BVRNH AR000CQDTJ + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetSnapshotEntry001, TestSize.Level1) +{ + TestSnapshotEntryPut(); + TestSnapshotEntryDelete(); +} + +/** + * @tc.name: get_snapshot_entry_002 + * @tc.desc: Read the data of the invalid key from the obtained snapshot object. + * @tc.type: FUNC + * @tc.require: AR000BVRNH AR000CQDTJ + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetSnapshotEntry002, TestSize.Level1) +{ + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key, MAX_KEY_SIZE); // max key size. + DistributedDBToolsUnitTest::GetRandomKeyValue(value, MAX_VAL_SIZE); // max valueSize; + + /** + * @tc.steps: step1.Insert [keyA, valueA] data through the Put interface of the delegate. + */ + g_kvDelegatePtr->Put(key, value); + KvStoreSnapshotDelegate *snapshot = nullptr; + KvStoreObserverUnitTest *observer = nullptr; + DBStatus status; + auto snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshot)); + + /** + * @tc.steps: step2. Obtain the snapshot object snapshotA through + * the GetKvStoreSnapshot interface of the delegate. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallback); + + snapshot->Get(key, g_valueCallback); + ASSERT_EQ(g_valueStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(g_value, value), true); + + /** + * @tc.steps: step3. Obtain the empty key data through the Get interface of the snapshotA. + * @tc.expected: step3. Return ERROR. + */ + Key keyEmpty; + snapshot->Get(keyEmpty, g_valueCallback); + ASSERT_EQ(g_valueStatus, INVALID_ARGS); + + /** + * @tc.steps: step4. Obtain the data whose key size exceeds 1024 through the Get interface of the snapshotA. + * @tc.expected: step4. Return ERROR. + */ + Key keyMax; + DistributedDBToolsUnitTest::GetRandomKeyValue(keyMax, INVALID_KEY_SIZE); // max add one + snapshot->Get(keyMax, g_valueCallback); + ASSERT_EQ(g_valueStatus, INVALID_ARGS); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot); +} + +static void SnapshotTestPreEntriesPutInner(KvStoreSnapshotDelegate *snapshotA, KvStoreSnapshotDelegate *&snapshotB) +{ + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryA.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryA.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryB.value); + g_entryB.key = g_entryA.key; + + g_entryB.key.push_back(std::rand() % 0xFF); // push back random one. + g_entryC = g_entryB; + uint8_t tmp = (g_entryC.key[0] == 0xFF) ? 0 : 0xFF; + g_entryC.key.insert(g_entryC.key.begin(), tmp); + + /** + * @tc.steps: step2. Obtain the data whose keyPrefix is empty through + * the GetEntries interface of the snapshotA. + * @tc.expected: step2. Return NOT_FOUND. + */ + snapshotA->GetEntries(g_entryA.key, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, NOT_FOUND); + + /** + * @tc.steps: step3. Obtain the data whose keyPrefix is empty through + * the GetEntries interface of the snapshotA. + * @tc.expected: step3. Return NOT_FOUND. + */ + g_kvDelegatePtr->Put(g_entryA.key, g_entryA.value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + g_kvDelegatePtr->Put(g_entryB.key, g_entryB.value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + g_kvDelegatePtr->Put(g_entryC.key, g_entryC.value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + DBStatus status; + auto snapshotDelegateCallbackB = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotB)); + /** + * @tc.steps: step5. Obtain the snapshot object snapshotB + * through the GetKvStoreSnapshot interface of the delegate. + * Obtain the data whose keyPrefix is empty through the GetEntries interface of the snapshotB. + * @tc.expected: step5. Return a non-empty snapshot. GetEntries Obtain data [keyA, valueA], [keyB, valueB]. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackB); + ASSERT_NE(snapshotB, nullptr); +} + +static void SnapshotTestPreEntriesPut() +{ + DBStatus status; + KvStoreSnapshotDelegate *snapshotA = nullptr; + auto snapshotDelegateCallbackA = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotA)); + + /** + * @tc.steps: step1. Obtain the snapshot object snapshotA through + * the GetKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. Returns a non-empty snapsho. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackA); + ASSERT_NE(snapshotA, nullptr); + + KvStoreSnapshotDelegate *snapshotB = nullptr; + SnapshotTestPreEntriesPutInner(snapshotA, snapshotB); + + snapshotA->GetEntries(g_entryA.key, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, NOT_FOUND); + snapshotB->GetEntries(g_entryA.key, g_entryVectorCallback); + EXPECT_EQ(g_matchSize, 2UL); + + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryA, g_entriesVector), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryB, g_entriesVector), true); + + g_entryD = g_entryA; + g_entryD.value.push_back(std::rand() % 0xFF); // random one byte. + + /** + * @tc.steps: step6. Insert [keyA, valueC] data through the Put interface of the delegate. + */ + g_kvDelegatePtr->Put(g_entryD.key, g_entryD.value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + KvStoreSnapshotDelegate *snapshotC = nullptr; + auto snapshotDelegateCallbackC = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotC)); + + /** + * @tc.steps: step7. Obtain the snapshot object snapshotC + * through the GetKvStoreSnapshot interface of the delegate. Obtain the data whose + * keyPrefix is empty through the GetEntries interface of the snapshotC. + * @tc.expected: step5. Return a non-empty snapshot. GetEntries Obtain data [keyA, valueC], [keyB, valueB]. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackC); + ASSERT_NE(snapshotC, nullptr); + + /** + * @tc.steps: step8. Obtain the data whose keyPrefix is empty + * through the GetEntries interface of the snapshotB. + * @tc.expected: step8. Return OK, GetEntries obtains data [keyA, valueA], [keyB, valueB].. + */ + snapshotB->GetEntries(g_entryA.key, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryA, g_entriesVector), true); + snapshotC->GetEntries(g_entryA.key, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryD, g_entriesVector), true); + + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotA); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotB); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotC); +} + +static void SnapshotTestPreEntriesDelete() +{ + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryA.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryA.value); + g_entryB.key = g_entryA.key; + g_entryB.key.push_back(std::rand() % 0xFF); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryB.value); + + g_kvDelegatePtr->Put(g_entryA.key, g_entryA.value); + g_kvDelegatePtr->Put(g_entryB.key, g_entryB.value); + + DBStatus status; + KvStoreSnapshotDelegate *snapshotA = nullptr; + auto snapshotDelegateCallbackA = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotA)); + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackA); + ASSERT_NE(snapshotA, nullptr); + + snapshotA->GetEntries(g_entryA.key, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, OK); + EXPECT_EQ(g_matchSize, 2UL); // entryA and entryB + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryA, g_entriesVector), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryB, g_entriesVector), true); + + /** + * @tc.steps: step9. Delete the keyA data through the Delete interface of the delegate. + */ + g_kvDelegatePtr->Delete(g_entryA.key); + + KvStoreSnapshotDelegate *snapshotB = nullptr; + auto snapshotDelegateCallbackB = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotB)); + + /** + * @tc.steps: step10. Obtain the snapshot object snapshotB + * through the GetKvStoreSnapshot interface of the delegate. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackB); + ASSERT_NE(snapshotB, nullptr); + + /** + * @tc.steps: step11\12. Obtain the value of keyA through the Get interface of snapshotA\B. + */ + snapshotA->GetEntries(g_entryA.key, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, OK); + EXPECT_EQ(g_matchSize, 2UL); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryA, g_entriesVector), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryB, g_entriesVector), true); + + snapshotB->GetEntries(g_entryA.key, g_entryVectorCallback); + EXPECT_EQ(g_matchSize, 1UL); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryA, g_entriesVector), false); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryB, g_entriesVector), true); + + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotA); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotB); +} + +/** + * @tc.name: GetSnapshotEntries001 + * @tc.desc: To test the function of obtaining full data when keyPrefix is set to null. + * @tc.type: FUNC + * @tc.require: AR000BVRNI AR000CQDTK + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetSnapshotEntries001, TestSize.Level1) +{ + std::srand(std::time(nullptr)); + SnapshotTestPreEntriesPut(); + SnapshotTestPreEntriesDelete(); +} + +static void SnapshotTestEmptyPreEntriesPut() +{ + DBStatus status; + Key emptyKey; + KvStoreSnapshotDelegate *snapshotA = nullptr; + auto snapshotDelegateCallbackA = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotA)); + + /** + * @tc.steps: step1.Obtain the snapshot object snapshotA + * through the GetKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. Returns a non-empty snapshot. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackA); + ASSERT_NE(snapshotA, nullptr); + + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryA.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryA.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryB.key, g_entryA.key.size() + 1); // more one + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entryB.value); + + g_kvDelegatePtr->Put(g_entryA.key, g_entryA.value); + g_kvDelegatePtr->Put(g_entryB.key, g_entryB.value); + + /** + * @tc.steps: step2. Obtain the data whose keyPrefix is AB from the GetEntries interface of the snapshotA. + * @tc.expected: step2. Return NOT_FOUND. + */ + snapshotA->GetEntries(emptyKey, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, NOT_FOUND); + + KvStoreSnapshotDelegate *snapshotB = nullptr; + auto snapshotDelegateCallbackB = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotB)); + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackB); + ASSERT_NE(snapshotB, nullptr); + snapshotA->GetEntries(emptyKey, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, NOT_FOUND); + snapshotB->GetEntries(emptyKey, g_entryVectorCallback); + EXPECT_EQ(g_matchSize, 2UL); + + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryA, g_entriesVector), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryB, g_entriesVector), true); + + g_entryC = g_entryA; + g_entryC.value.push_back(std::rand() % 0xFF); // random one byte. + + /** + * @tc.steps: step3.Insert the data of ["AB", valueA], ["AE", valueB], ["ABC", valueC], + * and ["CAB", valueD] through the Put interface of the delegate. + */ + g_kvDelegatePtr->Put(g_entryC.key, g_entryC.value); + KvStoreSnapshotDelegate *snapshotC = nullptr; + auto snapshotDelegateCallbackC = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotC)); + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackC); + ASSERT_NE(snapshotC, nullptr); + + /** + * @tc.steps: step5\6\7\8. Obtain the snapshot object snapshot + * through the GetKvStoreSnapshot interface of the delegate. + * Obtain the data whose keyPrefix is "AB" through the GetEntries interface of the snapshot. + * @tc.expected: step5\6\7\8. Get Returns a non-empty snapshot and data. + */ + snapshotB->GetEntries(emptyKey, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryA, g_entriesVector), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryB, g_entriesVector), true); + snapshotC->GetEntries(emptyKey, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryB, g_entriesVector), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryC, g_entriesVector), true); + + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotA); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotB); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotC); +} + +static void SnapshotTestEmptyPreEntriesDelete() +{ + DBStatus status; + Key emptyKey; + KvStoreSnapshotDelegate *snapshotA = nullptr; + auto snapshotDelegateCallbackA = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotA)); + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackA); + + ASSERT_NE(snapshotA, nullptr); + + /** + * @tc.steps: step9. Delete the "AB" data through the Delete interface of the delegate. + */ + g_kvDelegatePtr->Delete(g_entryC.key); + + /** + * @tc.steps: step11. Obtain the data whose keyPrefix is "AB" through the GetEntries interface of the snapshot. + * @tc.expected: step11. Return OK.get [ABC,valueC]. + */ + snapshotA->GetEntries(emptyKey, g_entryVectorCallback); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryC, g_entriesVector), true); + + KvStoreSnapshotDelegate *snapshotB = nullptr; + auto snapshotDelegateCallbackB = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshotB)); + + /** + * @tc.steps: step10.Obtain the snapshot object snapshot through the GetKvStoreSnapshot interface of the delegate. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallbackB); + ASSERT_NE(snapshotB, nullptr); + + /** + * @tc.steps: step12. Obtain the data whose keyPrefix is "AB" through the GetEntries interface of the snapshot. + * @tc.expected: step12. Return OK. + */ + snapshotB->GetEntries(emptyKey, g_entryVectorCallback); + EXPECT_EQ(g_entryVectorStatus, OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsEntryExist(g_entryC, g_entriesVector), false); + + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotA); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotB); +} + +/** + * @tc.name: GetSnapshotEntries002 + * @tc.desc: To test the function of obtaining the prefix data when keyPrefix is set to a non-empty value. + * @tc.type: FUNC + * @tc.require: AR000BVRNI AR000CQDTK + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetSnapshotEntries002, TestSize.Level1) +{ + std::srand(std::time(nullptr)); + SnapshotTestEmptyPreEntriesPut(); + SnapshotTestEmptyPreEntriesDelete(); +} + +/** + * @tc.name: GetSnapshotEntries003 + * @tc.desc: To test whether data can be obtained when keyPrefix is set to an ultra-long key. + * @tc.type: FUNC + * @tc.require: AR000BVRNI AR000CQDTK + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, GetSnapshotEntries003, TestSize.Level1) +{ + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key, MAX_KEY_SIZE); // max key size. + DistributedDBToolsUnitTest::GetRandomKeyValue(value, MAX_VAL_SIZE); // max valueSize; + + g_kvDelegatePtr->Put(key, value); + KvStoreSnapshotDelegate *snapshot = nullptr; + KvStoreObserverUnitTest *observer = nullptr; + DBStatus status; + auto snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshot)); + + /** + * @tc.steps: step1. Obtain the snapshot object snapshot through the GetKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. Returns a non-empty snapshot. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallback); + + snapshot->GetEntries(key, g_entryVectorCallback); + ASSERT_EQ(g_entryVectorStatus, OK); + EXPECT_EQ(g_matchSize, 1UL); + Entry entry = {key, value}; + DistributedDBToolsUnitTest::IsEntryExist(entry, g_entriesVector); + + /** + * @tc.steps: step2.Obtain the data of the key whose keyPrefix exceeds 1024 bytes + * through the GetEntries interface of the snapshotA. + * @tc.expected: step2. Return ERROR. + */ + Key keyMax; + DistributedDBToolsUnitTest::GetRandomKeyValue(keyMax, INVALID_KEY_SIZE); // max add one + snapshot->GetEntries(keyMax, g_entryVectorCallback); + ASSERT_EQ(g_entryVectorStatus, INVALID_ARGS); + g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot); +} + +/** + * @tc.name: TestTransactionException001 + * @tc.desc: An exception occurred while the test transaction was executing. + * @tc.type: FUNC + * @tc.require: AR000C0F0F AR000CQDTN or SR000BVRNJ + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationSyncDBTest, TransactionException001, TestSize.Level1) +{ + // pre-set: create a db for test. + vector entries, entries1; + + /** + * @tc.steps: step1. Start the transaction, insert the data in bulk, end the transaction, + * insert the data of the data of the key1, the value1, the data of the key2, the value2, the transaction. + */ + entries.push_back(ENTRY_1); + entries.push_back(ENTRY_2); + + EXPECT_EQ(g_kvDelegatePtr->StartTransaction(), OK); + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + EXPECT_TRUE(g_kvDelegatePtr->Commit() == OK); + + /** + * @tc.steps: step2. Start the transaction, insert the data in bulk, end the transaction, + * insert the data of the data of the key3, the value3, the data of the key4, the value4, the transaction. + */ + entries1.push_back(ENTRY_3); + entries1.push_back(ENTRY_4); + + EXPECT_EQ(g_kvDelegatePtr->StartTransaction(), OK); + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries1) == OK); + EXPECT_EQ(g_kvDelegatePtr->Commit(), OK); + + /** + * @tc.steps: step2. Close database and Delegate, + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + + /** + * @tc.steps: step4. Simulated scenes where the log library is not written to complete (power-down): + * read the commit-log database through the sqlite3 interface, delete the latest head node, + * and set the parent node of the header node as the head node. + */ + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + ASSERT_NE(factory, nullptr); + if (factory == nullptr) { + LOGE("failed to get DefaultFactory!"); + return; + } + int result = E_OK; + IKvDBCommitStorage *commitStorage = factory->CreateMultiVerCommitStorage(result); + ASSERT_EQ(result, E_OK); + ASSERT_NE(commitStorage, nullptr); + + std::string origIdentifier = USER_ID + "-" + APP_ID + "-" + STORE_ID_SYNC; + std::string hashIdentifier = DBCommon::TransferHashString(origIdentifier); + std::string hexDir = DBCommon::TransferStringToHex(hashIdentifier); + IKvDBCommitStorage::Property property = {g_testDir, hexDir, false}; + + ASSERT_EQ(commitStorage->Open(property), E_OK); + int errCode = E_OK; + result = commitStorage->RemoveCommit(commitStorage->GetHeader(errCode)); + ASSERT_EQ(result, E_OK); + delete commitStorage; + commitStorage = nullptr; + + /** + * @tc.steps: step5. Get delegate with GetKvStore, create snapshot, get data from key3 and key4. + * @tc.expected: step5. Data on key3 and key4 could not be obtained. + */ + CipherPassword passwd; + KvStoreDelegate::Option option = {true, false, false, CipherType::DEFAULT, passwd}; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, g_snapshotDelegateCallback); + EXPECT_TRUE(g_snapshotDelegateStatus == OK); + ASSERT_TRUE(g_snapshotDelegatePtr != nullptr); + + g_snapshotDelegatePtr->Get(KEY_3, g_valueCallback); + EXPECT_EQ(g_valueStatus, NOT_FOUND); + + g_valueStatus = INVALID_ARGS; + g_snapshotDelegatePtr->Get(KEY_4, g_valueCallback); + EXPECT_EQ(g_valueStatus, NOT_FOUND); +} diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..99f1d4db5e076e8ecb54e5393622316d5961f127 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_test.cpp @@ -0,0 +1,2006 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const bool LOCAL_ONLY = true; + const string STORE_ID = STORE_ID_LOCAL; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatusForQuery = INVALID_ARGS; + KvStoreDelegate *g_kvDelegatePtrForQuery = nullptr; + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallbackForQuery = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatusForQuery), std::ref(g_kvDelegatePtrForQuery)); + KvStoreNbDelegate *g_kvNbDelegatePtrForQuery = nullptr; + auto g_kvNbDelegateCallbackForQuery = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatusForQuery), std::ref(g_kvNbDelegatePtrForQuery)); + + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + // define the g_snapshotDelegateCallback, used to get some information when open a kv snapshot. + DBStatus g_snapshotDelegateStatus = INVALID_ARGS; + KvStoreSnapshotDelegate *g_snapshotDelegatePtr = nullptr; + // the type of g_snapshotDelegateCallback is function + auto g_snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_snapshotDelegateStatus), std::ref(g_snapshotDelegatePtr)); + + // define the g_valueCallback, used to query a value object data from the kvdb. + DBStatus g_valueStatus = INVALID_ARGS; + Value g_value; + // the type of g_valueCallback is function + auto g_valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(g_valueStatus), std::ref(g_value)); + + // define the g_entryVectorCallback, used to query a vector object data from the kvdb. + DBStatus g_entryVectorStatus = INVALID_ARGS; + unsigned long g_matchSize = 0; + std::vector g_entriesVector; + // the type of g_entryVectorCallback is function)> + auto g_entryVectorCallback = bind(&DistributedDBToolsUnitTest::EntryVectorCallback, placeholders::_1, + placeholders::_2, std::ref(g_entryVectorStatus), std::ref(g_matchSize), std::ref(g_entriesVector)); + + const string SCHEMA_STRING = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"BOOL\"," + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, NOT NULL, DEFAULT '3.1415'\"," + "\"field_name7\":\"LONG, DEFAULT 100\"," + "\"field_name8\":\"LONG, DEFAULT 100\"," + "\"field_name9\":\"LONG, DEFAULT 100\"," + "\"field_name10\":\"LONG, DEFAULT 100\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2\"]}"; + + void GetSnapshotUnitTest() + { + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, g_snapshotDelegateCallback); + EXPECT_TRUE(g_snapshotDelegateStatus == OK); + ASSERT_TRUE(g_snapshotDelegatePtr != nullptr); + } +#ifndef OMIT_JSON + const int CIRCLE_COUNT = 3; + void PutValidEntries1() + { + std::string validData = "{\"field_name1\":true,"; + validData += "\"field_name2\":true,"; + validData += "\"field_name3\":10,"; + validData += "\"field_name4\":20,"; + validData += "\"field_name5\":3.14,"; + validData += "\"field_name6\":\"3.1415\","; + validData += "\"field_name7\":100,"; + validData += "\"field_name8\":100,"; + validData += "\"field_name9\":100,"; + validData += "\"field_name10\":100}"; + + Value value(validData.begin(), validData.end()); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(key, value), OK); + } + void PutValidEntries2() + { + std::string validData = "{\"field_name1\":true," + "\"field_name2\":false," + "\"field_name3\":10," + "\"field_name4\":20," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415\"," + "\"field_name7\":100," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; + Value value(validData.begin(), validData.end()); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(key, value), OK); + } + void PutValidEntries3() + { + std::string validData = "{\"field_name1\":true," + "\"field_name2\":true," + "\"field_name3\":20," + "\"field_name4\":20," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415\"," + "\"field_name7\":100," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; + Value value(validData.begin(), validData.end()); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(key, value), OK); + } + void PutValidEntries4() + { + std::string validData = "{\"field_name1\":true," + "\"field_name2\":true," + "\"field_name3\":20," + "\"field_name4\":2," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415\"," + "\"field_name7\":100," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; + Value value(validData.begin(), validData.end()); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(key, value), OK); + } + void PutValidEntries5() + { + std::string validData = "{\"field_name1\":true," + "\"field_name2\":true," + "\"field_name3\":20," + "\"field_name4\":2," + "\"field_name5\":3.15," + "\"field_name6\":\"3.1415\"," + "\"field_name7\":1001," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; + Value value(validData.begin(), validData.end()); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(key, value), OK); + } + void PutValidEntries6() + { + std::string validData = "{\"field_name1\":true," + "\"field_name2\":true," + "\"field_name3\":20," + "\"field_name4\":2," + "\"field_name5\":3.15," + "\"field_name6\":\"4.141\"," + "\"field_name7\":1002," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; + Value value(validData.begin(), validData.end()); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(key, value), OK); + } + void PutValidEntries7() + { + std::string validData = "{\"field_name1\":true," + "\"field_name2\":true," + "\"field_name3\":20," + "\"field_name4\":2," + "\"field_name5\":3.15," + "\"field_name6\":\"4.141\"," + "\"field_name7\":100," + "\"field_name8\":200," + "\"field_name9\":100," + "\"field_name10\":100}"; + Value value(validData.begin(), validData.end()); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(key, value), OK); + } + void PutValidEntries8() + { + std::string validData = "{\"field_name1\":true," + "\"field_name2\":true," + "\"field_name3\":20," + "\"field_name4\":2," + "\"field_name5\":3.15," + "\"field_name6\":\"4.141\"," + "\"field_name7\":1009," + "\"field_name8\":200," + "\"field_name9\":100," + "\"field_name10\":500}"; + Value value(validData.begin(), validData.end()); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(key, value), OK); + } + + void PutValidEntries() + { + for (int i = 0; i < CIRCLE_COUNT; i++) { + PutValidEntries1(); + PutValidEntries2(); + PutValidEntries3(); + PutValidEntries4(); + PutValidEntries5(); + PutValidEntries6(); + PutValidEntries7(); + PutValidEntries8(); + } + } + + const std::string SCHEMA_DEFINE2 = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"INTEGER\"," + "\"field_name2\":\"DOUBLE\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2\"]}"; + + void PresetDataForPreifxAndOrderBy001() + { + std::string validData = "{\"field_name1\":1, \"field_name2\":1}"; + Value value(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_1, value), OK); + validData = "{\"field_name1\":1, \"field_name2\":2}"; + Value value2(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_2, value2), OK); + validData = "{\"field_name1\":2, \"field_name2\":3}"; + Value value3(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_3, value3), OK); + validData = "{\"field_name1\":2, \"field_name2\":4}"; + Value value4(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_4, value4), OK); + validData = "{\"field_name1\":3, \"field_name2\":5}"; + Value value5(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_5, value5), OK); + } +#endif + void TestSnapshotCreateAndRelease() + { + DBStatus status; + KvStoreSnapshotDelegate *snapshot = nullptr; + KvStoreObserver *observer = nullptr; + auto snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshot)); + + /** + * @tc.steps: step1. Obtain the snapshot object snapshot through + * the GetKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. Returns a non-empty snapshot. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallback); + + EXPECT_TRUE(status == OK); + EXPECT_NE(snapshot, nullptr); + + /** + * @tc.steps: step2. Release the obtained snapshot through + * the ReleaseKvStoreSnapshot interface of the delegate. + * @tc.expected: step2. Release successfully. + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot), OK); + } +#ifndef OMIT_JSON + vector PreDataForQueryByPreFixKey() + { + vector res; + for (int i = 0; i < 5; i++) { // rand num 5 for test + Key key = DistributedDBToolsUnitTest::GetRandPrefixKey({'a', 'b'}, 1024); // rand num 1024 for test + std::string validData = "{\"field_name1\":null, \"field_name2\":" + std::to_string(rand()) + "}"; + Value value(validData.begin(), validData.end()); + res.push_back({key, value}); + } + + for (int i = 0; i < 5; i++) { // rand num 5 for test + Key key = DistributedDBToolsUnitTest::GetRandPrefixKey({'a', 'c'}, 1024); // rand num 1024 for test + std::string validData = "{\"field_name1\":null, \"field_name2\":" + std::to_string(rand()) + "}"; + Value value(validData.begin(), validData.end()); + res.push_back({key, value}); + } + return res; + } + + static void PreDataForGroupTest() + { + std::string validData = "{\"field_name1\":1, \"field_name2\":1}"; + Value value(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_1, value), OK); + validData = "{\"field_name1\":2, \"field_name2\":2}"; + Value value2(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_2, value2), OK); + validData = "{\"field_name1\":3, \"field_name2\":3}"; + Value value3(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_3, value3), OK); + validData = "{\"field_name1\":4, \"field_name2\":4}"; + Value value4(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_4, value4), OK); + validData = "{\"field_name1\":5, \"field_name2\":5}"; + Value value5(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_5, value5), OK); + } +#endif +} + +class DistributedDBInterfacesDataOperationTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesDataOperationTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesDataOperationTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesDataOperationTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + // init values. + g_valueStatus = INVALID_ARGS; + g_value.clear(); + g_entryVectorStatus = INVALID_ARGS; + g_matchSize = 0; + + /* + * Here, we create STORE_ID.db before test, + * and it will be closed in TearDown(). + */ + CipherPassword passwd; + KvStoreDelegate::Option option = {true, LOCAL_ONLY, false, CipherType::DEFAULT, passwd}; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); +} + +void DistributedDBInterfacesDataOperationTest::TearDown(void) +{ + if (g_kvDelegatePtr != nullptr && g_snapshotDelegatePtr != nullptr) { + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; + } + + if (g_kvDelegatePtr != nullptr) { + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + } +} + +/** + * @tc.name: Put001 + * @tc.desc: Put a data(non-empty key, non-empty value) into an exist distributed db + * @tc.type: FUNC + * @tc.require: AR000CQDTM AR000CQS3Q + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, Put001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data(non-empty key and non-empty value) into the database. + * @tc.expected: step1. Put returns OK. + */ + Key keyTmp; + keyTmp.push_back(1); + Value valueTmp; + valueTmp.push_back('7'); + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == OK); + + /** + * @tc.steps: step2. Get the value according the key through the snapshot. + * @tc.expected: step2. Get returns OK. + */ + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); +} + +/** + * @tc.name: Put002 + * @tc.desc: Put a data(empty key) into an exist distributed db + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, Put002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data(empty key) into the database. + * @tc.expected: step1. Put returns INVALID_ARGS. + */ + Key keyTmp; + Value valueTmp; + valueTmp.push_back('7'); + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == INVALID_ARGS); +} + +/** + * @tc.name: Put003 + * @tc.desc: Put a data(non-empty key, empty value) into an exist distributed db + * @tc.type: FUNC + * @tc.require: AR000CQDTM AR000CQS3Q + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, Put003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data(empty value) into the database. + * @tc.expected: step1. Put returns OK. + */ + Key keyTmp; + keyTmp.push_back(1); + Value valueTmp; + + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == OK); +} + +/** + * @tc.name: Put004 + * @tc.desc: Put data into the local database + * @tc.type: FUNC + * @tc.require: AR000CQDVD AR000CQS3Q + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, Put004, TestSize.Level1) +{ + /** + * @tc.steps: step1. clear the database. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + + Key keyTmp; + keyTmp.push_back(1); + + Value valueTmp; + valueTmp.push_back('7'); + + /** + * @tc.steps: step2. Put one data into the database. + * @tc.expected: step2. Put returns OK. + */ + Value valueTest; + valueTest.push_back('9'); + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == OK); + + /** + * @tc.steps: step3. Get the data from the database. + * @tc.expected: step3. Get returns OK and the read value is equal to the value put before. + */ + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); + EXPECT_TRUE(g_value.size() > 0); + if (g_value.size() > 0) { + EXPECT_TRUE(g_value.front() == '7'); + } + + /** + * @tc.steps: step4. Change the value, and Put the data into the database. + * @tc.expected: step4. Put returns OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTest) == OK); + + if (g_kvDelegatePtr != nullptr && g_snapshotDelegatePtr != nullptr) { + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; + } + GetSnapshotUnitTest(); + /** + * @tc.steps: step5. Get the data from the database. + * @tc.expected: step5. Get returns OK and the read value is equal to the new put value. + */ + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); + EXPECT_TRUE(g_value.size() > 0); + if (g_value.size() > 0) { + EXPECT_TRUE(g_value.front() == '9'); + } +} + +/** + * @tc.name: Clear001 + * @tc.desc: Clear the data from an exist distributed db + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, Clear001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the valid data into the database. + */ + Key keyTmp; + DistributedDBToolsUnitTest::GetRandomKeyValue(keyTmp); + Value valueTmp; + DistributedDBToolsUnitTest::GetRandomKeyValue(valueTmp); + EXPECT_TRUE(g_kvDelegatePtr->Put(keyTmp, valueTmp) == OK); + + /** + * @tc.steps: step2. Clear the database. + * @tc.expected: step2. Clear returns OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + + /** + * @tc.steps: step3. Get the data from the database according the inserted key before clear. + * @tc.expected: step3. Get returns NOT_FOUND. + */ + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_EQ(g_valueStatus, NOT_FOUND); +} + +/** + * @tc.name: PutBatch001 + * @tc.desc: Putbatch data into the local database + * @tc.type: FUNC + * @tc.require: AR000BVDFE AR000CQDVC + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, PutBatch001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the prepared data. + */ + vector entries; + for (int i = 1; i < 10; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + } + /** + * @tc.steps: step2. PutBatch the prepared data. + * @tc.expected: step2. PutBatch returns OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + /** + * @tc.steps: step3. Get the data from the database. + * @tc.expected: step3. Get returns OK and the get value is equal to the inserted value before. + */ + GetSnapshotUnitTest(); + for (int i = 1; i < 10; i++) { + Key keyTmp; + keyTmp.push_back(i); + + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); + EXPECT_TRUE(g_value.size() > 0); + if (g_value.size() > 0) { + EXPECT_TRUE(g_value.front() == '8'); + } + } +} + +/** + * @tc.name: PutBatch002 + * @tc.desc: PutBatch modified data into the local database + * @tc.type: FUNC + * @tc.require: AR000BVDFE AR000CQDVC + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, PutBatch002, TestSize.Level1) +{ + /** + * @tc.steps: step1. prepare the batch data. + */ + vector entries; + for (int i = 1; i < 10; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('2'); + entries.push_back(entry); + } + /** + * @tc.steps: step2. PutBatch the prepared batch data. + * @tc.expected: step2. PutBatch returns OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + /** + * @tc.steps: step3. Get data from the database according the inserted keys. + * @tc.expected: step3. Get returns OK and the read value is equal to the inserted value. + */ + GetSnapshotUnitTest(); + for (int i = 1; i < 10; i++) { + Key keyTmp; + keyTmp.push_back(i); + + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == OK); + EXPECT_TRUE(g_value.size() > 0); + if (g_value.size() > 0) { + EXPECT_TRUE(g_value.front() == '2'); + } + } +} + +/** + * @tc.name: Delete001 + * @tc.desc: Delete existed data from the local database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, Delete001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the prepared data. + */ + Key keyTmp; + keyTmp.push_back(1); + Value valueTmp; + valueTmp.push_back(3); + EXPECT_EQ(g_kvDelegatePtr->Put(keyTmp, valueTmp), OK); + /** + * @tc.steps: step2. Delete the existed data from the database. + * @tc.expected: step2. Delete returns OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Delete(keyTmp), OK); + /** + * @tc.steps: step3. Get the deleted data from the database. + * @tc.expected: step3. Get returns NOT_FOUND. + */ + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == NOT_FOUND); +} + +/** + * @tc.name: Delete002 + * @tc.desc: Delete non-existed data from the local database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, Delete002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Clear the database. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + + /** + * @tc.steps: step2. Delete the non-existed data from the database. + * @tc.expected: step2. Delete returns OK. + */ + Key keyTmp; + keyTmp.push_back(1); + EXPECT_TRUE(g_kvDelegatePtr->Delete(keyTmp) == OK); +} + +/** + * @tc.name: DeleteBatch001 + * @tc.desc: Delete the existed batch data from the local database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, DeleteBatch001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the batch data into the database. + */ + vector entries; + for (int i = 1; i < 4; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('2'); + entries.push_back(entry); + } + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + /** + * @tc.steps: step2. Delete the batch data from the database. + * @tc.steps: step2. DeleteBatch returns OK. + */ + vector keys; + for (int i = 1; i < 4; i++) { + Key key; + key.push_back(i); + keys.push_back(key); + } + EXPECT_TRUE(g_kvDelegatePtr->DeleteBatch(keys) == OK); + + /** + * @tc.steps: step3. Get all the data from the database. + * @tc.steps: step3. GetEntries result NOT_FOUND. + */ + Key keyTmp; + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->Get(keyTmp, g_valueCallback); + EXPECT_TRUE(g_valueStatus == INVALID_ARGS); +} + +/** + * @tc.name: DeleteBatch002 + * @tc.desc: Delete the non-existed batch data from the local database + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, DeleteBatch002, TestSize.Level1) +{ + /** + * @tc.steps: step1. clear the database. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + /** + * @tc.steps: step2. Delete the batch non-existed data from the database. + * @tc.expected: step2. DeleteBatch returns OK + */ + vector keys; + for (int i = 1; i < 10; i++) { + Key key; + key.push_back(i); + keys.push_back(key); + } + EXPECT_TRUE(g_kvDelegatePtr->DeleteBatch(keys) == OK); +} + +/** + * @tc.name: GetEntries001 + * @tc.desc: Get the batch data from the non-empty database by the prefix key. + * @tc.type: FUNC + * @tc.require: AR000C6TUQ AR000CQDV3 + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, GetEntries001, TestSize.Level1) +{ + /** + * @tc.steps: step1. insert batch data into the database. + */ + vector entries; + for (int i = 1; i <= 10; i++) { + Entry entry; + for (int j = 1; j <= i; j++) { + entry.key.push_back(j); + } + entry.value.push_back(i); + entries.push_back(entry); + } + + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + Key keyPrefix; + for (int j = 1; j <= 5; j++) { + keyPrefix.push_back(j); + } + /** + * @tc.steps: step2. Get batch data from the database using the prefix key. + * @tc.expected: step2. GetEntries results OK and the result entries size is the match size. + */ + unsigned long matchSize = 6; + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->GetEntries(keyPrefix, g_entryVectorCallback); + ASSERT_TRUE(g_matchSize == matchSize); + EXPECT_TRUE(g_entryVectorStatus == OK); +} + +/** + * @tc.name: GetEntries002 + * @tc.desc: Get all the data(empty prefixkey) from the empty database. + * @tc.type: FUNC + * @tc.require: AR000C6TUQ AR000CQDV3 + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, GetEntries002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get all the data from the empty database. + * @tc.expected: step1. GetEntries results NOT_FOUND. + */ + Key keyPrefix; + for (int j = 1; j <= 5; j++) { + keyPrefix.push_back('a'); + } + + unsigned long matchSize = 0; + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->GetEntries(keyPrefix, g_entryVectorCallback); + ASSERT_TRUE(g_matchSize == matchSize); + EXPECT_TRUE(g_entryVectorStatus == NOT_FOUND); +} + +/** + * @tc.name: GetEntries003 + * @tc.desc: Get all the data(empty prefixkey) from the database. + * @tc.type: FUNC + * @tc.require: AR000C6TUQ AR000CQDV3 + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, GetEntries003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put batch data into the database. + */ + vector entries; + for (int i = 1; i <= 10; i++) { + Entry entry; + for (int j = 1; j <= i; j++) { + entry.key.push_back(j); + } + entry.value.push_back(i); + entries.push_back(entry); + } + + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + /** + * @tc.steps: step2. Get all the data from the database using the empty prefix key. + * @tc.expected: step2. GetEntries results OK and the entries size is the put batch data size. + */ + Key keyPrefix; + unsigned long matchSize = 10; + GetSnapshotUnitTest(); + g_snapshotDelegatePtr->GetEntries(keyPrefix, g_entryVectorCallback); + ASSERT_TRUE(g_matchSize == matchSize); + EXPECT_TRUE(g_entryVectorStatus == OK); +} + +/** + * @tc.name: GetSnapshot001 + * @tc.desc: Get observer is empty, whether you get the snapshot. + * @tc.type: FUNC + * @tc.require: AR000BVRNF AR000CQDTI + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, GetSnapshot001, TestSize.Level1) +{ + /** + * @tc.steps: step1.Obtain the snapshot object whose observer is null + * by using the GetKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. The obtained snapshot is not empty. + */ + TestSnapshotCreateAndRelease(); +} + +/** + * @tc.name: GetSnapshot002 + * @tc.desc: Get observer is not empty, whether you get the snapshot. + * @tc.type: FUNC + * @tc.require: AR000BVRNF AR000CQDTI + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, GetSnapshot002, TestSize.Level1) +{ + /** + * @tc.steps: step1.Obtain the snapshot object whose observer is null + * by using the GetKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. The obtained snapshot is not empty. + */ + DBStatus status; + KvStoreSnapshotDelegate *snapshot = nullptr; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_NE(observer, nullptr); + auto snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshot)); + g_kvDelegatePtr->GetKvStoreSnapshot(observer, snapshotDelegateCallback); + + EXPECT_TRUE(status == OK); + EXPECT_NE(snapshot, nullptr); + + /** + * @tc.steps: step2. Release the snapshot get before. + * @tc.expected: step2. ReleaseKvStoreSnapshot returns OK. + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot), OK); + delete observer; + observer = nullptr; +} + +/** + * @tc.name: ReleaseSnapshot001 + * @tc.desc: To test the function of releasing an empty snapshot. + * @tc.type: FUNC + * @tc.require: AR000BVRNF AR000CQDTI + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, ReleaseSnapshot001, TestSize.Level1) +{ + /** + * @tc.steps: step1.Release the null pointer snapshot through + * the ReleaseKvStoreSnapshot interface of the delegate. + * @tc.expected: step1. Return ERROR. + */ + KvStoreSnapshotDelegate *snapshot = nullptr; + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot), DB_ERROR); +} + +/** + * @tc.name: ReleaseSnapshot002 + * @tc.desc: Release the obtained snapshot object that is not empty. + * @tc.type: FUNC + * @tc.require: AR000BVRNF AR000CQDTI + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, ReleaseSnapshot002, TestSize.Level1) +{ + TestSnapshotCreateAndRelease(); +} + +/** + * @tc.name: SetConflictResolutionPolicySuccessTest001 + * @tc.desc: Verify SetConflictResolutionPolicy() return OK with valid input. + * @tc.type: FUNC + * @tc.require: AR000CQE12 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, SetConflictResolutionPolicySuccessTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. get g_kvDelegatePtr pointer + * @tc.expected: step1. g_kvDelegatePtr is not nullptr + */ + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + + /** + * @tc.steps: step2. invoke SetConflictResolutionPolicy() method by g_kvDelegatePtr + * @tc.expected: step2. SetConflictResolutionPolicy() return OK + */ + EXPECT_EQ(g_kvDelegatePtr->SetConflictResolutionPolicy(AUTO_LAST_WIN, nullptr), OK); +} + +/** + * @tc.name: SetConflictResolutionPolicyFailedTest001 + * @tc.desc: Verify SetConflictResolutionPolicy() return INVALID_ARGS with invalid input. + * @tc.type: FUNC + * @tc.require: AR000CQE12 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, SetConflictResolutionPolicyFailedTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. get g_kvDelegatePtr pointer + * @tc.expected: step1. g_kvDelegatePtr is not nullptr + */ + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + + /** + * @tc.steps: step2. invoke SetConflictResolutionPolicy() method by g_kvDelegatePtr + * @tc.expected: step2. SetConflictResolutionPolicy() return not OK + */ + EXPECT_NE(g_kvDelegatePtr->SetConflictResolutionPolicy(CUSTOMER_RESOLUTION, nullptr), OK); +} +#ifndef OMIT_JSON +/** + * @tc.name: GetEntriesWithQuery001 + * @tc.desc: check query_format. + * @tc.type: FUNC + * @tc.require: AR000DR9K7 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, GetEntriesWithQuery001, TestSize.Level1) +{ + /** + * @tc.steps: step1. get a non-schema store, Getentries with query + * @tc.expected: step1. get store ok, Getentries return OK + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("GetEntriesWithQuery001_001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + Query query = Query::Select(); + std::vector entries; + DBStatus ret = g_kvNbDelegatePtrForQuery->GetEntries(query, entries); + EXPECT_EQ(ret, NOT_FOUND); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("GetEntriesWithQuery001_001") == OK); + /** + * @tc.steps: step2. get a schema store, Getentries with empty query + * @tc.expected: step2. get store ok, Getentries return OK + */ + option.schema = SCHEMA_STRING; + g_mgr.GetKvStore("GetEntriesWithQuery001_002", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + ret = g_kvNbDelegatePtrForQuery->GetEntries(query, entries); + EXPECT_TRUE(ret == NOT_FOUND); + /** + * @tc.steps: step3. Getentries by query, query undefined field + * @tc.expected: step3. Getentries return INVALID_QUERY_FIELD + */ + query = query.EqualTo("$.field_name200", 10); + ret = g_kvNbDelegatePtrForQuery->GetEntries(query, entries); + EXPECT_EQ(ret, INVALID_QUERY_FIELD); + /** + * @tc.steps: step4. Getentries by query, query has invalid linker; + * @tc.expected: step4. Getentries return INVALID_QUERY_FORMAT + */ + Query invalidQuery1 = Query::Select().EqualTo("$.field_name1", true).And().Or(); + ret = g_kvNbDelegatePtrForQuery->GetEntries(invalidQuery1, entries); + EXPECT_TRUE(ret == INVALID_QUERY_FORMAT); + /** + * @tc.steps: step5. Getentries by query, query has invalid linker; + * @tc.expected: step5. Getentries return INVALID_QUERY_FORMAT + */ + Query invalidQuery2 = Query::Select().And(); + ret = g_kvNbDelegatePtrForQuery->GetEntries(invalidQuery2, entries); + EXPECT_TRUE(ret == INVALID_QUERY_FORMAT); + /** + * @tc.steps: step6. Getentries by query, query has invalid limit; + * @tc.expected: step6. Getentries return INVALID_QUERY_FORMAT + */ + Query invalidQuery3 = Query::Select().Limit(10, 0).EqualTo("$.field_name1", true); + ret = g_kvNbDelegatePtrForQuery->GetEntries(invalidQuery3, entries); + EXPECT_TRUE(ret == INVALID_QUERY_FORMAT); + /** + * @tc.steps: step7. Getentries by query, query has invalid orderby; + * @tc.expected: step7. Getentries return INVALID_QUERY_FORMAT + */ + Query invalidQuery4 = Query::Select().OrderBy("$.field_name1", true).EqualTo("field_name1", true); + ret = g_kvNbDelegatePtrForQuery->GetEntries(invalidQuery4, entries); + EXPECT_TRUE(ret == INVALID_QUERY_FORMAT); + /** + * @tc.steps: step8. Getentries by query, query has invalid orderby and limit; + * @tc.expected: step8. Getentries return INVALID_QUERY_FORMAT + */ + Query invalidQuery5 = Query::Select().Limit(10, 0).OrderBy("$.field_name1", true); + ret = g_kvNbDelegatePtrForQuery->GetEntries(invalidQuery5, entries); + EXPECT_TRUE(ret == INVALID_QUERY_FORMAT); + /** + * @tc.steps: step9. Getentries by query, query has invalid field type; + * @tc.expected: step9. Getentries return OK + */ + std::string queryType = "true"; + Query invalidQuery6 = Query::Select().EqualTo("$.field_name1", queryType); + ret = g_kvNbDelegatePtrForQuery->GetEntries(invalidQuery6, entries); + EXPECT_TRUE(ret == NOT_FOUND); + + Query invalidQuery7 = Query::Select().EqualTo("$.field_name1", queryType).IsNull("$.field_name1"); + ret = g_kvNbDelegatePtrForQuery->GetEntries(invalidQuery7, entries); + EXPECT_TRUE(ret == INVALID_QUERY_FORMAT); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("GetEntriesWithQuery001_002") == OK); +} + +/** + * @tc.name: GetEntriesWithQuery002 + * @tc.desc: GetEntries(const Query &query, std::vector &entries) interface test. + * @tc.type: FUNC + * @tc.require: AR000DR9K7 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, GetEntriesWithQuery002, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_STRING; + g_mgr.GetKvStore("GetEntriesWithQuery002_002", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + PutValidEntries(); + /** + * @tc.steps: step1. Getentries by query, query is empty; + * @tc.expected: step1. Getentries return OK, entries size == totalSize; + */ + Query query = Query::Select(); + std::vector entries; + int ret = g_kvNbDelegatePtrForQuery->GetEntries(query, entries); + EXPECT_EQ(ret, OK); + EXPECT_EQ(entries.size(), 24ul); + /** + * @tc.steps: step2. Getentries by query, query is full-set; + * @tc.expected: step2. Getentries return OK, ; + */ + std::vector inCondition = {1, 10, 100, 200}; + Query fullQuery = Query::Select().EqualTo("$.field_name1", true).And().NotEqualTo("$.field_name2", false). + And().GreaterThanOrEqualTo("$.field_name3", 10).And().LessThan("$.field_name4", 10).And(). + Like("$.field_name6", "4%").And().In("$.field_name7", inCondition).OrderBy("$.field_name9").Limit(20); + ret = g_kvNbDelegatePtrForQuery->GetEntries(fullQuery, entries); + EXPECT_EQ(ret, OK); + EXPECT_EQ(entries.size(), 3ul); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("GetEntriesWithQuery002_002") == OK); +} + +/** + * @tc.name: GetEntriesWithQuery003 + * @tc.desc: GetEntries(const Query &query, std::vector &entries) interface test. + * @tc.type: FUNC + * @tc.require: AR000DR9K7 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, GetEntriesWithQuery003, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_STRING; + g_mgr.GetKvStore("GetEntriesWithQuery003", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + PutValidEntries(); + /** + * @tc.steps: step1. Getentries by query, query is empty; + * @tc.expected: step1. Getentries return OK, entries size == totalSize; + */ + Query query = Query::Select(); + int count = 0; + int ret = g_kvNbDelegatePtrForQuery->GetCount(query, count); + EXPECT_TRUE(ret == OK); + EXPECT_TRUE(count == 24); + /** + * @tc.steps: step2. Getentries by query, query is full-set; + * @tc.expected: step2. Getentries return OK, ; + */ + std::vector inCondition = {1, 10, 100, 200}; + Query fullQuery = Query::Select().EqualTo("$.field_name1", true).And().NotEqualTo("$.field_name2", false). + And().GreaterThan("$.field_name3", 10).And().LessThan("$.field_name4", 10).And().Like("$.field_name6", "4%"). + And().In("$.field_name7", inCondition); + ret = g_kvNbDelegatePtrForQuery->GetCount(fullQuery, count); + EXPECT_TRUE(ret == OK); + EXPECT_TRUE(count == 3); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("GetEntriesWithQuery003") == OK); +} + +/** + * @tc.name: GetEntriesWithQuery004 + * @tc.desc: GetEntries(const Query &query, std::vector &entries) interface test. + * @tc.type: FUNC + * @tc.require: AR000DR9K7 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, GetEntriesWithQuery004, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_STRING; + g_mgr.GetKvStore("GetEntriesWithQuery004", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + PutValidEntries(); + /** + * @tc.steps: step1. Getentries by query, query is empty; + * @tc.expected: step1. Getentries return OK, entries size == totalSize; + */ + Query query = Query::Select(); + KvStoreResultSet *resultSet = nullptr; + int ret = g_kvNbDelegatePtrForQuery->GetEntries(query, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_TRUE(ret == OK); + EXPECT_EQ(resultSet->GetCount(), 24); + EXPECT_EQ(resultSet->GetPosition(), -1); + EXPECT_TRUE(!resultSet->IsFirst()); + EXPECT_TRUE(resultSet->MoveToFirst()); + EXPECT_TRUE(resultSet->IsFirst()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + /** + * @tc.steps: step2. Getentries by query, query is full-set; + * @tc.expected: step2. Getentries return OK, ; + */ + std::vector inCondition = {1, 10, 100, 200}; + Query fullQuery = Query::Select().EqualTo("$.field_name1", true).And().NotEqualTo("$.field_name2", false). + And().GreaterThan("$.field_name3", 10).And().LessThan("$.field_name4", 10).And().Like("$.field_name6", "4%"). + And().In("$.field_name7", inCondition).OrderBy("$.field_name9").Limit(20, 1); + ret = g_kvNbDelegatePtrForQuery->GetEntries(fullQuery, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_EQ(ret, OK); + EXPECT_EQ(resultSet->GetCount(), 2); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("GetEntriesWithQuery004") == OK); +} + +/** + * @tc.name: QueryIsNotNull001 + * @tc.desc: IsNotNull interface normal function + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, QueryIsNotNull001, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("QueryIsNotNull001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + std::string validData = "{\"field_name1\":null, \"field_name2\":1}"; + Value value(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_1, value), OK); + validData = "{\"field_name1\":2, \"field_name2\":2}"; + Value value2(validData.begin(), validData.end()); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->Put(KEY_2, value2), OK); + /** + * @tc.steps: step1. Get Query object by IsNotNull + */ + Query query = Query::Select().IsNotNull("$.field_name1"); + /** + * @tc.steps: step2. Use GetEntries get KV + * @tc.expected: step2. Getentries return OK, Get K1V1; + */ + std::vector entries; + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entries); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(entries.size(), 1ul); + EXPECT_EQ(entries[0].key, KEY_2); + EXPECT_EQ(entries[0].value, value2); + /** + * @tc.steps: step3. Use GetCount to get number of item + * @tc.expected: step3. Get count = 1; + */ + int count = -1; + g_kvNbDelegatePtrForQuery->GetCount(query, count); + EXPECT_EQ(count, 1); + /** + * @tc.steps: step4. Use GetEntries to get resultSet + * @tc.expected: step4. Getentries return OK, Get K1V1; + */ + KvStoreResultSet *resultSet = nullptr; + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(resultSet->GetCount(), 1); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("QueryIsNotNull001") == OK); +} + +/** + * @tc.name: QueryPreFixKey001 + * @tc.desc: Normal function of query by prefix key + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, QueryPreFixKey001, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("QueryPreFixKey001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + + vector entries = PreDataForQueryByPreFixKey(); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->PutBatch(entries), OK); + + /** + * @tc.steps: step1. Get Query object by PrefixKey ac + */ + Query query = Query::Select().PrefixKey({'a', 'c'}); + + /** + * @tc.steps: step2. Use GetEnties to get same key prefix ac + * @tc.expected: step2. Get count = 5, Key6~10 + */ + std::vector entriesRes; + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entriesRes); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(entriesRes.size(), 5ul); + for (size_t i = 0; i < entriesRes.size(); i++) { + EXPECT_EQ(entriesRes[i].key.front(), 'a'); + EXPECT_EQ(entriesRes[i].key[1], 'c'); + } + /** + * @tc.steps: step3. Use GetCount to get number of item of this query object + * @tc.expected: step3. Get count = 5 + */ + int count = -1; + g_kvNbDelegatePtrForQuery->GetCount(query, count); + EXPECT_EQ(count, 5); + /** + * @tc.steps: step4. Use GetEnties to get same key prefix ac of resultSet + * @tc.expected: step4. Get resultSet of key6~10 + */ + KvStoreResultSet *resultSet = nullptr; + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(resultSet->GetCount(), 5); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + + /** + * @tc.steps: step5. Get Query object by null PrefixKey + */ + Query query1 = Query::Select().PrefixKey({}); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + /** + * @tc.steps: step6. Use GetEnties and GetCount to null key prefix query object + * @tc.expected: step6. Get all KV from database + */ + EXPECT_EQ(errCode, OK); + EXPECT_EQ(entriesRes.size(), 10ul); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(entries, entriesRes, true)); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(resultSet->GetCount(), 10); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + + Query query2 = Query::Select().PrefixKey(Key(1025, 'a')); // 1025 over max key length 1 byte + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query2, entriesRes); + EXPECT_EQ(errCode, INVALID_ARGS); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("QueryPreFixKey001") == OK); +} + +/** + * @tc.name: QueryPreFixKey003 + * @tc.desc: For special key prefix combination condition of query + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, QueryPreFixKey003, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("QueryPreFixKey003", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + + vector entries = PreDataForQueryByPreFixKey(); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->PutBatch(entries), OK); + + /** + * @tc.steps: step1. Get Query object by double PrefixKey + */ + Query query = Query::Select().PrefixKey({'a', 'c'}).PrefixKey({}); + std::vector entriesRes; + /** + * @tc.steps: step2. Use GetEnties for double prefixkey query object + * @tc.expected: step2. return INVALID_QUERY_FORMAT + */ + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entriesRes); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + /** + * @tc.steps: step3. Use GetEnties for double prefixkey query object to get resultSet + * @tc.expected: step3. return INVALID_QUERY_FORMAT + */ + KvStoreResultSet *resultSet = nullptr; + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, resultSet); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + EXPECT_TRUE(resultSet == nullptr); + /** + * @tc.steps: step4. Get Query object by PrefixKey and orderBy + */ + Query query1 = Query::Select().PrefixKey({'a', 'b'}).OrderBy("$.field_name1"); + /** + * @tc.steps: step3. Use GetEnties and GetCount for this query object + * @tc.expected: step3. Can get content by GetEntries, but GetCount can not use for query object include orderBy + */ + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + EXPECT_EQ(entriesRes.size(), 5ul); + int count = -1; + errCode = g_kvNbDelegatePtrForQuery->GetCount(query1, count); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(resultSet->GetCount(), 5); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("QueryPreFixKey003") == OK); +} + +/** + * @tc.name: QueryPreFixKey004 + * @tc.desc: Query a prefix that does not exist + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, QueryPreFixKey004, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("QueryPreFixKey004", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + + vector entries = PreDataForQueryByPreFixKey(); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->PutBatch(entries), OK); + /** + * @tc.steps: step1. Get Query object by PrefixKey that does not exist + */ + Query query = Query::Select().PrefixKey({'c'}); + /** + * @tc.steps: step2. Use GetEnties and GetCount to get result + * @tc.expected: step2. Return NOT_FOUND, get result OK, number of KV is 0 + */ + std::vector entriesRes; + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entriesRes); + EXPECT_EQ(errCode, NOT_FOUND); + + int count = -1; + g_kvNbDelegatePtrForQuery->GetCount(query, count); + EXPECT_EQ(count, 0); + + KvStoreResultSet *resultSet = nullptr; + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(resultSet->GetCount(), 0); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("QueryPreFixKey004") == OK); +} + +/** + * @tc.name: QueryGroup001 + * @tc.desc: Query group nomal ability to change operation priority + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, QueryGroup001, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("QueryGroup001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + + PreDataForGroupTest(); + + /** + * @tc.steps: step1. Get Query object: + * query: <4 and =4 or >1 + * query1: (<4 and =4) or >1 + * query2: <4 and (=4 or >1) + */ + Query query = Query::Select().LessThan("$.field_name1", 4).And().EqualTo("$.field_name1", 4). + Or().GreaterThan("$.field_name1", 1); + Query query1 = Query::Select().BeginGroup().LessThan("$.field_name1", 4).And(). + EqualTo("$.field_name1", 4).EndGroup().Or().GreaterThan("$.field_name1", 1); + Query query2 = Query::Select().LessThan("$.field_name1", 4).And().BeginGroup(). + EqualTo("$.field_name1", 4).Or().GreaterThan("$.field_name1", 1).EndGroup(); + + /** + * @tc.steps: step2. Use GetEnties to get different result + * @tc.expected: step2. Result: + * query: count = 4 + * query1: count = 4 + * query2: count = 2 + */ + std::vector entries; + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entries); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(entries.size(), 4ul); + int count = -1; + g_kvNbDelegatePtrForQuery->GetCount(query, count); + EXPECT_EQ(count, 4); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query2, entries); + EXPECT_EQ(errCode, OK); + + EXPECT_EQ(entries.size(), 2ul); + g_kvNbDelegatePtrForQuery->GetCount(query2, count); + EXPECT_EQ(count, 2); + KvStoreResultSet *resultSet = nullptr; + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query2, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(resultSet->GetCount(), 2); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entries); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(entries.size(), 4ul); + g_kvNbDelegatePtrForQuery->GetCount(query1, count); + EXPECT_EQ(count, 4); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(resultSet->GetCount(), 4); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("QueryGroup001") == OK); +} + +/** + * @tc.name: QueryGroup002 + * @tc.desc: Test for illegal Group query object + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, QueryGroup002, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("QueryGroup002", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + + PreDataForGroupTest(); + + /** + * @tc.steps: step1. Get Query object: + * query: (<4 and (=4 or) >1) + * query1: (<4 and =4 or >1 + * query2: <4 and =4) or >1 + * query3: )<4 and =4( or >1 + * query4: <4 (and = 4 or >1) + */ + Query query = Query::Select().BeginGroup().LessThan("$.field_name1", 4).And().BeginGroup(). + EqualTo("$.field_name1", 4).Or().EndGroup().GreaterThan("$.field_name1", 1).EndGroup(); + Query query1 = Query::Select().BeginGroup().LessThan("$.field_name1", 4).And(). + EqualTo("$.field_name1", 4).Or().GreaterThan("$.field_name1", 1); + Query query2 = Query::Select().LessThan("$.field_name1", 4).And(). + EqualTo("$.field_name1", 4).EndGroup().Or().GreaterThan("$.field_name1", 1); + Query query3 = Query::Select().EndGroup().LessThan("$.field_name1", 4).And(). + EqualTo("$.field_name1", 4).BeginGroup().Or().GreaterThan("$.field_name1", 1); + Query query4 = Query::Select().LessThan("$.field_name1", 4).BeginGroup().And(). + EqualTo("$.field_name1", 4).Or().GreaterThan("$.field_name1", 1).EndGroup(); + + /** + * @tc.steps: step2. Use GetEnties and GetCount to get result + * @tc.expected: step2. All query object is illegal, reeturn INVALID_QUERY_FORMAT + */ + std::vector entries; + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entries); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entries); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query2, entries); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query3, entries); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query4, entries); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + + KvStoreResultSet *resultSet = nullptr; + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, resultSet); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + EXPECT_TRUE(resultSet == nullptr); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, resultSet); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + EXPECT_TRUE(resultSet == nullptr); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query2, resultSet); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + EXPECT_TRUE(resultSet == nullptr); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query3, resultSet); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + EXPECT_TRUE(resultSet == nullptr); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query4, resultSet); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + EXPECT_TRUE(resultSet == nullptr); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("QueryGroup002") == OK); +} + +/** + * @tc.name: QueryGroup003 + * @tc.desc: Query expressions containing nested parentheses + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, QueryGroup003, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("QueryGroup003", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + + PreDataForGroupTest(); + + /** + * @tc.steps: step1. Get Query object for (<=5 and (=4 or >1) and <3) + */ + Query query = Query::Select().BeginGroup().LessThan("$.field_name1", 5).And().BeginGroup(). + EqualTo("$.field_name1", 4).Or().GreaterThan("$.field_name1", 1).EndGroup().And(). + LessThan("$.field_name1", 3).EndGroup(); + + /** + * @tc.steps: step2. Use GetEnties and GetCount to get result + * @tc.expected: step2. reeturn OK, count = 1 + */ + std::vector entries; + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entries); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(entries.size(), 1ul); + int count = -1; + g_kvNbDelegatePtrForQuery->GetCount(query, count); + EXPECT_EQ(count, 1); + + KvStoreResultSet *resultSet = nullptr; + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, resultSet); + ASSERT_TRUE(resultSet != nullptr); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(resultSet->GetCount(), 1); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("QueryGroup003") == OK); +} + +/** + * @tc.name: multiOrderBy001 + * @tc.desc: Test multiple orderby conditions together to query + * @tc.type: FUNC + * @tc.require: AR000DR9K7 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, multiOrderBy001, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("multiOrderBy001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + + PreDataForGroupTest(); + + Query query = Query::Select().PrefixKey({}).OrderBy("$.field_name1").OrderBy("$.field_name1"); + std::vector entries; + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entries); + EXPECT_EQ(errCode, OK); + + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query.Limit(2, 2), entries); + EXPECT_EQ(errCode, OK); + + Query query1 = Query::Select().PrefixKey({}).Limit(2, 2).OrderBy("$.field_name1").OrderBy("$.field_name1"); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entries); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + + Query query2 = Query::Select().PrefixKey({}).OrderBy("$.field_name1").Limit(2, 2).OrderBy("$.field_name1"); + KvStoreResultSet *resultSet = nullptr; + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query2, resultSet); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + + Query query3 = Query::Select().PrefixKey({}).OrderBy("$.field_name1"). + OrderBy("$.field_name1").OrderBy("$.field_name1"); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query3, resultSet); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet), OK); + EXPECT_TRUE(resultSet == nullptr); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("multiOrderBy001") == OK); +} + +/** + * @tc.name: multiOrderBy001 + * @tc.desc: For multiple order query. + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, PreifxAndOrderBy001, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("PreifxAndOrderBy001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + + PresetDataForPreifxAndOrderBy001(); + + Query query = Query::Select().PrefixKey({}).OrderBy("$.field_name1", false); + std::vector entriesRes; + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entriesRes); + EXPECT_EQ(errCode, OK); + ASSERT_EQ(entriesRes.size(), 5ul); + EXPECT_EQ(entriesRes[0].key, KEY_5); + EXPECT_EQ(entriesRes[1].key, KEY_3); + EXPECT_EQ(entriesRes[2].key, KEY_4); + EXPECT_EQ(entriesRes[3].key, KEY_1); + EXPECT_EQ(entriesRes[4].key, KEY_2); + + Query query1 = Query::Select().OrderBy("$.field_name1", false); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + ASSERT_EQ(entriesRes.size(), 5ul); + EXPECT_EQ(entriesRes[0].key, KEY_5); + EXPECT_EQ(entriesRes[1].key, KEY_4); + EXPECT_EQ(entriesRes[2].key, KEY_3); + EXPECT_EQ(entriesRes[3].key, KEY_2); + EXPECT_EQ(entriesRes[4].key, KEY_1); + + Query query2 = Query::Select().PrefixKey({}).OrderBy("$.field_name1", false).OrderBy("$.field_name2", false); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query2, entriesRes); + ASSERT_EQ(entriesRes.size(), 5ul); + EXPECT_EQ(entriesRes[0].key, KEY_5); + EXPECT_EQ(entriesRes[1].key, KEY_4); + EXPECT_EQ(entriesRes[2].key, KEY_3); + EXPECT_EQ(entriesRes[3].key, KEY_2); + EXPECT_EQ(entriesRes[4].key, KEY_1); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("PreifxAndOrderBy001") == OK); +} + +/** + * @tc.name: PrefixAndOther001 + * @tc.desc: Combination of prefix query and logical filtering + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, PrefixAndOther001, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("PrefixAndOther001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_TRUE(g_kvNbDelegatePtrForQuery != nullptr); + EXPECT_TRUE(g_kvDelegateStatusForQuery == OK); + + PresetDataForPreifxAndOrderBy001(); + + std::vector entriesRes; + Query query1 = Query::Select().EqualTo("$.field_name1", 1).PrefixKey({}); + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + EXPECT_EQ(errCode, OK); + query1 = Query::Select().PrefixKey({}).EqualTo("$.field_name1", 1); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + EXPECT_EQ(errCode, OK); + + query1 = Query::Select().EqualTo("$.field_name1", 1).PrefixKey({}).And().EqualTo("$.field_name1", 1); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + EXPECT_EQ(errCode, OK); + + query1 = Query::Select().EqualTo("$.field_name1", 1).PrefixKey({}).And().EqualTo("$.field_name1", 2); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + EXPECT_EQ(errCode, NOT_FOUND); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("PrefixAndOther001") == OK); +} + +/** + * @tc.name: InKeys001 + * @tc.desc: InKeys query base function + * @tc.type: FUNC + * @tc.require: AR000GOH06 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, InKeys001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a database And Preset Data + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("InKeys001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_NE(g_kvNbDelegatePtrForQuery, nullptr); + EXPECT_EQ(g_kvDelegateStatusForQuery, OK); + + Key key = {'1'}; + DBStatus status = g_kvNbDelegatePtrForQuery->Put(key, VALUE_1); + ASSERT_EQ(status, OK); + const int dataSize = 10; // 10 data for test + std::set keys; + for (uint8_t i = 0; i < dataSize; i++) { + key.push_back(i); + DBStatus status = g_kvNbDelegatePtrForQuery->Put(key, VALUE_1); + ASSERT_EQ(status, OK); + keys.emplace(key); + key.pop_back(); + } + + /** + * @tc.steps: step2. Call GetEntries With Query, set all keys at Inkeys. + * @tc.expected: step2. Returns KvStoreResultSet, the count is dataSize, + * all data are equals the preset data + */ + KvStoreResultSet *resultSet = nullptr; + g_kvNbDelegatePtrForQuery->GetEntries(Query::Select().InKeys(keys), resultSet); + ASSERT_NE(resultSet, nullptr); + ASSERT_EQ(resultSet->GetCount(), dataSize); + for (int i = 0; i < dataSize; i++) { + resultSet->MoveToPosition(i); + Entry entry; + resultSet->GetEntry(entry); + key.push_back(i); + EXPECT_EQ(key, entry.key); + EXPECT_EQ(entry.value, VALUE_1); + key.pop_back(); + } + g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet); + + /** + * @tc.steps: step3. Call GetEntries With Query, set one other key at Inkeys. + * @tc.expected: step3. Returns KvStoreResultSet, the count is 0, + */ + g_kvNbDelegatePtrForQuery->GetEntries(Query::Select().InKeys({KEY_7}), resultSet); + ASSERT_NE(resultSet, nullptr); + ASSERT_EQ(resultSet->GetCount(), 0); + g_kvNbDelegatePtrForQuery->CloseResultSet(resultSet); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("InKeys001"), OK); +} + +/** + * @tc.name: InKeysLimit001 + * @tc.desc: InKeys query limit verification + * @tc.type: FUNC + * @tc.require: AR000GOH06 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, InKeysLimit001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a database + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("InKeysLimit001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_NE(g_kvNbDelegatePtrForQuery, nullptr); + EXPECT_EQ(g_kvDelegateStatusForQuery, OK); + + /** + * @tc.steps: step2. Construct a key set, and the key size over MAX_BATCH_SIZE + */ + std::set keys; + for (uint8_t i = 0; i < DBConstant::MAX_BATCH_SIZE + 1; i++) { + Key key = { i }; + keys.emplace(key); + } + + /** + * @tc.steps: step3. Call GetEntries With Query, set keys at Inkeys. + * @tc.expected: step3. Returns OVER_MAX_LIMITS, the resultSet is nullptr, + */ + KvStoreResultSet *resultSet = nullptr; + DBStatus status = g_kvNbDelegatePtrForQuery->GetEntries(Query::Select().InKeys(keys), resultSet); + EXPECT_EQ(status, OVER_MAX_LIMITS); + EXPECT_EQ(resultSet, nullptr); + + /** + * @tc.steps: step4. Call GetEntries With Query, set keys empty. + * @tc.expected: step4. Returns INVALID_ARGS, the resultSet is nullptr, + */ + status = g_kvNbDelegatePtrForQuery->GetEntries(Query::Select().InKeys({}), resultSet); + EXPECT_EQ(status, INVALID_ARGS); + EXPECT_EQ(resultSet, nullptr); + + /** + * @tc.steps: step4. Call GetEntries With Query, set a invalid key. + * @tc.expected: step4. Returns INVALID_ARGS, the resultSet is nullptr, + */ + status = g_kvNbDelegatePtrForQuery->GetEntries(Query::Select().InKeys({{}}), resultSet); + EXPECT_EQ(status, INVALID_ARGS); + EXPECT_EQ(resultSet, nullptr); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("InKeysLimit001"), OK); +} + +/** + * @tc.name: InKeysAndOther001 + * @tc.desc: Combination of InKeys query and logical filtering + * @tc.type: FUNC + * @tc.require: AR000GOH06 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, InKeysAndOther001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a database And Preset Data + */ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("InKeysAndOther001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_NE(g_kvNbDelegatePtrForQuery, nullptr); + EXPECT_EQ(g_kvDelegateStatusForQuery, OK); + + PresetDataForPreifxAndOrderBy001(); + + std::set keys = { KEY_1, KEY_2, KEY_4 }; + std::vector entriesRes; + + /** + * @tc.steps: step2. Call GetEntries With Query, use EqualTo and InKeys + * @tc.expected: step2. Returns OK + */ + Query query1 = Query::Select().EqualTo("$.field_name1", 1).InKeys(keys); + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + EXPECT_EQ(errCode, OK); + + /** + * @tc.steps: step3. Call GetEntries With Query, use InKeys and EqualTo + * @tc.expected: step3. Returns OK + */ + query1 = Query::Select().InKeys(keys).EqualTo("$.field_name1", 1); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + EXPECT_EQ(errCode, OK); + + /** + * @tc.steps: step4. Call GetEntries With Query, use EqualTo, InKeys and EqualTo, all valid + * @tc.expected: step4. Returns OK + */ + query1 = Query::Select().EqualTo("$.field_name1", 1).InKeys(keys).And().EqualTo("$.field_name2", 2); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + EXPECT_EQ(errCode, OK); + + /** + * @tc.steps: step4. Call GetEntries With Query, use EqualTo, InKeys and EqualTo, has invalid + * @tc.expected: step4. Returns NOT_FOUND + */ + query1 = Query::Select().EqualTo("$.field_name1", 1).InKeys(keys).And().EqualTo("$.field_name1", 2); + errCode = g_kvNbDelegatePtrForQuery->GetEntries(query1, entriesRes); + EXPECT_EQ(errCode, NOT_FOUND); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("InKeysAndOther001"), OK); +} + +/** + * @tc.name: InKeysAndOther002 + * @tc.desc: Combination of InKeys query and logical filtering + * @tc.type: FUNC + * @tc.require: AR000GOH06 + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesDataOperationTest, InKeysAndOther002, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_DEFINE2; + g_mgr.GetKvStore("InKeysAndOther001", option, g_kvNbDelegateCallbackForQuery); + ASSERT_NE(g_kvNbDelegatePtrForQuery, nullptr); + EXPECT_EQ(g_kvDelegateStatusForQuery, OK); + + std::set keys = { KEY_1, KEY_2, KEY_4 }; + std::vector queries = { + Query::Select().PrefixKey({}).InKeys(keys).Or().EqualTo("$.field_name1", 2), + Query::Select().PrefixKey({}).InKeys(keys).And().EqualTo("$.field_name1", 2), + Query::Select().InKeys(keys).Or().EqualTo("$.field_name1", 2), + Query::Select().InKeys(keys).And().EqualTo("$.field_name1", 2), + Query::Select().PrefixKey({}).Or().EqualTo("$.field_name1", 2), + Query::Select().PrefixKey({}).And().EqualTo("$.field_name1", 2), + Query::Select().PrefixKey({}).And().InKeys(keys).EqualTo("$.field_name1", 2), + Query::Select().And().InKeys(keys).EqualTo("$.field_name1", 2), + Query::Select().Or().PrefixKey({}).EqualTo("$.field_name1", 2), + Query::Select().BeginGroup().PrefixKey({}).Or().EqualTo("$.field_name1", 2).EndGroup(), + Query::Select().EqualTo("$.field_name1", 2).Or().InKeys(keys), + Query::Select().EqualTo("$.field_name1", 2).Or().PrefixKey({}), + }; + + for (const auto &query : queries) { + std::vector entriesRes; + int errCode = g_kvNbDelegatePtrForQuery->GetEntries(query, entriesRes); + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrForQuery), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("InKeysAndOther001"), OK); +} +#endif // OMIT_JSON \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_value_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_value_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..570d23d8fd831af89b00a7d61f2126daaba2cbde --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_value_test.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "distributeddb_tools_unit_test.h" +#include "data_value.h" + +using namespace testing::ext; +using namespace DistributedDB; + +namespace { +const int ONE_HUNDERED = 100; +const char DEFAULT_CHAR = 'D'; +const std::string DEFAULT_TEXT = "This is a text"; + +void DataValueDefaultNullCheck(DataValue &dataValue) +{ + /** + * @tc.steps: step1. create a dataValue + * @tc.expected: dataValue type is null. + */ + dataValue.ResetValue(); + EXPECT_EQ(dataValue.GetType(), StorageType::STORAGE_TYPE_NULL); +} + +void DataValueDoubleCheck(DataValue &dataValue) +{ + /** + * @tc.steps: step1. create a dataValue and set true + * @tc.expected: dataValue type is double. + */ + double targetDoubleVal = 1.0; + dataValue = targetDoubleVal; + EXPECT_EQ(dataValue.GetType(), StorageType::STORAGE_TYPE_REAL); + /** + * @tc.steps: step2. get a double from dataValue + * @tc.expected: get ok and value is equal to targetDoubleVal. + */ + double val = 0; + EXPECT_EQ(dataValue.GetDouble(val), E_OK); + EXPECT_EQ(val, targetDoubleVal); +} + +void DataValueInt64Check(DataValue &dataValue) +{ + /** + * @tc.steps: step1. create a dataValue and set INTE64_MAX + * @tc.expected: dataValue type is int64. + */ + int64_t targetInt64Val = INT64_MAX; + dataValue = targetInt64Val; + EXPECT_EQ(dataValue.GetType(), StorageType::STORAGE_TYPE_INTEGER); + /** + * @tc.steps: step2. get a int64 from dataValue + * @tc.expected: get ok and value is equal to INTE64_MAX. + */ + int64_t val = INT64_MIN; + EXPECT_EQ(dataValue.GetInt64(val), E_OK); + EXPECT_EQ(val, targetInt64Val); +} + +void DataValueStringCheck(DataValue &dataValue) +{ + /** + * @tc.steps: step1. set a string + * @tc.expected: dataValue type is string. + */ + dataValue.SetText(DEFAULT_TEXT); + EXPECT_EQ(dataValue.GetType(), StorageType::STORAGE_TYPE_TEXT); + /** + * @tc.steps: step2. get a string from dataValue + * @tc.expected: get ok and string is equal to DEFAULT_TEXT. + */ + std::string val; + EXPECT_EQ(dataValue.GetText(val), E_OK); + EXPECT_EQ(val, DEFAULT_TEXT); +} + +void DataValueBlobCheck(DataValue &dataValue) +{ + /** + * @tc.steps: step1. set a blob + * @tc.expected: dataValue type is blob. + */ + Blob targetVal; + std::vector vector(ONE_HUNDERED, DEFAULT_CHAR); + targetVal.WriteBlob(reinterpret_cast(vector.data()), vector.size()); + dataValue.SetBlob(targetVal); + EXPECT_EQ(dataValue.GetType(), StorageType::STORAGE_TYPE_BLOB); + /** + * @tc.steps: step2. get a blob from dataValue + * @tc.expected: get ok and value is equal to DEFAULT_TEXT. + */ + Blob val; + EXPECT_EQ(dataValue.GetBlob(val), E_OK); + for (int i = 0; i < ONE_HUNDERED; i++) { + EXPECT_EQ(targetVal.GetData()[i], val.GetData()[i]); + } +} + +const std::vector g_checkFuncList = { + &DataValueDefaultNullCheck, &DataValueInt64Check, + &DataValueDoubleCheck, &DataValueStringCheck, &DataValueBlobCheck +}; + +class DistributedDBInterfacesDataValueTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesDataValueTest::SetUpTestCase(void) +{ +} + +void DistributedDBInterfacesDataValueTest::TearDownTestCase(void) +{ +} + +void DistributedDBInterfacesDataValueTest::SetUp(void) +{ +} + +void DistributedDBInterfacesDataValueTest::TearDown(void) +{ +} + +/** + * @tc.name: DataValueCheck001 + * @tc.desc: To test dataValue work correctly when data is nullptr, bool, string, double, int64, uint8_t*. + * @tc.type: Func + * @tc.require: + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBInterfacesDataValueTest, DataValueCheck001, TestSize.Level1) +{ + for (const auto &func : g_checkFuncList) { + DataValue dataValue; + func(dataValue); + } +} + +/** + * @tc.name: DataValueCheck002 + * @tc.desc: To test dataValue work correctly when different type overwrite into dataValue. + * @tc.type: Func + * @tc.require: + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBInterfacesDataValueTest, DataValueCheck002, TestSize.Level1) +{ + for (uint32_t lastWriteIndex = 0; lastWriteIndex < g_checkFuncList.size(); lastWriteIndex++) { + DataValue dataValue; + for (uint32_t i = 0; i < g_checkFuncList.size(); i++) { + uint32_t index = (lastWriteIndex + i + 1) % static_cast(g_checkFuncList.size()); + g_checkFuncList[index](dataValue); + } + } +} +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_corrupt_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_corrupt_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..795d7e661c037377ca03ce2804d2c0ebffe29baf --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_corrupt_test.cpp @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "db_common.h" +#include "db_constant.h" +#include "distributeddb_tools_unit_test.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + const std::string APP_NAME = "app"; + const std::string USER_NAME = "account0"; + const int PASSWD_SIZE = 20; + const int WAIT_CALLBACK_TIME = 100; + KvStoreDelegateManager g_mgr(APP_NAME, USER_NAME); + string g_testDir; + KvStoreConfig g_config; + + DBStatus g_kvDelegateStatus = INVALID_ARGS; + DBStatus g_kvNbDelegateStatus = INVALID_ARGS; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + auto g_kvDelegateCallback = std::bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, std::placeholders::_1, + std::placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + auto g_kvNbDelegateCallback = std::bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + std::placeholders::_1, std::placeholders::_2, std::ref(g_kvNbDelegateStatus), std::ref(g_kvNbDelegatePtr)); + + std::string GetKvStoreDirectory(const std::string &storeId, int databaseType) + { + std::string identifier = USER_NAME + "-" + APP_NAME + "-" + storeId; + std::string hashIdentifierName = DBCommon::TransferHashString(identifier); + std::string identifierName = DBCommon::TransferStringToHex(hashIdentifierName); + std::string filePath = g_testDir + "/" + identifierName + "/"; + if (databaseType == DBConstant::DB_TYPE_LOCAL) { // local + filePath += (DBConstant::LOCAL_SUB_DIR + "/" + DBConstant::LOCAL_DATABASE_NAME + + DBConstant::SQLITE_DB_EXTENSION); + } else if (databaseType == DBConstant::DB_TYPE_SINGLE_VER) { // single ver + filePath += (DBConstant::SINGLE_SUB_DIR + "/" + DBConstant::MAINDB_DIR + "/" + + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION); + } else if (databaseType == DBConstant::DB_TYPE_MULTI_VER) { // multi ver + filePath += (DBConstant::MULTI_SUB_DIR + "/" + DBConstant::MULTI_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION); + } else { + filePath = ""; + } + + return filePath; + } + + int PutDataIntoDatabase(KvStoreDelegate *kvDelegate, KvStoreNbDelegate *kvNbDelegate) + { + if (kvDelegate == nullptr && kvNbDelegate == nullptr) { + return DBStatus::DB_ERROR; + } + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + DBStatus status = OK; + if (kvDelegate != nullptr) { + status = kvDelegate->Put(key, value); + if (status != OK) { + return status; + } + } + if (kvNbDelegate != nullptr) { + status = kvNbDelegate->Put(key, value); + if (status != OK) { + return status; + } + } + return status; + } +} + +class DistributedDBInterfacesDatabaseCorruptTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesDatabaseCorruptTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesDatabaseCorruptTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesDatabaseCorruptTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvDelegatePtr = nullptr; +} + +void DistributedDBInterfacesDatabaseCorruptTest::TearDown(void) +{ + g_mgr.SetKvStoreCorruptionHandler(nullptr); +} + +/** + * @tc.name: DatabaseCorruptionHandleTest001 + * @tc.desc: Check the corruption detect without setting the corrupt handler. + * @tc.type: FUNC + * @tc.require: AR000D487C SR000D4878 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseCorruptTest, DatabaseCorruptionHandleTest001, TestSize.Level3) +{ + /** + * @tc.steps: step1. Obtain the kvStore. + * @tc.steps: step2. Put one data into the store. + * @tc.steps: step3. Close the store. + */ + CipherPassword passwd; + Key randomPassword; + DistributedDBToolsUnitTest::GetRandomKeyValue(randomPassword, PASSWD_SIZE); + int errCode = passwd.SetValue(randomPassword.data(), randomPassword.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + KvStoreDelegate::Option option = {true, true, false, CipherType::DEFAULT, passwd}; + g_mgr.GetKvStore("corrupt1", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_EQ(PutDataIntoDatabase(g_kvDelegatePtr, nullptr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + + /** + * @tc.steps: step4. Modify the database file. + */ + std::string filePath = GetKvStoreDirectory("corrupt1", DBConstant::DB_TYPE_LOCAL); // local database. + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath); + + /** + * @tc.steps: step5. Re-obtain the kvStore. + * @tc.expected: step5. Returns null kvstore. + */ + g_mgr.GetKvStore("corrupt1", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_PASSWD_OR_CORRUPTED_DB); + g_mgr.DeleteKvStore("corrupt1"); +} + +/** + * @tc.name: DatabaseCorruptionHandleTest002 + * @tc.desc: Get kv store through different parameters for the same storeID. + * @tc.type: FUNC + * @tc.require: AR000D487C SR000D4878 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseCorruptTest, DatabaseCorruptionHandleTest002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the kvStore. + * @tc.steps: step2. Put data into the store. + * @tc.steps: step3. Close the store. + */ + CipherPassword passwd; + Key randomPassword; + DistributedDBToolsUnitTest::GetRandomKeyValue(randomPassword, PASSWD_SIZE); + int errCode = passwd.SetValue(randomPassword.data(), randomPassword.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + KvStoreDelegate::Option option = {true, false, false, CipherType::DEFAULT, passwd}; + KvStoreNbDelegate::Option nbOption = {true, false, false, CipherType::DEFAULT, passwd}; + + g_mgr.GetKvStore("corrupt2", option, g_kvDelegateCallback); + g_mgr.GetKvStore("corrupt3", nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_EQ(PutDataIntoDatabase(g_kvDelegatePtr, g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_kvNbDelegatePtr = nullptr; + + /** + * @tc.steps: step4. Modify the database file. + */ + std::string filePath = GetKvStoreDirectory("corrupt2", DBConstant::DB_TYPE_MULTI_VER); + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath); + filePath = GetKvStoreDirectory("corrupt3", DBConstant::DB_TYPE_SINGLE_VER); // single ver database. + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath); + KvStoreCorruptInfo corruptInfo; + auto notifier = bind(&KvStoreCorruptInfo::CorruptCallBack, &corruptInfo, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + g_mgr.SetKvStoreCorruptionHandler(notifier); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_CALLBACK_TIME)); + /** + * @tc.steps: step5. Re-obtain the kvStore. + * @tc.expected: step5. Returns null kvstore. + */ + g_mgr.GetKvStore("corrupt2", option, g_kvDelegateCallback); + g_mgr.GetKvStore("corrupt3", nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvDelegateStatus != OK); + ASSERT_TRUE(g_kvNbDelegateStatus != OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_CALLBACK_TIME)); + EXPECT_EQ(corruptInfo.GetDatabaseInfoSize(), 2UL); // 2 callback + EXPECT_EQ(corruptInfo.IsDataBaseCorrupted(APP_NAME, USER_NAME, "corrupt2"), true); + EXPECT_EQ(corruptInfo.IsDataBaseCorrupted(APP_NAME, USER_NAME, "corrupt3"), true); + g_mgr.DeleteKvStore("corrupt2"); + g_mgr.DeleteKvStore("corrupt3"); +} + +/** + * @tc.name: DatabaseCorruptionHandleTest003 + * @tc.desc: Test the CloseKvStore Interface and check whether the database file can be closed. + * @tc.type: FUNC + * @tc.require: AR000D487C SR000D4878 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseCorruptTest, DatabaseCorruptionHandleTest003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the kvStore. + * @tc.steps: step2. Put data into the store. + * @tc.steps: step3. Close the store. + */ + CipherPassword passwd; + Key randomPassword; + DistributedDBToolsUnitTest::GetRandomKeyValue(randomPassword, PASSWD_SIZE); + int errCode = passwd.SetValue(randomPassword.data(), randomPassword.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + KvStoreDelegate::Option option = {true, true, false, CipherType::DEFAULT, passwd}; + KvStoreNbDelegate::Option nbOption = {true, false, false, CipherType::DEFAULT, passwd}; + + g_mgr.GetKvStore("corrupt4", option, g_kvDelegateCallback); + g_mgr.GetKvStore("corrupt5", nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_EQ(PutDataIntoDatabase(g_kvDelegatePtr, g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step4. Modify the database file. + */ + std::string filePath = GetKvStoreDirectory("corrupt4", DBConstant::DB_TYPE_LOCAL); // local database. + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath); + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath + "-wal"); + filePath = GetKvStoreDirectory("corrupt5", DBConstant::DB_TYPE_SINGLE_VER); // single ver database. + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath); + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath + "-wal"); + KvStoreCorruptInfo corruptInfo; + auto notifier = bind(&KvStoreCorruptInfo::CorruptCallBack, &corruptInfo, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + g_mgr.SetKvStoreCorruptionHandler(notifier); + + /** + * @tc.steps: step5. Put data into the kvStore. + * @tc.expected: step5. The corrupt handler is called twice. + */ + ASSERT_NE(PutDataIntoDatabase(g_kvDelegatePtr, nullptr), OK); + ASSERT_NE(PutDataIntoDatabase(nullptr, g_kvNbDelegatePtr), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_CALLBACK_TIME)); + EXPECT_TRUE(corruptInfo.GetDatabaseInfoSize() >= 2UL); // 2 more callback + EXPECT_EQ(corruptInfo.IsDataBaseCorrupted(APP_NAME, USER_NAME, "corrupt4"), true); + EXPECT_EQ(corruptInfo.IsDataBaseCorrupted(APP_NAME, USER_NAME, "corrupt5"), true); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_kvNbDelegatePtr = nullptr; + EXPECT_EQ(g_mgr.DeleteKvStore("corrupt4"), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("corrupt5"), OK); +} + +/** + * @tc.name: DatabaseCorruptionHandleTest004 + * @tc.desc: Test the DeleteKvStore Interface and check whether the database files can be removed. + * @tc.type: FUNC + * @tc.require: AR000D487C SR000D4878 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseCorruptTest, DatabaseCorruptionHandleTest004, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the kvStore. + * @tc.steps: step2. Put data into the store. + * @tc.steps: step3. Close the store. + */ + CipherPassword passwd; + Key randomPassword; + DistributedDBToolsUnitTest::GetRandomKeyValue(randomPassword, PASSWD_SIZE); + int errCode = passwd.SetValue(randomPassword.data(), randomPassword.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + KvStoreDelegate::Option option = {true, true, false, CipherType::DEFAULT, passwd}; + KvStoreNbDelegate::Option nbOption = {true, false, false, CipherType::DEFAULT, passwd}; + + g_mgr.GetKvStore("corrupt6", option, g_kvDelegateCallback); + g_mgr.GetKvStore("corrupt7", nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_EQ(PutDataIntoDatabase(g_kvDelegatePtr, g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step4. Modify the database file. + */ + std::string filePath = GetKvStoreDirectory("corrupt6", DBConstant::DB_TYPE_LOCAL); // local database. + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath); + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath + "-wal"); + filePath = GetKvStoreDirectory("corrupt7", DBConstant::DB_TYPE_SINGLE_VER); // single ver database. + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath); + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath + "-wal"); + KvStoreCorruptInfo corruptInfo; + KvStoreCorruptInfo corruptInfoNew; + auto notifier = bind(&KvStoreCorruptInfo::CorruptCallBack, &corruptInfo, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + g_mgr.SetKvStoreCorruptionHandler(notifier); + auto notifierNew = bind(&KvStoreCorruptInfo::CorruptCallBack, &corruptInfoNew, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + g_mgr.SetKvStoreCorruptionHandler(notifierNew); + /** + * @tc.steps: step5. Re-obtain the kvStore. + * @tc.expected: step5. Returns null kvstore. + */ + ASSERT_NE(PutDataIntoDatabase(g_kvDelegatePtr, nullptr), OK); + ASSERT_NE(PutDataIntoDatabase(nullptr, g_kvNbDelegatePtr), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_CALLBACK_TIME)); + EXPECT_EQ(corruptInfo.GetDatabaseInfoSize(), 0UL); // no callback + EXPECT_TRUE(corruptInfoNew.GetDatabaseInfoSize() >= 2UL); // 2 more callback + EXPECT_EQ(corruptInfoNew.IsDataBaseCorrupted(APP_NAME, USER_NAME, "corrupt6"), true); + EXPECT_EQ(corruptInfoNew.IsDataBaseCorrupted(APP_NAME, USER_NAME, "corrupt7"), true); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_kvNbDelegatePtr = nullptr; + EXPECT_EQ(g_mgr.DeleteKvStore("corrupt6"), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("corrupt7"), OK); +} + +namespace { +const uint32_t MODIFY_SIZE = 12; // Modify size is 12 * sizeof(uint32_t); +const uint32_t MODIFY_VALUE = 0xF3F3F3F3; // random value, make sure to destroy the page header. +void TestDatabaseIntegrityCheckOption(const std::string &storeId, bool isEncrypted) +{ + KvStoreNbDelegate::Option nbOption = {true, false, isEncrypted}; + nbOption.isNeedIntegrityCheck = false; + nbOption.isNeedRmCorruptedDb = false; + if (isEncrypted) { + Key randPassword; + DistributedDBToolsUnitTest::GetRandomKeyValue(randPassword, PASSWD_SIZE); + ASSERT_EQ(nbOption.passwd.SetValue(randPassword.data(), randPassword.size()), CipherPassword::ErrorCode::OK); + } + auto filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + g_mgr.GetKvStore(storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_EQ(g_kvNbDelegateStatus, OK); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_EQ(PutDataIntoDatabase(nullptr, g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step1. Modify the database file header to destroy the header and call the GetKvStore. + * @tc.expected: step1. Returns null kv store and the errCode is INVALID_PASSWD_OR_CORRUPTED_DB. + */ + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath, 0, MODIFY_SIZE, MODIFY_VALUE); + g_mgr.GetKvStore(storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_EQ(g_kvNbDelegateStatus, INVALID_PASSWD_OR_CORRUPTED_DB); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + + /** + * @tc.steps: step2. call the GetKvStore with integrity check option is true. + * @tc.expected: step2. Returns null kv store and the errCode is INVALID_PASSWD_OR_CORRUPTED_DB. + */ + nbOption.isNeedIntegrityCheck = true; + g_mgr.GetKvStore(storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_EQ(g_kvNbDelegateStatus, INVALID_PASSWD_OR_CORRUPTED_DB); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + + /** + * @tc.steps: step3. call the GetKvStore with remove corrupted database option is true. + * @tc.expected: step3. Returns non-null kv store and the errCode is OK. + */ + nbOption.isNeedIntegrityCheck = false; + nbOption.isNeedRmCorruptedDb = true; + g_mgr.GetKvStore(storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + + ASSERT_EQ(PutDataIntoDatabase(nullptr, g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step4. Modify the second page of the database file and Get the kv store. + * @tc.expected: step4. Returns non-null kv store and the errCode is OK(GetKvStore skip the check of the page 2). + */ + size_t filePos = isEncrypted ? 1024 : 4096; // 1024 and 4096 is the page size. + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath, filePos, MODIFY_SIZE, MODIFY_VALUE); + nbOption.isNeedRmCorruptedDb = false; + g_mgr.GetKvStore(storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step5. Get the kv store with check the integrity. + * @tc.expected: step5. Returns null kv store and the errCode is INVALID_PASSWD_OR_CORRUPTED_DB. + */ + nbOption.isNeedIntegrityCheck = true; + g_mgr.GetKvStore(storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_EQ(g_kvNbDelegateStatus, INVALID_PASSWD_OR_CORRUPTED_DB); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + + /** + * @tc.steps: step5. Get the kv store with check the integrity and the rm corrupted database option. + * @tc.expected: step5. Returns non-null kv store and the errCode is OK. + */ + nbOption.isNeedRmCorruptedDb = true; + g_mgr.GetKvStore(storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); +} +} + +/** + * @tc.name: DatabaseIntegrityCheck001 + * @tc.desc: Test the integrity check option. + * @tc.type: FUNC + * @tc.require: AR000D487C SR000D4878 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseCorruptTest, DatabaseIntegrityCheck001, TestSize.Level2) +{ + LOGI("Begin to check the unencrypted database"); + TestDatabaseIntegrityCheckOption("integrity_check001", false); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + LOGI("Begin to check the encrypted database"); + TestDatabaseIntegrityCheckOption("integrity_check002", true); +} + +/** + * @tc.name: DatabaseIntegrityCheck002 + * @tc.desc: Test the integrity check interface. + * @tc.type: FUNC + * @tc.require: AR000D487C SR000D4878 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseCorruptTest, DatabaseIntegrityCheck002, TestSize.Level1) +{ + CipherPassword passwd; + KvStoreNbDelegate::Option nbOption = {true, false, false, CipherType::DEFAULT, passwd}; + nbOption.isNeedIntegrityCheck = true; + nbOption.isNeedRmCorruptedDb = true; + auto filePath = GetKvStoreDirectory("integrity021", DBConstant::DB_TYPE_SINGLE_VER); + for (uint32_t i = 1; i < 4; i++) { + LOGI("%u th test!", i); + g_mgr.GetKvStore("integrity021", nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_EQ(g_kvNbDelegatePtr->CheckIntegrity(), OK); + ASSERT_EQ(PutDataIntoDatabase(nullptr, g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath, i * 4096, MODIFY_SIZE, MODIFY_VALUE); // page size 4096 + g_mgr.GetKvStore("integrity021", nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->CheckIntegrity(), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("integrity021"), OK); + } + LOGI("Begin the encrypted check"); + Key randomPassword; + DistributedDBToolsUnitTest::GetRandomKeyValue(randomPassword, PASSWD_SIZE); + int errCode = passwd.SetValue(randomPassword.data(), randomPassword.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + nbOption = {true, false, true, CipherType::DEFAULT, passwd}; + nbOption.isNeedIntegrityCheck = true; + nbOption.isNeedRmCorruptedDb = true; + filePath = GetKvStoreDirectory("integrity022", DBConstant::DB_TYPE_SINGLE_VER); + for (uint32_t i = 1; i < 4; i++) { + LOGI("%u th test the encrypted database!", i); + g_mgr.GetKvStore("integrity022", nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_EQ(g_kvNbDelegatePtr->CheckIntegrity(), OK); + ASSERT_EQ(PutDataIntoDatabase(nullptr, g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + DistributedDBToolsUnitTest::ModifyDatabaseFile(filePath, i * 1024, MODIFY_SIZE, MODIFY_VALUE); // page size 1024 + g_mgr.GetKvStore("integrity022", nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->CheckIntegrity(), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("integrity022"), OK); + } +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..95b076c2475786cd7ed93a0e9c0764075d60619f --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_test.cpp @@ -0,0 +1,1435 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "db_common.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "kvdb_manager.h" +#include "platform_specific.h" +#include "process_system_api_adapter_impl.h" +#include "runtime_context.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + enum { + SCHEMA_TYPE1 = 1, + SCHEMA_TYPE2 + }; + static int g_conflictCount = 0; + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + string g_testDir; + KvStoreConfig g_config; + + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); +#ifndef OMIT_JSON + const int PASSWD_LEN = 10; + const int PASSWD_VAL = 45; + void GenerateValidSchemaString(std::string &string, int num = SCHEMA_TYPE1) + { + switch (num) { + case SCHEMA_TYPE1: + string = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":{" + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, NOT NULL, DEFAULT '3.1415'\"," + "\"field_name7\":[]," + "\"field_name8\":{}" + "}" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2.field_name6\"]}"; + break; + case SCHEMA_TYPE2: + string = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"LONG, DEFAULT 100\"," + "\"field_name2\":{" + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, NOT NULL, DEFAULT '3.1415'\"" + "}" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2.field_name6\"]}"; + break; + default: + return; + } + } + + void GenerateInvalidSchemaString(std::string &string) + { + string = "123"; + } + + void GenerateEmptySchemaString(std::string &string) + { + string.clear(); + } + + int WriteValidDataIntoKvStore() + { + return OK; + } + + void OpenOpenedKvstoreWithSchema(const std::string &storeId, bool isEncrypt) + { + /** + * @tc.steps: step1. create a new db(non-memory, encrypt), with schema; + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + */ + KvStoreNbDelegate::Option option = {true, false, isEncrypt}; + if (isEncrypt) { + CipherPassword passwd; + vector passwdBuffer(PASSWD_LEN, PASSWD_VAL); + passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + option.passwd = passwd; + } + GenerateValidSchemaString(option.schema); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *kvNbDelegatePtr1 = g_kvNbDelegatePtr; + /** + * @tc.steps: step2. open an opened db, with same schema; + * @tc.expected: step2. Returns a non-null kvstore and error code is OK. + */ + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *kvNbDelegatePtr2 = g_kvNbDelegatePtr; + /** + * @tc.steps: step3. open an opened db, with valid but different schema; + * @tc.expected: step3. Returns a null kvstore and error code is SCHEMA_MISMATCH. + */ + GenerateValidSchemaString(option.schema, SCHEMA_TYPE2); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == SCHEMA_MISMATCH); + + /** + * @tc.steps: step4. open an opened db, with invalid schema; + * @tc.expected: step4. Returns a null kvstore and error code is INVALID_SCHEMA. + */ + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_SCHEMA); + + /** + * @tc.steps: step5. open an opened db, with empty schema; + * @tc.expected: step5. Returns a null kvstore and error code is INVALID_SCHEMA. + */ + std::string emptySchema; + option.schema = emptySchema; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == SCHEMA_MISMATCH); + + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr1), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr2), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore(storeId) == OK); + } + + void OpenClosedSchemaKvStore(const std::string &storeId, bool isEncrypt, std::string &inSchema) + { + /** + * @tc.steps: step1. create a new db(non-memory), with input schema; + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + */ + KvStoreNbDelegate::Option option = {true, false, isEncrypt}; + option.schema = inSchema; + if (isEncrypt) { + CipherPassword passwd; + vector passwdBuffer(PASSWD_LEN, PASSWD_VAL); + passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + option.passwd = passwd; + } + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(WriteValidDataIntoKvStore() == OK); + /** + * @tc.steps: step2. close the created kvstore; + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + + /** + * @tc.steps: step3. reopen the kvstore with same schema; + * @tc.expected: step3. Return OK. + */ + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(WriteValidDataIntoKvStore() == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + + /** + * @tc.steps: step4. reopen the kvstore with valid schema, but the schema is not equal to inSchema; + * @tc.expected: step4. Return a null kvstore and retCode is SCHEMA_MISMATCH. + */ + GenerateValidSchemaString(option.schema, SCHEMA_TYPE2); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == SCHEMA_MISMATCH); + /** + * @tc.steps: step5. reopen the kvstore with invalid schema; + * @tc.expected: step5. Return a null kvstore and retCode is INVALID_SCHEMA. + */ + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_SCHEMA); + /** + * @tc.steps: step6. reopen the kvstore with empty schema; + * @tc.expected: step6. Return a read-only kvstore and retCode is READ_ONLY. + */ + GenerateEmptySchemaString(option.schema); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + // here should return READ_ONLY + EXPECT_TRUE(WriteValidDataIntoKvStore() == OK); + KvStoreNbDelegate *kvNbDelegatePtr1 = g_kvNbDelegatePtr; + + // Open another kvstore with empty schema + GenerateEmptySchemaString(option.schema); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *kvNbDelegatePtr2 = g_kvNbDelegatePtr; + // here should return READ_ONLY + EXPECT_TRUE(WriteValidDataIntoKvStore() == OK); + + // Open another kvstore with origin schema + option.schema = inSchema; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == SCHEMA_MISMATCH); + + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr1), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr2), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore(storeId) == OK); + } + + void OpenClosedNormalKvStore(const std::string &storeId, bool isEncrypt) + { + /** + * @tc.steps: step1. create a new db(non-memory), without schema; + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + */ + KvStoreNbDelegate::Option option = {true, false, isEncrypt}; + if (isEncrypt) { + CipherPassword passwd; + vector passwdBuffer(PASSWD_LEN, PASSWD_VAL); + passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + option.passwd = passwd; + } + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(WriteValidDataIntoKvStore() == OK); + /** + * @tc.steps: step2. close the created kvstore; + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + + /** + * @tc.steps: step3. reopen the kvstore with empty schema; + * @tc.expected: step3. Return a kvstore and retCode is OK. + */ + GenerateEmptySchemaString(option.schema); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(WriteValidDataIntoKvStore() == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + /** + * @tc.steps: step4. reopen the kvstore with valid schema; + * @tc.expected: step4. Return OK. + */ + GenerateValidSchemaString(option.schema); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(WriteValidDataIntoKvStore() == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + + /** + * @tc.steps: step5. reopen the kvstore with invalid schema; + * @tc.expected: step5. Return a null kvstore and retCode is SCHEMA_MISMATCH. + */ + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_SCHEMA); + EXPECT_TRUE(g_mgr.DeleteKvStore(storeId) == OK); + } +#endif +} + +class DistributedDBInterfacesDatabaseTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesDatabaseTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); +} + +void DistributedDBInterfacesDatabaseTest::TearDownTestCase(void) +{ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); +} + +void DistributedDBInterfacesDatabaseTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvDelegatePtr = nullptr; +} + +void DistributedDBInterfacesDatabaseTest::TearDown(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +/** + * @tc.name: GetKvStore001 + * @tc.desc: Get kv store through different parameters. + * @tc.type: FUNC + * @tc.require: AR000CQDV4 AR000CQS3P + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, GetKvStore001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter the normal storId, createIfNecessary(true) and isLocal(true). + * @tc.steps: step2. Close the kvStore through the CloseKvStore interface of the delegate manager. + * @tc.expected: step1. Returns a non-null kvstore. + * @tc.expected: step2. Returns OK. + */ + KvStoreDelegate::Option option = {true, true, false}; + g_mgr.GetKvStore("distributed_db_test1", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + /** + * @tc.steps: step3. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter the normal storId, createIfNecessary(true) and isLocal(false). + * @tc.steps: step4. Close the kvStore through the CloseKvStore interface of the delegate manager. + * @tc.expected: step3. Returns a non-null kvstore. + * @tc.expected: step4. Returns OK. + */ + option = {true, false, false}; + g_mgr.GetKvStore("distributed_db_test2", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + /** + * @tc.steps: step5. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter the normal storId, createIfNecessary(false) and isLocal(true). + * @tc.expected: step5. Returns a non-null kvstore and error code is ERROR. + */ + option = {false, true, false}; + g_mgr.GetKvStore("distributed_db_test3", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_NE(g_kvDelegateStatus, OK); + + /** + * @tc.steps: step6. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter the normal storId, createIfNecessary(false) and isLocal(false). + * @tc.expected: step6. Returns a non-null kvstore and error code is ERROR. + */ + option = {false, false, false}; + g_mgr.GetKvStore("distributed_db_test4", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_NE(g_kvDelegateStatus, OK); + + /** + * @tc.steps: step7. Obtain the kvStore through the GetKvStore interface of the delegate manager + * which is initialized with the empty appid. + * @tc.expected: step7. Returns a non-null kvstore and error code is INVALID_ARGS. + */ + KvStoreDelegateManager invalidMgrFirst("", USER_ID); + invalidMgrFirst.SetKvStoreConfig(g_config); + option = {true, true, false}; + invalidMgrFirst.GetKvStore("distributed_db_test5", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_ARGS); + + /** + * @tc.steps: step8. Obtain the kvStore through the GetKvStore interface of the delegate manager + * which is initialized with the empty userid. + * @tc.expected: step8. Returns a non-null kvstore and error code is INVALID_ARGS. + */ + KvStoreDelegateManager invalidMgrSecond(APP_ID, ""); + invalidMgrSecond.SetKvStoreConfig(g_config); + invalidMgrSecond.GetKvStore("distributed_db_test6", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_ARGS); + + /** + * @tc.steps: step9. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter the empty storId, createIfNecessary(true) and isLocal(true). + * @tc.expected: step9. Returns a non-null kvstore and error code is INVALID_ARGS. + */ + g_mgr.GetKvStore("", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_ARGS); + + /** + * @tc.steps: step10. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter the invalid storId, createIfNecessary(true) and isLocal(true). + * @tc.expected: step10. Returns a non-null kvstore and error code is INVALID_ARGS. + */ + g_mgr.GetKvStore("$@.test", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_ARGS); + + /** + * @tc.steps: step11. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter: all alphabet string storId, createIfNecessary(true) and isLocal(true). + * @tc.expected: step11. Returns a non-null kvstore and error code is OK. + */ + g_mgr.GetKvStore("TEST", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + /** + * @tc.steps: step12. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter: digital string storId, createIfNecessary(true) and isLocal(true). + * @tc.expected: step12. Returns a non-null kvstore and error code is OK. + */ + g_mgr.GetKvStore("123", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + /** + * @tc.steps: step13. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parmater: digital and alphabet combined string storId, createIfNecessary(true) and isLocal(true). + * @tc.expected: step13. Returns a non-null kvstore and error code is OK. + */ + g_mgr.GetKvStore("TEST_test_123", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); +} + +/** + * @tc.name: GetKvStore002 + * @tc.desc: Get kv store through different parameters for the same storeID. + * @tc.type: FUNC + * @tc.require: AR000CQDV5 AR000CQS3P + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, GetKvStore002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter createIfNecessary(true) and isLocal(true). + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + */ + CipherPassword passwd; + KvStoreDelegate::Option option = {true, true, false}; + g_mgr.GetKvStore("distributed_getkvstore_002", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + /** + * @tc.steps: step2. Re-Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter createIfNecessary(true) and isLocal(false). + * @tc.expected: step2. Returns a non-null kvstore and error code is OK. + */ + option.localOnly = false; + g_mgr.GetKvStore("distributed_getkvstore_002", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + /** + * @tc.steps: step3. Re-Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter createIfNecessary(false) and isLocal(true). + * @tc.expected: step3. Returns a non-null kvstore and error code is OK. + */ + option = {false, true, false}; + g_mgr.GetKvStore("distributed_getkvstore_002", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + /** + * @tc.steps: step4. Re-Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter createIfNecessary(false) and isLocal(false). + * @tc.expected: step4. Returns a non-null kvstore and error code is OK. + */ + option = {false, false, false}; + g_mgr.GetKvStore("distributed_getkvstore_002", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + /** + * @tc.steps: step5. Re-Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter createIfNecessary(false) and isLocal(false). + * @tc.expected: step5. Returns a non-null kvstore and error code is OK. + */ + option = {true, true, false}; + g_mgr.GetKvStore("distributed_getkvstore_002", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + string retStoreId = g_kvDelegatePtr->GetStoreId(); + EXPECT_TRUE(retStoreId.compare("distributed_getkvstore_002") == 0); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); +} + +/** + * @tc.name: GetKvStore003 + * @tc.desc: Get kv store through different SecurityOption, abnormal or normal. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + * @tc.author: liuwenkai + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, GetKvStore003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter secOption(abnormal). + * @tc.expected: step1. Returns a null kvstore and error code is not OK. + */ + std::shared_ptr g_adapter = std::make_shared(); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); + KvStoreNbDelegate::Option option = {true, false, false}; + int abnormalNum = -100; + option.secOption.securityLabel = abnormalNum; + option.secOption.securityFlag = abnormalNum; + g_mgr.GetKvStore("distributed_getkvstore_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus != OK); + + /** + * @tc.steps: step2. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter secOption(normal). + * @tc.expected: step2. Returns a non-null kvstore and error code is OK. + */ + option.secOption.securityLabel = S3; + option.secOption.securityFlag = 0; + g_mgr.GetKvStore("distributed_getkvstore_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *kvNbDelegatePtr1 = g_kvNbDelegatePtr; + + /** + * @tc.steps: step3. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter secOption(normal but not same as last). + * @tc.expected: step3. Returns a null kvstore and error code is not OK. + */ + option.secOption.securityLabel = S3; + option.secOption.securityFlag = 1; + g_mgr.GetKvStore("distributed_getkvstore_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus != OK); + + /** + * @tc.steps: step4. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter secOption(normal and same as last). + * @tc.expected: step4. Returns a non-null kvstore and error code is OK. + */ + option.secOption.securityLabel = S3; + option.secOption.securityFlag = 0; + g_mgr.GetKvStore("distributed_getkvstore_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr1), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore("distributed_getkvstore_003") == OK); +} + +static void NotifierCallback(const KvStoreNbConflictData &data) +{ + LOGE("Trigger conflict callback!"); + g_conflictCount++; +} + +/** + * @tc.name: GetKvStore004 + * @tc.desc: Get kv store parameters with Observer and Notifier, then trigger callback. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + * @tc.author: liuwenkai + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, GetKvStore004, TestSize.Level1) +{ + /** + * @tc.steps: step1. Obtain the kvStore through the GetKvStore interface of the delegate manager + * using the parameter observer, notifier, key. + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_NE(observer, nullptr); + Key key; + Value value1; + Value value2; + key.push_back(1); + value1.push_back(1); + value2.push_back(2); + option.conflictType = CONFLICT_NATIVE_ALL; + option.notifier = NotifierCallback; + option.key = key; + option.observer = observer; + option.mode = OBSERVER_CHANGES_NATIVE; + g_conflictCount = 0; + int sleepTime = 100; + g_mgr.GetKvStore("distributed_getkvstore_004", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step2. Put(k1,v1) to db and check the observer info. + * @tc.expected: step2. Put successfully and trigger notifier callback. + */ + EXPECT_TRUE(g_kvNbDelegatePtr->Put(key, value1) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + LOGI("observer count:%lu", observer->GetCallCount()); + EXPECT_TRUE(observer->GetCallCount() == 1); + + /** + * @tc.steps: step3. put(k1,v2) to db and check the observer info. + * @tc.expected: step3. put successfully and trigger conflict callback. + */ + EXPECT_TRUE(g_kvNbDelegatePtr->Put(key, value2) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + LOGI("observer count:%lu", observer->GetCallCount()); + EXPECT_TRUE(observer->GetCallCount() == 2); + LOGI("call conflictNotifier count:%d, 1 means trigger success.", g_conflictCount); + EXPECT_EQ(g_conflictCount, 1); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + delete observer; + observer = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore("distributed_getkvstore_004") == OK); +} + +/** + * @tc.name: CloseKvStore001 + * @tc.desc: Test the CloseKvStore Interface and check whether the database file can be closed. + * @tc.type: FUNC + * @tc.require: AR000CQDV6 AR000CQS3P + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, CloseKvStore001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Obtain the kvStore of the non-existed database through the GetKvStore interface of + * the delegate manager using the parameter createdIfNecessary(true) + * @tc.steps: step2. Close the valid kvStore. + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + * @tc.expected: step2. Returns OK. + */ + CipherPassword passwd; + KvStoreDelegate::Option option = {true, true, false}; + g_mgr.GetKvStore("CloseKvStore_001", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + + /** + * @tc.steps: step3. Obtain the kvStore of the existed database through the GetKvStore interface of + * the delegate manager using the parameter createIfNecessary(false) + * @tc.steps: step4. Close the valid kvStore. + * @tc.expected: step3. Returns a non-null kvstore and error code is OK. + * @tc.expected: step4. Returns OK. + */ + option = {false, true, false}; + g_mgr.GetKvStore("CloseKvStore_001", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + + /** + * @tc.steps: step5. Close the invalid kvStore which is nullptr. + * @tc.expected: step5. Returns INVALID_ARGS. + */ + KvStoreDelegate *storeDelegate = nullptr; + EXPECT_EQ(g_mgr.CloseKvStore(storeDelegate), INVALID_ARGS); +} + +/** + * @tc.name: DeleteKvStore001 + * @tc.desc: Test the DeleteKvStore Interface and check whether the database files can be removed. + * @tc.type: FUNC + * @tc.require: AR000C2F0C AR000CQDV7 + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, DeleteKvStore001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Obtain the kvStore through the GetKvStore interface of + * the delegate manager using the parameter createIfNecessary(true) + * @tc.steps: step2. Close the kvStore. + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + * @tc.expected: step2. Returns OK. + */ + CipherPassword passwd; + KvStoreDelegate::Option option = {true, true, false}; + const std::string storeId("DeleteKvStore_001"); + g_mgr.GetKvStore(storeId, option, g_kvDelegateCallback); + std::string origIdentifierName = USER_ID + "-" + APP_ID + "-" + storeId; + std::string hashIdentifierName = DBCommon::TransferHashString(origIdentifierName); + std::string identifierName = DBCommon::TransferStringToHex(hashIdentifierName); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + + /** + * @tc.steps: step3. Check the database file + * @tc.expected: step3. the database file are existed. + */ + string dbFileName = g_testDir + "/" + identifierName + "/local/local.db"; + ifstream dbFile(dbFileName); + EXPECT_TRUE(dbFile); + dbFile.close(); + string walFileName = g_testDir + "/" + identifierName + "/local/local.db-wal"; + fstream walFile(walFileName, fstream::out); + EXPECT_TRUE(walFile.is_open()); + walFile.close(); + string shmFileName = g_testDir + "/" + identifierName + "/local/local.db-shm"; + fstream shmFile(shmFileName, fstream::out); + EXPECT_TRUE(shmFile.is_open()); + shmFile.close(); + + std::string dataBaseDir = g_testDir + "/" + identifierName; + EXPECT_GE(access(dataBaseDir.c_str(), F_OK), 0); + + /** + * @tc.steps: step4. Delete the kvStore through the DeleteKvStore interface of + * the delegate manager + * @tc.steps: step5. Check the database files and the storage paths. + * @tc.expected: step4. Returns OK. + * @tc.expected: step5. The database files and the storage paths are not existed. + */ + EXPECT_TRUE(g_mgr.DeleteKvStore(storeId) == OK); + ifstream dbFileAfter(dbFileName); + ifstream walFileAfter(walFileName); + ifstream shmFileAfter(shmFileName); + EXPECT_FALSE(dbFileAfter); + EXPECT_FALSE(walFileAfter); + EXPECT_FALSE(shmFileAfter); + ASSERT_EQ(OS::CheckPathExistence(dataBaseDir), false); + std::string storeIdOnlyIdentifier = DBCommon::TransferHashString(storeId); + std::string storeIdOnlyIdentifierName = DBCommon::TransferStringToHex(storeIdOnlyIdentifier); + std::string storeIdOnlyIdDataBaseDir = g_testDir + "/" + storeIdOnlyIdentifierName; + ASSERT_EQ(OS::CheckPathExistence(storeIdOnlyIdDataBaseDir), false); + + /** + * @tc.steps: step6. Re-Delete the kvStore through the DeleteKvStore interface of + * the delegate manager + * @tc.expected: step6. Returns NOT_FOUND. + */ + EXPECT_TRUE(g_mgr.DeleteKvStore(storeId) == NOT_FOUND); +} + +/** + * @tc.name: RepeatCloseKvStore001 + * @tc.desc: Close the kv store repeatedly and check the database. + * @tc.type: FUNC + * @tc.require: AR000C2F0C AR000CQDV7 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, RepeatCloseKvStore001, TestSize.Level2) +{ + /** + * @tc.steps: step1. Obtain the kvStore through the GetKvStore interface of + * the delegate manager using the parameter createIfNecessary(true) + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + */ + CipherPassword passwd; + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("RepeatCloseKvStore_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + static const size_t totalSize = 50; + + /** + * @tc.steps: step2. Put into the database some data. + * @tc.expected: step2. Put returns OK. + */ + std::vector keys; + for (size_t i = 0; i < totalSize; i++) { + Entry entry; + DistributedDBToolsUnitTest::GetRandomKeyValue(entry.key, static_cast(i + 1)); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry.value); + EXPECT_EQ(g_kvNbDelegatePtr->Put(entry.key, entry.value), OK); + keys.push_back(entry.key); + } + + /** + * @tc.steps: step3. Delete the data from the database, and close the database, reopen the database and + * get the data. + * @tc.expected: step3. Delete returns OK, Close returns OK and Get returns NOT_FOUND. + */ + for (size_t i = 0; i < keys.size(); i++) { + Value value; + EXPECT_EQ(g_kvNbDelegatePtr->Delete(keys[i]), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Get(keys[i], value), NOT_FOUND); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_mgr.GetKvStore("RepeatCloseKvStore_001", option, g_kvNbDelegateCallback); + EXPECT_EQ(g_kvNbDelegatePtr->Get(keys[i], value), NOT_FOUND); + } + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + /** + * @tc.steps: step4. Delete the kvstore created before. + * @tc.expected: step4. Delete returns OK. + */ + EXPECT_EQ(g_mgr.DeleteKvStore("RepeatCloseKvStore_001"), OK); +} +#ifndef OMIT_JSON +/** + * @tc.name: CreatKvStoreWithSchema001 + * @tc.desc: Create non-memory KvStore with schema, check if create success. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, CreatKvStoreWithSchema001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a new db(non-memory, non-encrypt), with valid schema; + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + GenerateValidSchemaString(option.schema); + g_mgr.GetKvStore("CreatKvStoreWithSchema_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore("CreatKvStoreWithSchema_001") == OK); + /** + * @tc.steps: step2. create a new db(non-memory, non-encrypt), with invalid schema; + * @tc.expected: step2. Returns null kvstore and error code is INVALID_SCHEMA. + */ + option = {true, false, false}; + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore("CreatKvStoreWithSchema_001_invalid_schema", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_SCHEMA); + EXPECT_TRUE(g_mgr.DeleteKvStore("CreatKvStoreWithSchema_001_invalid_schema") == NOT_FOUND); +#ifndef OMIT_ENCRYPT + /** + * @tc.steps: step3. create a new db(non-memory, encrypt), with valid schema; + * @tc.expected: step3. Returns a non-null kvstore and error code is OK. + */ + CipherPassword passwd; + vector passwdBuffer(10, 45); + passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + option = {true, false, true}; + GenerateValidSchemaString(option.schema); + option.passwd = passwd; + g_mgr.GetKvStore("CreatKvStoreWithSchema_001_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore("CreatKvStoreWithSchema_001_002") == OK); + + /** + * @tc.steps: step4. create a new db(non-memory, non-encrypt), with invalid schema; + * @tc.expected: step2. Returns null kvstore and error code is INVALID_SCHEMA. + */ + option = {true, false, false}; + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore("CreatKvStoreWithSchema_002_invalid_schema", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_SCHEMA); + EXPECT_TRUE(g_mgr.DeleteKvStore("CreatKvStoreWithSchema_002_invalid_schema") == NOT_FOUND); +#endif +} + +/** + * @tc.name: CreatKvStoreWithSchema002 + * @tc.desc: Create memory KvStore with schema, check if create success. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, CreatKvStoreWithSchema002, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a new db(memory, non-encrypt), with valid schema; + * @tc.expected: step1. Returns a null kvstore and error code is NOT_SUPPORT. + */ + KvStoreNbDelegate::Option option = {true, true, false}; + GenerateValidSchemaString(option.schema); + g_mgr.GetKvStore("CreatKvStoreWithSchema_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == NOT_SUPPORT); + + /** + * @tc.steps: step2. create a new db(memory, non-encrypt), with invalid schema; + * @tc.expected: step2. Returns null kvstore and error code is NOT_SUPPORT. + */ + option = {true, true, false}; + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore("CreatKvStoreWithSchema_002_invalid", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == NOT_SUPPORT); +} +#ifndef OMIT_ENCRYPT +/** + * @tc.name: OpenKvStoreWithSchema001 + * @tc.desc: open an opened kvstore(non-memory, no-schema) with schema, check if open success. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, OpenKvStoreWithSchema001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a new db(non-memory, non-encrypt), without schema; + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("OpenKvStoreWithSchema_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *kvNbDelegatePtr = g_kvNbDelegatePtr; + /** + * @tc.steps: step2. open the db with valid schema; + * @tc.expected: step2. Returns a null kvstore and error code is SCHEMA_MISMATCH. + */ + GenerateValidSchemaString(option.schema); + g_mgr.GetKvStore("OpenKvStoreWithSchema_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == SCHEMA_MISMATCH); + + /** + * @tc.steps: step3. open the db with invalid schema; + * @tc.expected: step3. Returns a null kvstore and error code is SCHEMA_MISMATCH. + */ + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore("OpenKvStoreWithSchema_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_SCHEMA); + + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore("OpenKvStoreWithSchema_001") == OK); + /** + * @tc.steps: step4. create a new db(non-memory, encrypt), without schema; + * @tc.expected: step4. Returns a non-null kvstore and error code is OK. + */ + option = {true, false, true}; + CipherPassword passwd; + vector passwdBuffer(10, 45); + passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + option.passwd = passwd; + g_mgr.GetKvStore("OpenKvStoreWithSchema_001_encrypt", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + kvNbDelegatePtr = g_kvNbDelegatePtr; + /** + * @tc.steps: step5. open the db with valid schema; + * @tc.expected: step5. Returns a null kvstore and error code is SCHEMA_MISMATCH. + */ + GenerateValidSchemaString(option.schema); + g_mgr.GetKvStore("OpenKvStoreWithSchema_001_encrypt", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == SCHEMA_MISMATCH); + + /** + * @tc.steps: step6. open the db with invalid schema; + * @tc.expected: step6. Returns a null kvstore and error code is SCHEMA_MISMATCH. + */ + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore("OpenKvStoreWithSchema_001_encrypt", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == INVALID_SCHEMA); + + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore("OpenKvStoreWithSchema_001_encrypt") == OK); +} +#endif +/** + * @tc.name: OpenKvStoreWithSchema002 + * @tc.desc: open an opened kvstore(non-memory, schema) with schema, check if open success. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, OpenKvStoreWithSchema002, TestSize.Level1) +{ +#ifndef OMIT_ENCRYPT + /** + * @tc.steps: step1. open an opened kvstore(non-memory, non-encrypt), with different schemas; + */ + OpenOpenedKvstoreWithSchema("OpenKvStoreWithSchema_002", true); +#endif + /** + * @tc.steps: step2. open an opened kvstore(non-memory, encrypt), with different schemas; + */ + OpenOpenedKvstoreWithSchema("OpenKvStoreWithSchema_002_encrypt", false); +} + +/** + * @tc.name: OpenKvStoreWithSchema003 + * @tc.desc: open an opened kvstore(memory) with different schemas, check if open success. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, OpenKvStoreWithSchema003, TestSize.Level1) +{ + /** + * @tc.steps: step1. create a new db(memory, non-encrypt), without schema; + * @tc.expected: step1. Returns a non-null kvstore and error code is OK. + */ + KvStoreNbDelegate::Option option = {true, true, false}; + g_mgr.GetKvStore("OpenKvStoreWithSchema_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *kvNbDelegatePtr1 = g_kvNbDelegatePtr; + /** + * @tc.steps: step2. open a new db(memory, non-encrypt), without schema; + * @tc.expected: step2. Returns a non-null kvstore and error code is OK. + */ + g_mgr.GetKvStore("OpenKvStoreWithSchema_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *kvNbDelegatePtr2 = g_kvNbDelegatePtr; + /** + * @tc.steps: step3. open a new db(memory, non-encrypt), with valid schema; + * @tc.expected: step3. Returns a null kvstore and error code is NOT_SUPPORT. + */ + GenerateValidSchemaString(option.schema); + g_mgr.GetKvStore("OpenKvStoreWithSchema_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == NOT_SUPPORT); + + /** + * @tc.steps: step4. open a new db(memory, non-encrypt), with invalid schema; + * @tc.expected: step4. Returns a null kvstore and error code is NOT_SUPPORT. + */ + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore("OpenKvStoreWithSchema_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == NOT_SUPPORT); + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr1), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr2), OK); +} + +/** + * @tc.name: OpenKvStoreWithSchema004 + * @tc.desc: open a totally closed schema-kvstore(non-memory) with different schemas, check if open success. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, OpenKvStoreWithSchema004, TestSize.Level1) +{ + /** + * @tc.steps: step1. open a new db(non-memory, non-encrypt), with different schemas; + * @tc.expected: step1. Returns a null kvstore and error code is NOT_SUPPORT. + */ + std::string schema; + GenerateValidSchemaString(schema); + OpenClosedSchemaKvStore("OpenKvStoreWithSchema_004", false, schema); +#ifndef OMIT_ENCRYPT + /** + * @tc.steps: step2. open a new db(non-memory, encrypt), with different schemas; + * @tc.expected: step2. Returns a null kvstore and error code is NOT_SUPPORT. + */ + OpenClosedSchemaKvStore("OpenKvStoreWithSchema_004_encrypt", true, schema); +#endif +} + +/** + * @tc.name: OpenKvStoreWithSchema005 + * @tc.desc: open a totally closed non-schema-kvstore(non-memory) with different schemas, check if open success. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, OpenKvStoreWithSchema005, TestSize.Level1) +{ + /** + * @tc.steps: step1. open a new db(non-memory, non-encrypt, non-schema), with different schemas; + * @tc.expected: step1. Returns a different result. + */ + OpenClosedNormalKvStore("OpenKvStoreWithSchema_005", false); +#ifndef OMIT_ENCRYPT + /** + * @tc.steps: step2. open a new db(non-memory, encrypt, non-schema), with different schemas; + * @tc.expected: step2. Returns a different result. + */ + OpenClosedNormalKvStore("OpenKvStoreWithSchema_005", true); +#endif +} + +/** + * @tc.name: OpenKvStoreWithSchema006 + * @tc.desc: open a memory non-schema-kvstore with different schemas, check if open success. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: weifeng + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, OpenKvStoreWithSchema006, TestSize.Level1) +{ + /** + * @tc.steps: step1. open a new db(memory, non-encrypt, non-schema), without schema; + * @tc.expected: step1. Returns OK. + */ + KvStoreNbDelegate::Option option = {true, true, false}; + g_mgr.GetKvStore("OpenKvStoreWithSchema_006", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step2. close the kvstore; + * @tc.expected: step2. Returns OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + + /** + * @tc.steps: step3. reopen the kvstore without schema; + * @tc.expected: step3. Returns OK. + */ + g_mgr.GetKvStore("OpenKvStoreWithSchema_006", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(WriteValidDataIntoKvStore() == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + + /** + * @tc.steps: step4. reopen the kvstore with valid schema; + * @tc.expected: step4. Returns OK. + */ + GenerateValidSchemaString(option.schema); + g_mgr.GetKvStore("OpenKvStoreWithSchema_006", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == NOT_SUPPORT); + + /** + * @tc.steps: step4. reopen the kvstore with invalid schema; + * @tc.expected: step4. Returns OK. + */ + GenerateInvalidSchemaString(option.schema); + g_mgr.GetKvStore("OpenKvStoreWithSchema_006", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus == NOT_SUPPORT); +} +#endif +/** + * @tc.name: OpenKvStoreWithStoreOnly001 + * @tc.desc: open the kv store with the option that createDirByStoreIdOnly is true. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, OpenKvStoreWithStoreOnly001, TestSize.Level1) +{ + /** + * @tc.steps: step1. open the kv store with the option that createDirByStoreIdOnly is true. + * @tc.expected: step1. Returns OK. + */ + KvStoreNbDelegate::Option option; + option.createDirByStoreIdOnly = true; + g_mgr.GetKvStore("StoreOnly001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + auto kvStorePtr = g_kvNbDelegatePtr; + /** + * @tc.steps: step2. open the same store with the option that createDirByStoreIdOnly is false. + * @tc.expected: step2. Returns NOT OK. + */ + option.createDirByStoreIdOnly = false; + g_kvNbDelegatePtr = nullptr; + g_mgr.GetKvStore("StoreOnly001", option, g_kvNbDelegateCallback); + EXPECT_EQ(g_kvDelegateStatus, INVALID_ARGS); + /** + * @tc.steps: step3. close the kvstore and delete the kv store; + * @tc.expected: step3. Returns OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(kvStorePtr), OK); + kvStorePtr = nullptr; + EXPECT_EQ(g_mgr.DeleteKvStore("StoreOnly001"), OK); +} + +/** + * @tc.name: GetDBWhileOpened001 + * @tc.desc: open the kv store with the option that createDirByStoreIdOnly is true. + * @tc.type: FUNC + * @tc.require: AR000E8S2V + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, GetDBWhileOpened001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the connection. + * @tc.expected: step1. Returns OK. + */ + KvDBProperties property; + std::string storeId = "openTest"; + std::string origId = USER_ID + "-" + APP_ID + "-" + storeId; + std::string identifier = DBCommon::TransferHashString(origId); + std::string hexDir = DBCommon::TransferStringToHex(identifier); + property.SetStringProp(KvDBProperties::IDENTIFIER_DATA, identifier); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, hexDir); + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + property.SetBoolProp(KvDBProperties::MEMORY_MODE, false); + property.SetBoolProp(KvDBProperties::ENCRYPTED_MODE, false); + property.SetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, true); + property.SetStringProp(KvDBProperties::APP_ID, APP_ID); + property.SetStringProp(KvDBProperties::USER_ID, USER_ID); + property.SetStringProp(KvDBProperties::APP_ID, storeId); + + int errCode = E_OK; + auto connection1 = KvDBManager::GetDatabaseConnection(property, errCode, false); + EXPECT_EQ(errCode, E_OK); + /** + * @tc.steps: step2. Get the connection with the para: isNeedIfOpened is false. + * @tc.expected: step2. Returns -E_ALREADY_OPENED. + */ + auto connection2 = KvDBManager::GetDatabaseConnection(property, errCode, false); + EXPECT_EQ(errCode, -E_ALREADY_OPENED); + EXPECT_EQ(connection2, nullptr); + + /** + * @tc.steps: step3. Get the connection with the para: isNeedIfOpened is true. + * @tc.expected: step3. Returns E_OK. + */ + auto connection3 = KvDBManager::GetDatabaseConnection(property, errCode, true); + EXPECT_EQ(errCode, OK); + EXPECT_NE(connection3, nullptr); + + KvDBManager::ReleaseDatabaseConnection(connection1); + KvDBManager::ReleaseDatabaseConnection(connection3); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); +} +namespace { + void OpenCloseDatabase(const std::string &storeId) + { + KvStoreNbDelegate::Option option; + DBStatus status; + KvStoreNbDelegate *delegate = nullptr; + auto nbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(delegate)); + int totalNum = 0; + for (size_t i = 0; i < 100; i++) { // cycle 100 times. + g_mgr.GetKvStore(storeId, option, nbDelegateCallback); + if (delegate != nullptr) { + totalNum++; + } + g_mgr.CloseKvStore(delegate); + delegate = nullptr; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + LOGD("Succeed %d times", totalNum); + } +} + +/** + * @tc.name: FreqOpenCloseDel001 + * @tc.desc: Open/close/delete the kv store concurrently. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, FreqOpenCloseDel001, TestSize.Level2) +{ + std::string storeId = "FrqOpenCloseDelete001"; + std::thread t1(OpenCloseDatabase, storeId); + std::thread t2([&]() { + for (int i = 0; i < 10000; i++) { + g_mgr.DeleteKvStore(storeId); + } + }); + t1.join(); + t2.join(); +} + +/** + * @tc.name: FreqOpenClose001 + * @tc.desc: Open and close the kv store concurrently. + * @tc.type: FUNC + * @tc.require: AR000DR9K2 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, FreqOpenClose001, TestSize.Level2) +{ + std::string storeId = "FrqOpenClose001"; + std::thread t1(OpenCloseDatabase, storeId); + std::thread t2(OpenCloseDatabase, storeId); + std::thread t3(OpenCloseDatabase, storeId); + std::thread t4(OpenCloseDatabase, storeId); + t1.join(); + t2.join(); + t3.join(); + t4.join(); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); +} + +/** + * @tc.name: CheckKvStoreDir001 + * @tc.desc: Delete the kv store with the option that createDirByStoreIdOnly is true. + * @tc.type: FUNC + * @tc.require: AR000CQDV7 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, CheckKvStoreDir001, TestSize.Level1) +{ + /** + * @tc.steps: step1. open the kv store with the option that createDirByStoreIdOnly is true. + * @tc.expected: step1. Returns OK. + */ + KvStoreNbDelegate::Option option; + option.createDirByStoreIdOnly = true; + const std::string storeId("StoreOnly002"); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + std::string testSubDir; + EXPECT_EQ(KvStoreDelegateManager::GetDatabaseDir(storeId, testSubDir), OK); + std::string dataBaseDir = g_testDir + "/" + testSubDir; + EXPECT_GE(access(dataBaseDir.c_str(), F_OK), 0); + + /** + * @tc.steps: step2. delete the kv store, and check the directory. + * @tc.expected: step2. the directory is removed. + */ + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_kvNbDelegatePtr = nullptr; + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + LOGI("[%s]", dataBaseDir.c_str()); + ASSERT_EQ(OS::CheckPathExistence(dataBaseDir), false); +} + +/** + * @tc.name: CompressionRate1 + * @tc.desc: Open the kv store with invalid compressionRate and open successfully. + * @tc.type: FUNC + * @tc.require: AR000G3QTT + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBInterfacesDatabaseTest, CompressionRate1, TestSize.Level1) +{ + /** + * @tc.steps: step1. Open the kv store with the option that comressionRate is invalid. + * @tc.expected: step1. Open kv store successfully. Returns OK. + */ + KvStoreNbDelegate::Option option; + option.isNeedCompressOnSync = true; + option.compressionRate = 0; // 0 is invalid. + const std::string storeId("CompressionRate1"); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_kvNbDelegatePtr = nullptr; + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); +} + +HWTEST_F(DistributedDBInterfacesDatabaseTest, DataInterceptor1, TestSize.Level1) +{ + /** + * @tc.steps: step1. Open the kv store with the option that comressionRate is invalid. + * @tc.expected: step1. Open kv store successfully. Returns OK. + */ + KvStoreNbDelegate::Option option; + const std::string storeId("DataInterceptor1"); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + g_kvNbDelegatePtr->SetPushDataInterceptor( + [](InterceptedData &data, const std::string &sourceID, const std::string &targetID) { + int errCode = OK; + auto entries = data.GetEntries(); + for (size_t i = 0; i < entries.size(); i++) { + if (entries[i].key.empty() || entries[i].key.at(0) != 'A') { + continue; + } + auto newKey = entries[i].key; + newKey[0] = 'B'; + errCode = data.ModifyKey(i, newKey); + if (errCode != OK) { + break; + } + } + return errCode; + } + ); + + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_kvNbDelegatePtr = nullptr; + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_device_identifier_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_device_identifier_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea23f80efbbe9812594e29f59f340f1a1898a9bf --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_device_identifier_test.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "distributeddb_data_generate_unit_test.h" +#include "kv_store_nb_delegate_impl.h" +#include "platform_specific.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + DBStatus g_kvDelegateStatus = INVALID_ARGS; + SQLiteSingleVerNaturalStore *g_store = nullptr; + DistributedDB::SQLiteSingleVerNaturalStoreConnection *g_connection = nullptr; + const string STORE_ID = STORE_ID_SYNC; + const int TIME_LAG = 100; + const std::string DEVICE_ID_1 = "ABC"; + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); +} + +class DistributedDBDeviceIdentifierTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBDeviceIdentifierTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + string dir = g_testDir + STORE_ID + "/" + DBConstant::SINGLE_SUB_DIR; + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } +} + +void DistributedDBDeviceIdentifierTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + STORE_ID + "/" + DBConstant::SINGLE_SUB_DIR) != 0) { + LOGE("rm test db files error!"); + } + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); +} + +void DistributedDBDeviceIdentifierTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(STORE_ID, option, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + + KvDBProperties property; + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, STORE_ID); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + + g_store = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(g_store, nullptr); + ASSERT_EQ(g_store->Open(property), E_OK); + + int erroCode = E_OK; + g_connection = static_cast(g_store->GetDBConnection(erroCode)); + ASSERT_NE(g_connection, nullptr); + g_store->DecObjRef(g_store); + EXPECT_EQ(erroCode, E_OK); +} + +void DistributedDBDeviceIdentifierTest::TearDown(void) +{ + if (g_connection != nullptr) { + g_connection->Close(); + } + + g_store = nullptr; + + if (g_kvNbDelegatePtr != nullptr) { + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore(STORE_ID) == OK); + } + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); +} + +/** + * @tc.name: DeviceIdentifier001 + * @tc.desc: Set pragma to be GET_DEVICE_IDENTIFIER_OF_ENTRY, + * set Key to be null and origDevice to be false, expect return INVALID_ARGS. + * @tc.type: FUNC + * @tc.require: AR000D08KV + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBDeviceIdentifierTest, DeviceIdentifier001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Sync 1K data from DEVICE_B into database, with Key= KEY_1, and Value = VALUE_1. + * @tc.expected: step1. Expect return true. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + + /** + * @tc.steps:step2. Set PragmaCmd to be GET_DEVICE_IDENTIFIER_OF_ENTRY, and set input key to be null + * @tc.expected: step2. Expect return INVALID_ARGS. + */ + Key keyNull; + PragmaEntryDeviceIdentifier param; + param.key = keyNull; + param.origDevice = false; + PragmaData input = static_cast(¶m); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(GET_DEVICE_IDENTIFIER_OF_ENTRY, input), INVALID_ARGS); +} + +/** + * @tc.name: DeviceIdentifier002 + * @tc.desc: Set pragma to be GET_DEVICE_IDENTIFIER_OF_ENTRY, + * set Key to be null and origDevice to be true, expect return INVALID_ARGS. + * @tc.type: FUNC + * @tc.require: AR000D08KV + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBDeviceIdentifierTest, DeviceIdentifier002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Sync 1K data from DEVICE_B into database, with Key= KEY_1, and Value = VALUE_1. + * @tc.expected: step1. Expect return true. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + + /** + * @tc.steps:step2. Set PragmaCmd to be GET_DEVICE_IDENTIFIER_OF_ENTRY, and set input key to be null + * @tc.expected: step2. Expect return INVALID_ARGS. + */ + Key keyNull; + PragmaEntryDeviceIdentifier param; + param.key = keyNull; + param.origDevice = true; + PragmaData input = static_cast(¶m); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(GET_DEVICE_IDENTIFIER_OF_ENTRY, input), INVALID_ARGS); +} + +/** + * @tc.name: DeviceIdentifier003 + * @tc.desc: Set pragma to be GET_DEVICE_IDENTIFIER_OF_ENTRY and origDevice to be false. + * Check if a non-existing key will return NOT_FOUND. + * @tc.type: FUNC + * @tc.require: AR000D08KV + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBDeviceIdentifierTest, DeviceIdentifier003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Sync 1K data from DEVICE_B into database, with Key= KEY_1, and Value = VALUE_1. + * @tc.expected: step1. Expect return true. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + /** + * @tc.steps:step2. Set PragmaCmd to be GET_DEVICE_IDENTIFIER_OF_ENTRY, and set Key= Key_2 + * @tc.expected: step2. Expect return NOT_FOUND. + */ + PragmaEntryDeviceIdentifier param; + param.key = KEY_2; + param.origDevice = false; + PragmaData input = static_cast(¶m); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(GET_DEVICE_IDENTIFIER_OF_ENTRY, input), NOT_FOUND); +} + +/** + * @tc.name: DeviceIdentifier004 + * @tc.desc: Set pragma to be GET_DEVICE_IDENTIFIER_OF_ENTRY and origDevice to be true. + * Check if a non-existing key will return NOT_FOUND. + * @tc.type: FUNC + * @tc.require: AR000D08KV + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBDeviceIdentifierTest, DeviceIdentifier004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Sync 1K data from DEVICE_B into database, with Key= KEY_1, and Value = VALUE_1. + * @tc.expected: step1. Expect return true. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + /** + * @tc.steps:step2. Set PragmaCmd to be GET_DEVICE_IDENTIFIER_OF_ENTRY, and set Key= Key_2 + * @tc.expected: step2. Expect return NOT_FOUND. + */ + PragmaEntryDeviceIdentifier param; + param.key = KEY_2; + param.origDevice = true; + PragmaData input = static_cast(¶m); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(GET_DEVICE_IDENTIFIER_OF_ENTRY, input), NOT_FOUND); +} + +/** + * @tc.name: DeviceIdentifier005 + * @tc.desc: Set pragma to be GET_DEVICE_IDENTIFIER_OF_ENTRY and origDevice to be false. check if returns OK. + * @tc.type: FUNC + * @tc.require: AR000D08KV + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBDeviceIdentifierTest, DeviceIdentifier005, TestSize.Level1) +{ + /** + * @tc.steps:step1. Sync 1K data from DEVICE_B into database, with Key= KEY_1, and Value = VALUE_1. + * @tc.expected: step1. Expect return true. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + /** + * @tc.steps:step2. Set PragmaCmd = GET_DEVICE_IDENTIFIER_OF_ENTRY, Key= Key_1, origDevice = false. + * @tc.expected: step2. Expect return deviceIdentifier is the same as deviceIdentifier of DEVICE_B. + */ + PragmaEntryDeviceIdentifier param; + param.key = KEY_1; + param.origDevice = false; + PragmaData input = static_cast(¶m); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(GET_DEVICE_IDENTIFIER_OF_ENTRY, input), OK); +} + +/** + * @tc.name: DeviceIdentifier006 + * @tc.desc: Set pragma to be GET_DEVICE_IDENTIFIER_OF_ENTRY and origDevice to be true. check if returns OK. + * @tc.type: FUNC + * @tc.require: AR000D08KV + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBDeviceIdentifierTest, DeviceIdentifier006, TestSize.Level1) +{ + /** + * @tc.steps:step1. Sync 1K data from DEVICE_B into database, with Key= KEY_1, and Value = VALUE_1. + * @tc.expected: step1. Expect return true. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + /** + * @tc.steps:step2. Set PragmaCmd = GET_DEVICE_IDENTIFIER_OF_ENTRY, Key= Key_1, origDevice = false. + * @tc.expected: step2. Expect return deviceIdentifier is the same as deviceIdentifier of DEVICE_B. + */ + PragmaEntryDeviceIdentifier param; + param.key = KEY_1; + param.origDevice = true; + PragmaData input = static_cast(¶m); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(GET_DEVICE_IDENTIFIER_OF_ENTRY, input), OK); +} + +/** + * @tc.name: DeviceIdentifier007 + * @tc.desc: Set pragma to be GET_IDENTIFIER_OF_DEVICE. check if empty deviceID returns INVALID_ARGS. + * @tc.type: FUNC + * @tc.require: AR000D08KV + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBDeviceIdentifierTest, DeviceIdentifier007, TestSize.Level1) +{ + /** + * @tc.steps:step1. Set PragmaCmd = GET_IDENTIFIER_OF_DEVICE, deviceID= NULL. + * @tc.expected: step1. Expect return INVALID_ARGS. + */ + PragmaDeviceIdentifier param; + param.deviceID = ""; + PragmaData input = static_cast(¶m); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(GET_IDENTIFIER_OF_DEVICE, input), INVALID_ARGS); +} + +/** + * @tc.name: DeviceIdentifier008 + * @tc.desc: Set pragma to be GET_IDENTIFIER_OF_DEVICE. check if deviceIdentifier matches deviceID. + * @tc.type: FUNC + * @tc.require: AR000D08KV + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBDeviceIdentifierTest, DeviceIdentifier008, TestSize.Level1) +{ + /** + * @tc.steps:step1. Set PragmaCmd = GET_IDENTIFIER_OF_DEVICE, deviceID = DEVICE_ID_1 + * @tc.expected: step1. Expect return deviceIdentifier is the same as deviceIdentifier of DEVICE_ID_1. + */ + PragmaDeviceIdentifier param; + param.deviceID = DEVICE_ID_1; + PragmaData input = static_cast(¶m); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(GET_IDENTIFIER_OF_DEVICE, input), OK); + EXPECT_EQ(param.deviceIdentifier, DBCommon::TransferHashString(DEVICE_ID_1)); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_database_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_database_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..645b3c7c2398144cc6b6166907c1ddc04fb91ef1 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_database_test.cpp @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OMIT_ENCRYPT +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + + const string STORE_ID1 = "store1"; + const string STORE_ID2 = "store2"; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; +} + +class DistributedDBInterfacesEncryptDatabaseTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + static void CheckRekeyWithMultiKvStore(bool isLocal); + static void CheckRekeyWithExistedSnapshot(bool isLocal); + static void CheckRekeyWithExistedObserver(bool isLocal); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesEncryptDatabaseTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesEncryptDatabaseTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesEncryptDatabaseTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBInterfacesEncryptDatabaseTest::TearDown(void) +{ +} + +void DistributedDBInterfacesEncryptDatabaseTest::CheckRekeyWithMultiKvStore(bool isLocal) +{ + DBStatus status; + KvStoreDelegate *kvStore1 = nullptr; + auto delegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(status), std::ref(kvStore1)); + /** + * @tc.steps:step1. Get the delegate. + */ + KvStoreDelegate::Option option = {true, isLocal, false}; + g_mgr.GetKvStore(STORE_ID1, option, delegateCallback); + ASSERT_TRUE(kvStore1 != nullptr); + + KvStoreDelegate *kvStore2 = nullptr; + auto delegateCallback2 = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(status), std::ref(kvStore2)); + /** + * @tc.steps:step2. Get another delegate. + */ + option.createIfNecessary = false; + g_mgr.GetKvStore(STORE_ID1, option, delegateCallback2); + ASSERT_TRUE(kvStore2 != nullptr); + + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + CipherPassword passwd; // random password + vector passwdBuffer(10, 45); // 10 and 45 as random password. + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + EXPECT_EQ(kvStore1->Rekey(passwd), BUSY); + /** + * @tc.steps:step4. Close the kv store delegate. + */ + EXPECT_EQ(g_mgr.CloseKvStore(kvStore2), OK); + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(kvStore1->Rekey(passwd), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvStore1), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); +} + +void DistributedDBInterfacesEncryptDatabaseTest::CheckRekeyWithExistedSnapshot(bool isLocal) +{ + DBStatus status; + KvStoreDelegate *kvStore = nullptr; + auto delegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(status), std::ref(kvStore)); + /** + * @tc.steps:step1. Get the delegate. + */ + KvStoreDelegate::Option option = {true, isLocal, false}; + g_mgr.GetKvStore(STORE_ID1, option, delegateCallback); + ASSERT_TRUE(kvStore != nullptr); + + KvStoreSnapshotDelegate *snapshot = nullptr; + auto snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(status), std::ref(snapshot)); + /** + * @tc.steps:step2. Get the snapshot through the delegate. + */ + kvStore->GetKvStoreSnapshot(nullptr, snapshotDelegateCallback); + EXPECT_NE(snapshot, nullptr); + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + CipherPassword passwd; // random password + vector passwdBuffer(10, 45); // 10 and 45 as random password. + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + EXPECT_EQ(kvStore->Rekey(passwd), BUSY); + /** + * @tc.steps:step4. Release the snapshot. + */ + EXPECT_EQ(kvStore->ReleaseKvStoreSnapshot(snapshot), OK); + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(kvStore->Rekey(passwd), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvStore), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); +} + +void DistributedDBInterfacesEncryptDatabaseTest::CheckRekeyWithExistedObserver(bool isLocal) +{ + DBStatus status; + KvStoreDelegate *kvStore = nullptr; + auto delegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(status), std::ref(kvStore)); + /** + * @tc.steps:step1. Get the delegate. + */ + KvStoreDelegate::Option option = {true, isLocal, false}; + g_mgr.GetKvStore(STORE_ID1, option, delegateCallback); + ASSERT_TRUE(kvStore != nullptr); + /** + * @tc.steps:step2. Register the non-null observer. + */ + auto observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + ASSERT_EQ(kvStore->RegisterObserver(observer), OK); + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + CipherPassword passwd; // random password + vector passwdBuffer(10, 45); // 10 and 45 as random password. + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + EXPECT_EQ(kvStore->Rekey(passwd), BUSY); + /** + * @tc.steps:step4. Unregister the observer. + */ + EXPECT_EQ(kvStore->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(kvStore->Rekey(passwd), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvStore), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); +} + +/** + * @tc.name: LocalDatabaseRekeyCheck001 + * @tc.desc: Attempt to rekey while another delegate is existed. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDatabaseTest, LocalDatabaseRekeyCheck001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the local delegate. + */ + /** + * @tc.steps:step2. Get another local delegate. + */ + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + /** + * @tc.steps:step4. Close the kv store delegate. + */ + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + CheckRekeyWithMultiKvStore(true); +} + +/** + * @tc.name: LocalDatabaseRekeyCheck002 + * @tc.desc: Attempt to rekey while the snapshot is existed. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDatabaseTest, LocalDatabaseRekeyCheck002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the local delegate. + */ + /** + * @tc.steps:step2. Get the snapshot through the delegate. + */ + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + /** + * @tc.steps:step4. Release the snapshot. + */ + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + CheckRekeyWithExistedSnapshot(true); +} + +/** + * @tc.name: MultiVerRekeyCheck001 + * @tc.desc: Attempt to rekey while another delegate is existed. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDatabaseTest, MultiVerRekeyCheck001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the multi version delegate. + */ + /** + * @tc.steps:step2. Get another multi version delegate. + */ + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + /** + * @tc.steps:step4. Close the kv store delegate. + */ + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + CheckRekeyWithMultiKvStore(false); +} + +/** + * @tc.name: MultiVerRekeyCheck002 + * @tc.desc: Attempt to rekey while the snapshot is existed. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDatabaseTest, MultiVerRekeyCheck002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the multi version delegate. + */ + /** + * @tc.steps:step2. Get the snapshot through the delegate. + */ + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + /** + * @tc.steps:step4. Release the snapshot. + */ + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + CheckRekeyWithExistedSnapshot(false); +} + +/** + * @tc.name: MultiVerRekeyCheck003 + * @tc.desc: Attempt to rekey while the observer is existed. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDatabaseTest, MultiVerRekeyCheck003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the delegate. + */ + /** + * @tc.steps:step2. Register the non-null observer. + */ + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + /** + * @tc.steps:step4. Unregister the observer. + */ + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + CheckRekeyWithExistedObserver(false); +} + +/** + * @tc.name: SingleVerRekeyCheck001 + * @tc.desc: Attempt to rekey while another delegate is existed. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDatabaseTest, SingleVerRekeyCheck001, TestSize.Level1) +{ + DBStatus status; + KvStoreNbDelegate *kvStore1 = nullptr; + auto delegateCallback1 = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(status), std::ref(kvStore1)); + /** + * @tc.steps:step1. Get the single version delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(STORE_ID2, option, delegateCallback1); + ASSERT_TRUE(kvStore1 != nullptr); + + KvStoreNbDelegate *kvStore2 = nullptr; + auto delegateCallback2 = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(status), std::ref(kvStore2)); + /** + * @tc.steps:step2. Get another single version delegate. + */ + option.createIfNecessary = false; + g_mgr.GetKvStore(STORE_ID2, option, delegateCallback2); + ASSERT_TRUE(kvStore2 != nullptr); + + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + CipherPassword passwd; + vector passwdBuffer(10, 45); + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + EXPECT_EQ(kvStore1->Rekey(passwd), BUSY); + /** + * @tc.steps:step4. Close the kv store delegate. + */ + EXPECT_EQ(g_mgr.CloseKvStore(kvStore2), OK); + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(kvStore1->Rekey(passwd), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvStore1), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID2), OK); +} + +/** + * @tc.name: SingleVerRekeyCheck002 + * @tc.desc: Attempt to rekey when the observer exists. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDatabaseTest, SingleVerRekeyCheck002, TestSize.Level1) +{ + DBStatus status; + KvStoreNbDelegate *kvStore = nullptr; + auto delegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(status), std::ref(kvStore)); + /** + * @tc.steps:step1. Get the single version delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(STORE_ID2, option, delegateCallback); + ASSERT_TRUE(kvStore != nullptr); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the empty key. + */ + Key key; + EXPECT_EQ(kvStore->RegisterObserver(key, 4, observer), OK); // Only use the event 4. + + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + CipherPassword passwd; + vector passwdBuffer(10, 45); + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + EXPECT_EQ(kvStore->Rekey(passwd), BUSY); + /** + * @tc.steps:step4. Unregister the observer. + */ + EXPECT_EQ(kvStore->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(kvStore->Rekey(passwd), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvStore), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID2), OK); +} + +static void NotifierCallback(const KvStoreNbConflictData &data) +{ +} +/** + * @tc.name: SingleVerRekeyCheck003 + * @tc.desc: Attempt to rekey while the conflict notifier is set. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDatabaseTest, SingleVerRekeyCheck003, TestSize.Level1) +{ + DBStatus status; + KvStoreNbDelegate *kvStore = nullptr; + auto delegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(status), std::ref(kvStore)); + /** + * @tc.steps:step1. Get the single version delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(STORE_ID2, option, delegateCallback); + ASSERT_TRUE(kvStore != nullptr); + /** + * @tc.steps:step2. Set the non-null conflict notifier. + */ + const int conflictAll = 15; + ASSERT_EQ(kvStore->SetConflictNotifier(conflictAll, NotifierCallback), OK); + CipherPassword passwd; + vector passwdBuffer(10, 45); + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + /** + * @tc.steps:step3. Execute the rekey operation. + * @tc.expected: step3. return BUSY. + */ + EXPECT_EQ(kvStore->Rekey(passwd), BUSY); + /** + * @tc.steps:step4. Set the null conflict notifier to unregister the conflict notifier. + */ + EXPECT_EQ(kvStore->SetConflictNotifier(1, nullptr), OK); + /** + * @tc.steps:step5. Execute the rekey operation. + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(kvStore->Rekey(passwd), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvStore), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID2), OK); +} +#endif diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_delegate_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_delegate_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f2d9b53f2569aef0134f02f25396a9665086bc31 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_delegate_test.cpp @@ -0,0 +1,1112 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OMIT_ENCRYPT +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "kv_store_nb_conflict_data.h" +#include "log_print.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_utils.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + const string STORE_ID1 = "store1_singleVersionDB"; + const string STORE_ID2 = "store2_localDB"; + const string STORE_ID3 = "store3_multiVersionDB"; + CipherPassword g_passwd1; // 5 '1' + CipherPassword g_passwd2; // 5 '2' + CipherPassword g_passwd3; // 5 '3' + const CipherPassword PASSWD_EMPTY; + const int CONFLICT_ALL = 15; + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + string g_testDir; + KvStoreConfig g_config; + DBStatus g_errCode = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_errCode), std::ref(g_kvNbDelegatePtr)); + + // define the delegate call back + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_errCode), std::ref(g_kvDelegatePtr)); + + DBStatus g_snapshotDelegateStatus = INVALID_ARGS; + KvStoreSnapshotDelegate *g_snapshotDelegatePtr = nullptr; + + auto g_snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_snapshotDelegateStatus), std::ref(g_snapshotDelegatePtr)); + + DBStatus g_valueStatus = INVALID_ARGS; + Value g_value; + + auto g_valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(g_valueStatus), std::ref(g_value)); + + void GetSnapshotAndValueCheck(const Key &testKey, const Value &testValue, DBStatus errCode) + { + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, g_snapshotDelegateCallback); + EXPECT_TRUE(g_snapshotDelegateStatus == OK); + ASSERT_TRUE(g_snapshotDelegatePtr != nullptr); + // check value and errCode + g_snapshotDelegatePtr->Get(testKey, g_valueCallback); + EXPECT_EQ(g_valueStatus, errCode); + + if (errCode == OK) { + EXPECT_TRUE(g_value.size() > 0); + if (g_value.size() > 0) { + EXPECT_EQ(g_value.front(), testValue[0]); + } + } + } + + void GetNbKvStoreAndCheckFun(const std::string &storeId, const KvStoreNbDelegate::Option &option, + const Key &testKey, const Value &testValue) + { + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(testKey, testValue), OK); + Value valueRead; + EXPECT_EQ(g_kvNbDelegatePtr->Get(testKey, valueRead), OK); + EXPECT_EQ(valueRead, testValue); + } + + void NotifierConnectTestCallback(const KvStoreNbConflictData &data) + { + g_errCode = INVALID_ARGS; + } +} + +class DistributedDBInterfacesEncryptDelegateTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesEncryptDelegateTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + vector passwdBuffer1(5, 1); // 5 and 1 as random password. + int errCode = g_passwd1.SetValue(passwdBuffer1.data(), passwdBuffer1.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer2(5, 2); // 5 and 2 as random password. + errCode = g_passwd2.SetValue(passwdBuffer2.data(), passwdBuffer2.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer3(5, 3); // 5 and 3 as random password. + errCode = g_passwd3.SetValue(passwdBuffer3.data(), passwdBuffer3.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); +} + +void DistributedDBInterfacesEncryptDelegateTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesEncryptDelegateTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_errCode = INVALID_ARGS; + g_kvNbDelegatePtr = nullptr; + g_kvDelegatePtr = nullptr; +} + +void DistributedDBInterfacesEncryptDelegateTest::TearDown(void) {} + +/** + * @tc.name: EncryptedDbOperation001 + * @tc.desc: Test the single version db encrypted function. + * @tc.type: FUNC + * @tc.require: AR000CQDT5 AR000CQDT6 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbOperation001, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID1, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_TRUE(g_errCode == OK); + /** + * @tc.steps: step1. Put [KEY_1, V1] + * @tc.expected: step1. Get result OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps: step2. Close db and open it again ,then get the value of K1 + * @tc.expected: step2. Close and open db successfully, value of K1 is V1 + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_1); + /** + * @tc.steps: step3. Put [KEY_1, V2] + * @tc.expected: step3. Get result OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps: step4. Close db and open it again ,then get the value of K1 + * @tc.expected: step4. Close and open db successfully, value of K1 is V2 + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + g_mgr.GetKvStore(STORE_ID1, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + Value valueRead2; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, valueRead2), OK); + EXPECT_EQ(valueRead2, VALUE_2); + /** + * @tc.steps: step5. Delete record K1 + * @tc.expected: step5. Get result OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_1), OK); + /** + * @tc.steps: step6. Close db and open it again ,then get the value of K1 + * @tc.expected: step6. Close and open db successfully, get value of K1 NOT_FOUND + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + g_mgr.GetKvStore(STORE_ID1, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, valueRead2), NOT_FOUND); + + // additional test + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + option.passwd = g_passwd2; + g_mgr.GetKvStore(STORE_ID1, option, g_kvNbDelegateCallback); + EXPECT_EQ(g_kvNbDelegatePtr, nullptr); + EXPECT_EQ(g_errCode, INVALID_PASSWD_OR_CORRUPTED_DB); + + // finilize logic + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), INVALID_ARGS); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbOperation002 + * @tc.desc: Test the local db encrypted function. + * @tc.type: FUNC + * @tc.require: AR000CQDT5 AR000CQDT6 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbOperation002, TestSize.Level1) +{ + KvStoreDelegate::Option option = {true, true, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + /** + * @tc.steps: step1. Put [KEY_1, V1] + * @tc.expected: step1. Get result OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps: step2. Close db and open it again ,then get the value of K1 + * @tc.expected: step2. Close and open db successfully, value of K1 is V1 + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_1, OK); + + /** + * @tc.steps: step3. Put [KEY_1, V2] + * @tc.expected: step3. Get result OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps: step4. Close db and open it again ,then get the value of K1 + * @tc.expected: step4. Close and open db successfully, value of K1 is V2 + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_2, OK); + /** + * @tc.steps: step5. Delete record K1 + * @tc.expected: step5. Get result OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Delete(KEY_1), OK); + /** + * @tc.steps: step6. Close db and open it again ,then get the value of K1 + * @tc.expected: step6. Close and open db successfully, get value of K1 NOT_FOUND + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + GetSnapshotAndValueCheck(KEY_1, VALUE_2, NOT_FOUND); + + // additional test + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + option.passwd = g_passwd2; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + EXPECT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_EQ(g_errCode, INVALID_PASSWD_OR_CORRUPTED_DB); + + // finilize logic + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID2), OK); +} + +/** + * @tc.name: EncryptedDbOperation003 + * @tc.desc: Test the multi version db encrypted function. + * @tc.type: FUNC + * @tc.require: AR000CQDT5 AR000CQDT6 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbOperation003, TestSize.Level1) +{ + KvStoreDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + /** + * @tc.steps: step1. Put [KEY_1, V1] + * @tc.expected: step1. Get result OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps: step2. Close db and open it again ,then get the value of K1 + * @tc.expected: step2. Close and open db successfully, value of K1 is V1 + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_1, OK); + /** + * @tc.steps: step3. Put [KEY_1, V2] + * @tc.expected: step3. Get result OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps: step4. Close db and open it again ,then get the value of K1 + * @tc.expected: step4. Close and open db successfully, value of K1 is V2 + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_2, OK); + /** + * @tc.steps: step5. Delete record K1 + * @tc.expected: step5. Get result OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Delete(KEY_1), OK); + /** + * @tc.steps: step6. Close db and open it again ,then get the value of K1 + * @tc.expected: step6. Close and open db successfully, get value of K1 NOT_FOUND + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_2, NOT_FOUND); + + // additional test + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + option.passwd = g_passwd2; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + EXPECT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_EQ(g_errCode, INVALID_PASSWD_OR_CORRUPTED_DB); + + // finilize logic + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID3), OK); +} + +/** + * @tc.name: EncryptedDbSwitch001 + * @tc.desc: Test the single version db for Rekey function. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch001, TestSize.Level1) +{ + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_1); + /** + * @tc.steps: step3. Rekey passwd to passwd2 + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(g_passwd2), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step4/step5. Put and get [KEY_1, V2] + * @tc.expected: step4/step5. Get value of KEY_1, value of K1 is V2. + */ + option.passwd = g_passwd2; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_2); + + /** + * @tc.steps: step6. Rekey passwd to empty + * @tc.expected: step6. result is OK + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(PASSWD_EMPTY), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step7/step8. Put and get [KEY_1, V3] + * @tc.expected: step7/step8. Get value of KEY_1, value of K1 is V3. + */ + option.isEncryptedDb = false; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_3); + /** + * @tc.steps: step9. Rekey passwd to passwd3 + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(g_passwd3), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step10/step11. Put and get [KEY_1, V4] + * @tc.expected: step10/step11. Get value of KEY_1, value of K1 is V4. + */ + option.isEncryptedDb = true; + option.passwd = g_passwd3; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_4); + + // finilize logic + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbSwitch002 + * @tc.desc: Test the single version db Rekey function return BUSY because of multiple instances. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch002, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID1, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + /** + * @tc.steps: step1. open same database again + * @tc.expected: step1. Get result OK + */ + DBStatus errCode = INVALID_ARGS; + KvStoreNbDelegate *kvNbDelegatePtr = nullptr; + auto kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(errCode), std::ref(kvNbDelegatePtr)); + g_mgr.GetKvStore(STORE_ID1, option, kvNbDelegateCallback); + ASSERT_TRUE(kvNbDelegatePtr != nullptr); + EXPECT_TRUE(errCode == OK); + /** + * @tc.steps: step2. invoke rekey logic + * @tc.expected: step2. Get result BUSY + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(g_passwd2), BUSY); + // finilize logic + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); +} + +/** + * @tc.name: EncryptedDbSwitch003 + * @tc.desc: Test the single version db Rekey function return BUSY because of observer. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch003, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID1, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + /** + * @tc.steps: step1. register observer + * @tc.expected: step1. Get result OK + */ + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_NE(observer, nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, observer), OK); + /** + * @tc.steps: step2. invoke rekey logic + * @tc.expected: step2. Get result BUSY + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(g_passwd2), BUSY); + // finilize logic + delete observer; + observer = nullptr; + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); +} + +/** + * @tc.name: EncryptedDbSwitch004 + * @tc.desc: Test the single version db Rekey function return BUSY because of conflict notifier. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch004, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID1, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + /** + * @tc.steps: step1. register observer + * @tc.expected: step1. Get result OK + */ + EXPECT_EQ(g_kvNbDelegatePtr->SetConflictNotifier(CONFLICT_ALL, NotifierConnectTestCallback), OK); + /** + * @tc.steps: step2. invoke rekey logic + * @tc.expected: step2. Get result BUSY + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(g_passwd2), BUSY); + // finilize logic + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); +} + +/** + * @tc.name: EncryptedDbSwitch008 + * @tc.desc: Test the multi version db Rekey function return BUSY because of Snapshot not close. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch008, TestSize.Level1) +{ +} + +/** + * @tc.name: EncryptedDbSwitch009 + * @tc.desc: Test the single version db Rekey function from password1 to password2. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch009, TestSize.Level1) +{ + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_1); + /** + * @tc.steps: step3. Rekey passwd to passwd2 + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(g_passwd2), OK); + Value valueRead1; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, valueRead1), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step4/step5. Put and get [KEY_1, V2] + * @tc.expected: step4/step5. Get value of KEY_1, value of K1 is V2. + */ + option.passwd = g_passwd2; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_2); + // finilize logic + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbSwitch010 + * @tc.desc: Test the single version db Rekey function from encrypted db unencrypted db . + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch010, TestSize.Level1) +{ + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_1); + /** + * @tc.steps: step6. Rekey passwd to empty + * @tc.expected: step6. result is OK + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(PASSWD_EMPTY), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step7/step8. Put and get [KEY_1, V3] + * @tc.expected: step7/step8. Get value of KEY_1, value of K1 is V3. + */ + option.isEncryptedDb = false; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_3); + // finilize logic + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbSwitch011 + * @tc.desc: Test the single version db Rekey function from unencrypted db to encrypted db. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch011, TestSize.Level1) +{ + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + KvStoreNbDelegate::Option option = {true, false, false, CipherType::DEFAULT, PASSWD_EMPTY}; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_1); + /** + * @tc.steps: step3. Rekey passwd to passwd2 + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(g_passwd2), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step4/step5. Put and get [KEY_1, V2] + * @tc.expected: step4/step5. Get value of KEY_1, value of K1 is V2. + */ + option.passwd = g_passwd2; + option.isEncryptedDb = true; + GetNbKvStoreAndCheckFun(STORE_ID1, option, KEY_1, VALUE_2); + // finilize logic + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbSwitch012 + * @tc.desc: Test the local db Rekey function from password1 to password2. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch012, TestSize.Level1) +{ + KvStoreDelegate::Option option = {true, true, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_1), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_1, OK); + + /** + * @tc.steps: step3. Rekey passwd to passwd2 + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_kvDelegatePtr->Rekey(g_passwd2), OK); + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_3), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step4/step5. Put and get [KEY_1, V2] + * @tc.expected: step4/step5. Get value of KEY_1, value of K1 is V2. + */ + option.passwd = g_passwd2; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_2), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_2, OK); + // finilize logic + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID2), OK); + g_kvDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbSwitch013 + * @tc.desc: Test the local db Rekey function from encrypted db unencrypted db . + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch013, TestSize.Level1) +{ + KvStoreDelegate::Option option = {true, true, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_1), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_1, OK); + /** + * @tc.steps: step3. Rekey passwd to empty + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_kvDelegatePtr->Rekey(PASSWD_EMPTY), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step4/step5. Put and get [KEY_1, V2] + * @tc.expected: step4/step5. Get value of KEY_1, value of K1 is V2. + */ + option.isEncryptedDb = false; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_2), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_2, OK); + // finilize logic + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID2), OK); + g_kvDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbSwitch014 + * @tc.desc: Test the local db Rekey function from unencrypted db to encrypted db. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch014, TestSize.Level1) +{ + KvStoreDelegate::Option option = {true, true, false, CipherType::DEFAULT, PASSWD_EMPTY}; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_1), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_1, OK); + /** + * @tc.steps: step3. Rekey passwd to passwd2 + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_kvDelegatePtr->Rekey(g_passwd2), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step4/step5. Put and get [KEY_1, V2] + * @tc.expected: step4/step5. Get value of KEY_1, value of K1 is V2. + */ + option.passwd = g_passwd2; + option.isEncryptedDb = true; + g_mgr.GetKvStore(STORE_ID2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_2), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_2, OK); + // finilize logic + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID2), OK); + g_kvDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbSwitch015 + * @tc.desc: Test the multi version db Rekey function from password1 to password2. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch015, TestSize.Level1) +{ + KvStoreDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_1), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_1, OK); + /** + * @tc.steps: step3. Rekey passwd to passwd2 + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_kvDelegatePtr->Rekey(g_passwd2), OK); + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_3), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_3, OK); + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step4/step5. Put and get [KEY_1, V2] + * @tc.expected: step4/step5. Get value of KEY_1, value of K1 is V2. + */ + option.passwd = g_passwd2; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_2), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_2, OK); + // finilize logic + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID3), OK); + g_kvDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbSwitch016 + * @tc.desc: Test the multi version db Rekey function from encrypted db unencrypted db . + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch016, TestSize.Level1) +{ + KvStoreDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_1), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_1, OK); + /** + * @tc.steps: step3. Rekey passwd to empty + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_kvDelegatePtr->Rekey(PASSWD_EMPTY), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step4/step5. Put and get [KEY_1, V2] + * @tc.expected: step4/step5. Get value of KEY_1, value of K1 is V2. + */ + option.isEncryptedDb = false; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_2), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_2, OK); + // finilize logic + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID3), OK); + g_kvDelegatePtr = nullptr; +} + +/** + * @tc.name: EncryptedDbSwitch017 + * @tc.desc: Test the multi version db Rekey function from unencrypted db to encrypted db. + * @tc.type: FUNC + * @tc.require: AR000CQDT7 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, EncryptedDbSwitch017, TestSize.Level1) +{ + KvStoreDelegate::Option option = {true, false, false, CipherType::DEFAULT, PASSWD_EMPTY}; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + /** + * @tc.steps: step1/step2. Put and get [KEY_1, V1] + * @tc.expected: step1/step2. Get value of KEY_1, value of K1 is V1. + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_1), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_1, OK); + /** + * @tc.steps: step3. Rekey passwd to passwd2 + * @tc.expected: step3. result is OK + */ + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_kvDelegatePtr->Rekey(g_passwd2), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + g_errCode = INVALID_ARGS; + /** + * @tc.steps: step4/step5. Put and get [KEY_1, V2] + * @tc.expected: step4/step5. Get value of KEY_1, value of K1 is V2. + */ + option.passwd = g_passwd2; + option.isEncryptedDb = true; + g_mgr.GetKvStore(STORE_ID3, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_1, VALUE_2), OK); + + GetSnapshotAndValueCheck(KEY_1, VALUE_2, OK); + // finilize logic + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID3), OK); + g_kvDelegatePtr = nullptr; +} + +/** + * @tc.name: OpenEncryptedDb001 + * @tc.desc: Test create an encrypted database successfully. + * @tc.type: FUNC + * @tc.require: AR000CQDT5 AR000CQDT6 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, OpenEncryptedDb001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create single version encrypted database + * @tc.expected: step1. Get result OK. + */ + KvStoreNbDelegate::Option option1 = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID1, option1, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_errCode == OK); + /** + * @tc.steps: step2. create multi version encrypted database + * @tc.expected: step2. Get result OK. + */ + KvStoreDelegate::Option option2 = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID3, option2, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + /** + * @tc.steps: step3. Close db. + * @tc.expected: step3. Get result ok. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID3), OK); +} + +/** + * @tc.name: OpenEncryptedDb002 + * @tc.desc: Test create an encrypted database failed. + * @tc.type: FUNC + * @tc.require: AR000CQDT8 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, OpenEncryptedDb002, TestSize.Level1) +{ + /** + * @tc.steps: step1. create single version encrypted database + * @tc.expected: step1. Get result INVALID_ARGS. + */ + KvStoreNbDelegate::Option option1 = {true, false, true, CipherType::DEFAULT, PASSWD_EMPTY}; + g_mgr.GetKvStore(STORE_ID1, option1, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_EQ(g_errCode, INVALID_ARGS); + /** + * @tc.steps: step2. create multi version encrypted database + * @tc.expected: step2. Get result INVALID_ARGS. + */ + KvStoreDelegate::Option option2 = {true, false, true, CipherType::DEFAULT, PASSWD_EMPTY}; + g_mgr.GetKvStore(STORE_ID3, option2, g_kvDelegateCallback); + EXPECT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_EQ(g_errCode, INVALID_ARGS); + /** + * @tc.steps: step3. Close db. + * @tc.expected: step3. Get result ok. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), INVALID_ARGS); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), INVALID_ARGS); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), NOT_FOUND); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID3), NOT_FOUND); +} + +/** + * @tc.name: OpenEncryptedDb003 + * @tc.desc: Test reopen an encrypted database successfully. + * @tc.type: FUNC + * @tc.require: AR000CQDT8 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, OpenEncryptedDb003, TestSize.Level1) +{ + KvStoreNbDelegate::Option option1 = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID1, option1, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + KvStoreDelegate::Option option2 = {true, false, true, CipherType::DEFAULT, g_passwd3}; + g_mgr.GetKvStore(STORE_ID3, option2, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + /** + * @tc.steps: step1. create single version encrypted database + * @tc.expected: step1. Get result INVALID_ARGS. + */ + KvStoreNbDelegate::Option option3 = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID1, option3, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + /** + * @tc.steps: step2. create multi version encrypted database + * @tc.expected: step2. Get result INVALID_ARGS. + */ + KvStoreDelegate::Option option4 = {true, false, true, CipherType::DEFAULT, g_passwd3}; + g_mgr.GetKvStore(STORE_ID3, option4, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + /** + * @tc.steps: step3. Close db. + * @tc.expected: step3. Get result ok. + */ + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID3), OK); +} + +/** + * @tc.name: OpenEncryptedDb004 + * @tc.desc: Test reopen an encrypted database failed. + * @tc.type: FUNC + * @tc.require: AR000CQDT8 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesEncryptDelegateTest, OpenEncryptedDb004, TestSize.Level1) +{ + KvStoreNbDelegate::Option option1 = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(STORE_ID1, option1, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + KvStoreDelegate::Option option2 = {true, false, true, CipherType::DEFAULT, g_passwd3}; + g_mgr.GetKvStore(STORE_ID3, option2, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_errCode, OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + /** + * @tc.steps: step1. create single version encrypted database + * @tc.expected: step1. Get result INVALID_ARGS. + */ + KvStoreNbDelegate::Option option3 = {true, false, true, CipherType::DEFAULT, g_passwd2}; + g_mgr.GetKvStore(STORE_ID1, option3, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_EQ(g_errCode, INVALID_PASSWD_OR_CORRUPTED_DB); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), INVALID_ARGS); + /** + * @tc.steps: step2. create multi version encrypted database + * @tc.expected: step2. Get result INVALID_ARGS. + */ + KvStoreDelegate::Option option4 = {true, false, true, CipherType::DEFAULT, g_passwd2}; + g_mgr.GetKvStore(STORE_ID3, option4, g_kvDelegateCallback); + EXPECT_TRUE(g_kvDelegatePtr == nullptr); + EXPECT_EQ(g_errCode, INVALID_PASSWD_OR_CORRUPTED_DB); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), INVALID_ARGS); + /** + * @tc.steps: step3. Close db. + * @tc.expected: step3. Get result ok. + */ + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID1), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID3), OK); +} +#endif diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_import_and_export_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_import_and_export_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28279b62036f9dea8df70f89930ee824beef4fb7 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_import_and_export_test.cpp @@ -0,0 +1,1132 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OMIT_ENCRYPT +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "platform_specific.h" +#include "process_communicator_test_stub.h" +#include "process_system_api_adapter_impl.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + string g_testDir; + KvStoreConfig g_config; + std::string g_exportFileDir; + std::vector g_junkFilesList; + + // define the g_kvNbDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + KvStoreNbDelegate *g_kvNbDelegatePtrWithoutPasswd = nullptr; + KvStoreDelegate *g_kvDelegatePtrWithoutPasswd = nullptr; + KvStoreDelegate::Option g_option; + const size_t MAX_PASSWD_SIZE = 128; + + // define the g_valueCallback, used to query a value object data from the kvdb. + DBStatus g_valueStatus = INVALID_ARGS; + Value g_value; + + CipherPassword g_passwd1; + CipherPassword g_passwd2; + CipherPassword g_passwd3; + CipherPassword g_passwd4; + // the type of g_valueCallback is function + auto g_valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(g_valueStatus), std::ref(g_value)); + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + void RemoveJunkFile(const std::vector &fileList) + { + for (auto &junkFile : fileList) { + std::ifstream file(junkFile); + if (file) { + file.close(); + int result = remove(junkFile.c_str()); + if (result < 0) { + LOGE("failed to delete the db file:%d", errno); + } + } + } + return; + } +} + +class DistributedDBInterfacesImportAndExportTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesImportAndExportTest::SetUpTestCase(void) +{ + g_mgr.SetProcessLabel("6666", "8888"); + g_mgr.SetProcessCommunicator(std::make_shared()); + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + g_exportFileDir = g_testDir + "/ExportDir"; + OS::MakeDBDirectory(g_exportFileDir); + vector passwdBuffer1(5, 1); // 5 and 1 as random password. + int errCode = g_passwd1.SetValue(passwdBuffer1.data(), passwdBuffer1.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer2(5, 2); // 5 and 2 as random password. + errCode = g_passwd2.SetValue(passwdBuffer2.data(), passwdBuffer2.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer3(5, 3); // 5 and 3 as random password. + errCode = g_passwd3.SetValue(passwdBuffer3.data(), passwdBuffer3.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer4(5, 4); // 5 and 4 as random password. + errCode = g_passwd4.SetValue(passwdBuffer4.data(), passwdBuffer4.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); +} + +void DistributedDBInterfacesImportAndExportTest::TearDownTestCase(void) +{ + OS::RemoveDBDirectory(g_exportFileDir); + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesImportAndExportTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_junkFilesList.clear(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvNbDelegatePtr = nullptr; + g_kvDelegatePtr = nullptr; +} + +void DistributedDBInterfacesImportAndExportTest::TearDown(void) +{ + RemoveJunkFile(g_junkFilesList); +} + +/** + * @tc.name: NormalExport001 + * @tc.desc: The data of the current version of the board is exported and the package file is single. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, NormalExport001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Pre-create folder dir + */ + std::string singleExportFileName = g_exportFileDir + "/singleNormalExport001.$$"; + std::string singleStoreId = "distributed_ExportSingle_001"; + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step2. Specify the path to export the non-encrypted board database. + * @tc.expected: step2. Returns OK + */ + CipherPassword passwd; + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, passwd), OK); + + std::string mulitExportFileName = g_exportFileDir + "/mulitNormalExport001.$$"; + std::string multiStoreId = "distributed_ExportMulit_001"; + g_mgr.GetKvStore(multiStoreId, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step3. Specify the path to export the multi-version non-encrypted database. + * @tc.expected: step3. Returns OK + */ + EXPECT_EQ(g_kvDelegatePtr->Export(mulitExportFileName, passwd), OK); + + // clear resource + g_junkFilesList.push_back(singleExportFileName); + g_junkFilesList.push_back(mulitExportFileName); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); +} + +/** + * @tc.name: UndisturbedlSingleExport001 + * @tc.desc: Check that the export action is an independent transaction. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, UndisturbedlSingleExport001, TestSize.Level1) +{ + std::string singleStoreId = "distributed_ExportSingle_002"; + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step1. Three known data records are preset in the board database. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + g_kvNbDelegatePtr->Put(KEY_2, VALUE_2); + g_kvNbDelegatePtr->Put(KEY_3, VALUE_3); + + /** + * @tc.steps: step2. Execute the export action. + */ + std::string singleExportFileName = g_exportFileDir + "/UndisturbedlSingleExport001.$$"; + CipherPassword passwd; + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, passwd), OK); + + /** + * @tc.steps: step3. Insert multiple new data records into the database. + */ + g_kvNbDelegatePtr->Put(KEY_4, VALUE_4); + g_kvNbDelegatePtr->Put(KEY_5, VALUE_5); + + /** + * @tc.steps: step4. Import backup data. + * @tc.expected: step4. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), OK); + + /** + * @tc.steps: step5. Check whether the imported data is the preset content in step 1. + * @tc.expected: step5. Three preset data records are found. + */ + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_3, readValue), OK); + EXPECT_EQ(readValue, VALUE_3); + + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_4, readValue), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_5, readValue), NOT_FOUND); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); + g_junkFilesList.push_back(singleExportFileName); +} + +static void GetSnapshotUnitTest(KvStoreDelegate *&kvDelegatePtr, KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus snapshotDelegateStatus = INVALID_ARGS; + auto snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(snapshotDelegateStatus), std::ref(snapshotDelegatePtr)); + + kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallback); + EXPECT_TRUE(snapshotDelegateStatus == OK); + ASSERT_TRUE(snapshotDelegatePtr != nullptr); +} + +/** + * @tc.name: UndisturbedlMultiExport001 + * @tc.desc: Check that the export action is an independent transaction. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, UndisturbedlMultiExport001, TestSize.Level1) +{ + std::string multiStoreId = "distributed_Exportmulit_001"; + g_mgr.GetKvStore(multiStoreId, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step1. Three known data records are preset in the board database. + */ + g_kvDelegatePtr->Put(KEY_1, VALUE_1); + g_kvDelegatePtr->Put(KEY_2, VALUE_2); + g_kvDelegatePtr->Put(KEY_3, VALUE_3); + + /** + * @tc.steps: step2. Execute the export action. + */ + std::string mulitExportFileName = g_exportFileDir + "/UndisturbedlMultiExport001.$$"; + CipherPassword passwd; + EXPECT_EQ(g_kvDelegatePtr->Export(mulitExportFileName, passwd), OK); + + /** + * @tc.steps: step3. Insert multiple new data records into the database. + */ + g_kvDelegatePtr->Put(KEY_4, VALUE_4); + g_kvDelegatePtr->Put(KEY_5, VALUE_5); + + /** + * @tc.steps: step4. Import backup data. + * @tc.expected: step4. Returns OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Import(mulitExportFileName, passwd), OK); + + KvStoreSnapshotDelegate *snapshotDelegatePtr = nullptr; + GetSnapshotUnitTest(g_kvDelegatePtr, snapshotDelegatePtr); + + /** + * @tc.steps: step5. Check whether the imported data is the preset content in step 1. + * @tc.expected: step5. Three preset data records are found. + */ + snapshotDelegatePtr->Get(KEY_1, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(g_value, VALUE_1); + snapshotDelegatePtr->Get(KEY_2, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(g_value, VALUE_2); + snapshotDelegatePtr->Get(KEY_3, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(g_value, VALUE_3); + + snapshotDelegatePtr->Get(KEY_4, g_valueCallback); + EXPECT_EQ(g_valueStatus, NOT_FOUND); + snapshotDelegatePtr->Get(KEY_5, g_valueCallback); + EXPECT_EQ(g_valueStatus, NOT_FOUND); + + if (g_kvDelegatePtr != nullptr && snapshotDelegatePtr != nullptr) { + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotDelegatePtr) == OK); + snapshotDelegatePtr = nullptr; + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); + g_junkFilesList.push_back(mulitExportFileName); +} + +/** + * @tc.name: ExportParameterCheck001 + * @tc.desc: Check the verification of abnormal interface parameters. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, ExportParameterCheck001, TestSize.Level1) +{ + std::string singleStoreId = "distributed_ExportSingle_003"; + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + + /** + * @tc.steps: step1. The filePath path does not exist. + * @tc.expected: step1. Return INVALID_ARGS. + */ + std::string invalidFileName = g_exportFileDir + "/tempNotCreated/" + "/ExportParameterCheck001.$$"; + CipherPassword passwd; + EXPECT_EQ(g_kvNbDelegatePtr->Export(invalidFileName, passwd), INVALID_ARGS); + + /** + * @tc.steps: step2. Password length MAX_PASSWD_SIZE + 1 + * @tc.expected: step2. Return INVALID_ARGS. + */ + vector passwdBuffer(MAX_PASSWD_SIZE + 1, MAX_PASSWD_SIZE); + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OVERSIZE); + /** + * @tc.steps: step3. Password length MAX_PASSWD_SIZE + * @tc.expected: step3. Return OK. + */ + passwdBuffer.resize(MAX_PASSWD_SIZE, MAX_PASSWD_SIZE); + errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + std::string singleExportFileName = g_exportFileDir + "/ExportParameterCheck001.$$"; + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, passwd), OK); + // Check export FILE_ALREADY_EXISTED + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, passwd), FILE_ALREADY_EXISTED); + + /** + * @tc.steps: step4. Delete the database. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); + + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step5. Use the password to import the file again, + * @tc.expected: step5. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), OK); + Value readValue; + g_kvNbDelegatePtr->Get(KEY_1, readValue); + EXPECT_EQ(readValue, VALUE_1); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); + g_junkFilesList.push_back(singleExportFileName); +} + +/** + * @tc.name: ExportParameterCheck002 + * @tc.desc: Check the verification of abnormal interface parameters. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, ExportParameterCheck002, TestSize.Level1) +{ + std::string multiStoreId = "distributed_ExportMulti_003"; + g_mgr.GetKvStore(multiStoreId, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + g_kvDelegatePtr->Put(KEY_1, VALUE_1); + + /** + * @tc.steps: step1. The filePath path does not exist. + * @tc.expected: step1. Return INVALID_ARGS. + */ + std::string invalidExportFileName = g_exportFileDir + "/tempNotCreated/" + "/ExportParameterCheck002.$$"; + CipherPassword passwd; + EXPECT_EQ(g_kvDelegatePtr->Export(invalidExportFileName, passwd), INVALID_ARGS); + + /** + * @tc.steps: step2. Password length MAX_PASSWD_SIZE + 1 + * @tc.expected: step2. Return INVALID_ARGS. + */ + vector passwdBuffer(MAX_PASSWD_SIZE + 1, MAX_PASSWD_SIZE); + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OVERSIZE); + /** + * @tc.steps: step3. Password length MAX_PASSWD_SIZE + * @tc.expected: step3. Return OK. + */ + passwdBuffer.resize(MAX_PASSWD_SIZE, MAX_PASSWD_SIZE); + errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + std::string multiExportFileName = g_exportFileDir + "/ExportParameterCheck002.$$"; + EXPECT_EQ(g_kvDelegatePtr->Export(multiExportFileName, passwd), OK); + EXPECT_EQ(g_kvDelegatePtr->Export(multiExportFileName, passwd), FILE_ALREADY_EXISTED); // Check export INVALID_FILE + + /** + * @tc.steps: step4. Delete the database. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); + + g_mgr.GetKvStore(multiStoreId, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step5. Use the password to import the file again, + * @tc.expected: step5. Return OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Import(multiExportFileName, passwd), OK); + + KvStoreSnapshotDelegate *snapshotDelegatePtr = nullptr; + GetSnapshotUnitTest(g_kvDelegatePtr, snapshotDelegatePtr); + + snapshotDelegatePtr->Get(KEY_1, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(g_value, VALUE_1); + + if (g_kvDelegatePtr != nullptr && snapshotDelegatePtr != nullptr) { + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotDelegatePtr) == OK); + snapshotDelegatePtr = nullptr; + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); + g_junkFilesList.push_back(multiExportFileName); +} + +/** + * @tc.name: NormalImport001 + * @tc.desc: Normal import capability for single version, parameter verification capability + * @tc.type: FUNC + * @tc.require: AR000D487A + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, NormalImport001, TestSize.Level1) +{ + std::string singleExportFileName = g_exportFileDir + "/NormalImport001.$$"; + std::string singleStoreId = "distributed_Importmulti_001"; + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + + CipherPassword passwd; + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, passwd), OK); + + /** + * @tc.steps: step1. Import the invalid path. + * @tc.expected: step1. Return INVALID_ARGS. + */ + std::string invalidPath = g_exportFileDir + "sdad" + "/NormalImport001.$$"; + EXPECT_EQ(g_kvNbDelegatePtr->Import(invalidPath, passwd), INVALID_ARGS); + + /** + * @tc.steps: step2. Import an authorized path with an incorrect password. + * @tc.expected: step2. Return INVALID_FILE. + */ + vector passwdBuffer(MAX_PASSWD_SIZE, MAX_PASSWD_SIZE); + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), INVALID_FILE); + + /** + * @tc.steps: step3. Import a permission path without a password. + * @tc.expected: step3. Return OK. + */ + errCode = passwd.Clear(); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), OK); + + /** + * @tc.steps: step4. Check whether the data is the same as the backup database. + * @tc.expected: step4. Same database data. + */ + Value readValue; + g_kvNbDelegatePtr->Get(KEY_1, readValue); + EXPECT_EQ(readValue, VALUE_1); + + // clear resource + g_junkFilesList.push_back(singleExportFileName); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); +} + +/** + * @tc.name: NormalImport001 + * @tc.desc: Normal import capability for multi version, parameter verification capability + * @tc.type: FUNC + * @tc.require: AR000D487A + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, NormalImport002, TestSize.Level1) +{ + std::string multiExportFileName = g_exportFileDir + "/NormalImport002.$$"; + std::string multiStoreId = "distributed_ImportSingle_002"; + g_mgr.GetKvStore(multiStoreId, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + g_kvDelegatePtr->Put(KEY_1, VALUE_1); + + CipherPassword passwd; + EXPECT_EQ(g_kvDelegatePtr->Export(multiExportFileName, passwd), OK); + + /** + * @tc.steps: step1. Import the invalid path. + * @tc.expected: step1. Return INVALID_ARGS. + */ + std::string invalidPath = g_exportFileDir + "sdad" + "/NormalImport002.$$"; + EXPECT_EQ(g_kvDelegatePtr->Import(invalidPath, passwd), INVALID_ARGS); + + /** + * @tc.steps: step2. Import an authorized path with an incorrect password. + * @tc.expected: step2. Return INVALID_FILE. + */ + vector passwdBuffer(MAX_PASSWD_SIZE, MAX_PASSWD_SIZE); + int errCode = passwd.SetValue(passwdBuffer.data(), passwdBuffer.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + EXPECT_EQ(g_kvDelegatePtr->Import(multiExportFileName, passwd), INVALID_FILE); + + g_kvDelegatePtr->Delete(KEY_1); + /** + * @tc.steps: step3. Import a permission path without a password. + * @tc.expected: step3. Return OK. + */ + errCode = passwd.Clear(); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + EXPECT_EQ(g_kvDelegatePtr->Import(multiExportFileName, passwd), OK); + + KvStoreSnapshotDelegate *snapshotDelegatePtr = nullptr; + GetSnapshotUnitTest(g_kvDelegatePtr, snapshotDelegatePtr); + + /** + * @tc.steps: step4. Check whether the data is the same as the backup database. + * @tc.expected: step4. Same database data. + */ + snapshotDelegatePtr->Get(KEY_1, g_valueCallback); + EXPECT_EQ(g_valueStatus, OK); + EXPECT_EQ(g_value, VALUE_1); + + if (g_kvDelegatePtr != nullptr && snapshotDelegatePtr != nullptr) { + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshotDelegatePtr) == OK); + snapshotDelegatePtr = nullptr; + } + + // clear resource + g_junkFilesList.push_back(multiExportFileName); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); +} + +/** + * @tc.name: ExceptionFileImport001 + * @tc.desc: Normal import capability for single version, parameter verification capability + * @tc.type: FUNC + * @tc.require: AR000D487A + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, ExceptionFileImport001, TestSize.Level1) +{ + std::string singleExportFileName = g_exportFileDir + "/ExceptionFileImport001.$$"; + std::string singleStoreId = "distributed_ImportExceptionsigle_001"; + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + g_kvNbDelegatePtr->Put(KEY_2, VALUE_2); + + CipherPassword passwd; + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, passwd), OK); + + /** + * @tc.steps: step1. Repeat import backup file to same database. + * @tc.expected: step1. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), OK); + + /** + * @tc.steps: step2. Change the name of file1 to file2. + */ + std::string newSingleExportFileName = g_exportFileDir + "/newExceptionFileImport001.$$"; + EXPECT_EQ(rename(singleExportFileName.c_str(), newSingleExportFileName.c_str()), 0); + + /** + * @tc.steps: step3. Import file1 into the database. + * @tc.expected: step3. Return INVALID_FILE. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), INVALID_FILE); + + /** + * @tc.steps: step4. Import file2 into the database. + * @tc.expected: step4. Return INVALID_FILE. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Import(newSingleExportFileName, passwd), OK); + + // clear resource + g_junkFilesList.push_back(singleExportFileName); + g_junkFilesList.push_back(newSingleExportFileName); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); +} + +/** + * @tc.name: ExceptionFileImport002 + * @tc.desc: Normal import capability for multi version, parameter verification capability + * @tc.type: FUNC + * @tc.require: AR000D487A + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, ExceptionFileImport002, TestSize.Level1) +{ + std::string multiExportFileName = g_exportFileDir + "/ExceptionFileImport002.$$"; + std::string multiStoreId = "distributed_ImportExceptionMulti_001"; + g_mgr.GetKvStore(multiStoreId, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + g_kvDelegatePtr->Put(KEY_1, VALUE_1); + + CipherPassword passwd; + EXPECT_EQ(g_kvDelegatePtr->Export(multiExportFileName, passwd), OK); + + /** + * @tc.steps: step1. Import the backup file that has been tampered with to the multi-version database. + * @tc.expected: step1. Return INVALID_FILE. + */ + EXPECT_EQ(DistributedDBToolsUnitTest::ModifyDatabaseFile(multiExportFileName), 0); + EXPECT_EQ(g_kvDelegatePtr->Import(multiExportFileName, passwd), INVALID_FILE); + + // clear resource + g_junkFilesList.push_back(multiExportFileName); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); +} + +/** + * @tc.name: ExceptionFileImport003 + * @tc.desc: The data of the current version of the board is exported and the package file is single. + * @tc.type: FUNC + * @tc.require: AR000D487A + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, ExceptionFileImport003, TestSize.Level1) +{ + std::string singleExportFileName = g_exportFileDir + "/singleExceptionFileImport003.$$"; + std::string singleStoreId = "distributed_ExportSingle_001"; + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + CipherPassword passwd; + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, passwd), OK); + + std::string mulitExportFileName = g_exportFileDir + "/mulitExceptionFileImport003.$$"; + std::string multiStoreId = "distributed_ExportMulit_001"; + g_mgr.GetKvStore(multiStoreId, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvDelegatePtr->Export(mulitExportFileName, passwd), OK); + + /** + * @tc.steps: step1. Use the single ver import interface. The file path is a multi-version backup file. + * @tc.expected: step1. Return INVALID_FILE. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Import(mulitExportFileName, passwd), INVALID_FILE); + + /** + * @tc.steps: step2. Use the single ver import interface. The file path is a single-version backup file. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), OK); + + /** + * @tc.steps: step3. Use the multi-version import interface. The file path is a single-version backup file. + * @tc.expected: step3. Return INVALID_FILE. + */ + EXPECT_EQ(g_kvDelegatePtr->Import(singleExportFileName, passwd), INVALID_FILE); + + /** + * @tc.steps: step4. Use the multi-version import interface. The file path is a multi-version backup file. + * @tc.expected: step4. Return INVALID_FILE. + */ + EXPECT_EQ(g_kvDelegatePtr->Import(mulitExportFileName, passwd), OK); + + // clear resource + g_junkFilesList.push_back(singleExportFileName); + g_junkFilesList.push_back(mulitExportFileName); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); +} + +/** + * @tc.name: ExceptionFileImport004 + * @tc.desc: The data of the current version of the board is exported and the package file is single. + * @tc.type: FUNC + * @tc.require: AR000D487A + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, ExceptionFileImport004, TestSize.Level1) +{ + std::string singleExportFileName = g_exportFileDir + "/singleExceptionFileImport004.$$"; + std::string singleStoreId = "distributed_ExportSingle_004"; + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, g_passwd2), OK); + + std::string mulitExportFileName = g_exportFileDir + "/mulitExceptionFileImport004.$$"; + std::string multiStoreId = "distributed_ExportMulit_004"; + + KvStoreDelegate::Option multiOption = {true, false, true, CipherType::DEFAULT, g_passwd1}; + g_mgr.GetKvStore(multiStoreId, multiOption, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + EXPECT_EQ(g_kvDelegatePtr->Export(mulitExportFileName, g_passwd2), OK); + + /** + * @tc.steps: step1. Use the diff passwd, try to import database. + */ + CipherPassword passwd; + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), INVALID_FILE); + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, g_passwd1), INVALID_FILE); + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, g_passwd2), OK); + + EXPECT_EQ(g_kvDelegatePtr->Import(mulitExportFileName, passwd), INVALID_FILE); + EXPECT_EQ(g_kvDelegatePtr->Import(mulitExportFileName, g_passwd1), INVALID_FILE); + EXPECT_EQ(g_kvDelegatePtr->Import(mulitExportFileName, g_passwd2), OK); + + // clear resource + g_junkFilesList.push_back(singleExportFileName); + g_junkFilesList.push_back(mulitExportFileName); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); +} + +static void TryDbForPasswordIndependence001() +{ + std::string singleStoreIdNoPasswd = "distributed_ExportSingle_005"; + std::string singleStoreId = "distributed_ExportSingle_006"; + + /** + * @tc.steps: step4. Run the p3 command to open the database db1. + * @tc.expected: step4. Return ERROR. + */ + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd3}; + g_mgr.GetKvStore(singleStoreIdNoPasswd, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_NE(g_kvDelegateStatus, OK); + + /** + * @tc.steps: step5. Run the p4 command to open the database db2. + * @tc.expected: step5. Return ERROR. + */ + option = {true, false, true, CipherType::DEFAULT, g_passwd4}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + ASSERT_TRUE(g_kvDelegateStatus != OK); + + /** + * @tc.steps: step6. Open the db1 directly. + * @tc.expected: step6. Return OK. + */ + option = {true, false, false, CipherType::DEFAULT, g_passwd3}; + g_mgr.GetKvStore(singleStoreIdNoPasswd, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_TRUE(g_kvDelegateStatus == OK); + g_kvNbDelegatePtrWithoutPasswd = g_kvNbDelegatePtr; + + /** + * @tc.steps: step7. Open the db1 directly + * @tc.expected: step7. Return ERROR. + */ + option = {true, false, false, CipherType::DEFAULT, g_passwd3}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + ASSERT_TRUE(g_kvDelegateStatus != OK); + + /** + * @tc.steps: step8. Run the p2 command to open the db2 file. + * @tc.expected: step8. Return ERROR. + */ + option = {true, false, true, CipherType::DEFAULT, g_passwd2}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_TRUE(g_kvDelegateStatus == OK); +} + +/** + * @tc.name: PasswordIndependence001 + * @tc.desc: The data of the current version of the board is exported and the package file is single. + * @tc.type: FUNC + * @tc.require: AR000D487B + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, PasswordIndependence001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Back up a single database db1 No password backup password p3 + */ + std::string singleExportFileNameNoPasswd = g_exportFileDir + "/singleNoPasswdIndependence001.$$"; + std::string singleStoreIdNoPasswd = "distributed_ExportSingle_005"; + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(singleStoreIdNoPasswd, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + g_kvNbDelegatePtrWithoutPasswd = g_kvNbDelegatePtr; + + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileNameNoPasswd, g_passwd3), OK); + + /** + * @tc.steps: step2. Back up the database of the single version db2 Password p2 Backup file password p4 + */ + std::string singleExportFileName = g_exportFileDir + "/singleIndependence001.$$"; + std::string singleStoreId = "distributed_ExportSingle_006"; + option = {true, false, true, CipherType::DEFAULT, g_passwd2}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, g_passwd4), OK); + + /** + * @tc.steps: step3. Recover the backup file. + */ + EXPECT_EQ(g_kvNbDelegatePtrWithoutPasswd->Import(singleExportFileNameNoPasswd, g_passwd3), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, g_passwd4), OK); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrWithoutPasswd), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + (void)TryDbForPasswordIndependence001(); + + // clear resource + g_junkFilesList.push_back(singleExportFileName); + g_junkFilesList.push_back(singleExportFileNameNoPasswd); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtrWithoutPasswd), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreIdNoPasswd), OK); +} + +static void TryDbForPasswordIndependence002() +{ + std::string multiStoreIdNoPasswd = "distributed_ExportMulti_007"; + std::string multiStoreId = "distributed_ExportMulti_008"; + + KvStoreDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd3}; + g_mgr.GetKvStore(multiStoreIdNoPasswd, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + ASSERT_TRUE(g_kvDelegateStatus != OK); + + option = {true, false, true, CipherType::DEFAULT, g_passwd4}; + g_mgr.GetKvStore(multiStoreId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + ASSERT_TRUE(g_kvDelegateStatus != OK); + + option = {true, false, false, CipherType::DEFAULT, g_passwd3}; + g_mgr.GetKvStore(multiStoreIdNoPasswd, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_TRUE(g_kvDelegateStatus == OK); + g_kvDelegatePtrWithoutPasswd = g_kvDelegatePtr; + + option = {true, false, false, CipherType::DEFAULT, g_passwd3}; + g_mgr.GetKvStore(multiStoreId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + ASSERT_TRUE(g_kvDelegateStatus != OK); + + option = {true, false, true, CipherType::DEFAULT, g_passwd2}; + g_mgr.GetKvStore(multiStoreId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_TRUE(g_kvDelegateStatus == OK); +} + +/** + * @tc.name: PasswordIndependence002 + * @tc.desc: The data of the current version of the board is exported and the package file is single. + * @tc.type: FUNC + * @tc.require: AR000D487B + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, PasswordIndependence002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Back up a single database db1 No password backup password p3 + */ + std::string multiExportFileNameNoPasswd = g_exportFileDir + "/multiNoPasswdIndependence001.$$"; + std::string multiStoreIdNoPasswd = "distributed_ExportMulti_007"; + KvStoreDelegate::Option option; + g_mgr.GetKvStore(multiStoreIdNoPasswd, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + g_kvDelegatePtrWithoutPasswd = g_kvDelegatePtr; + + EXPECT_EQ(g_kvDelegatePtr->Export(multiExportFileNameNoPasswd, g_passwd3), OK); + + /** + * @tc.steps: step2. Back up the database of the single version db2 Password p2 Backup file password p4 + */ + std::string multiExportFileName = g_exportFileDir + "/multiIndependence001.$$"; + std::string multiStoreId = "distributed_ExportMulti_008"; + option = {true, false, true, CipherType::DEFAULT, g_passwd2}; + g_mgr.GetKvStore(multiStoreId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvDelegatePtr->Export(multiExportFileName, g_passwd4), OK); + + /** + * @tc.steps: step3. Recover the backup file. + */ + EXPECT_EQ(g_kvDelegatePtrWithoutPasswd->Import(multiExportFileNameNoPasswd, g_passwd3), OK); + EXPECT_EQ(g_kvDelegatePtr->Import(multiExportFileName, g_passwd4), OK); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtrWithoutPasswd), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + /** + * @tc.steps: step4. Try diff passwd. + */ + (void)TryDbForPasswordIndependence002(); + + // clear resource + g_junkFilesList.push_back(multiExportFileName); + g_junkFilesList.push_back(multiExportFileNameNoPasswd); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtrWithoutPasswd), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreIdNoPasswd), OK); +} + +/** + * @tc.name: PasswordIndependence002 + * @tc.desc: The data of the current version of the board is exported and the package file is single. + * @tc.type: FUNC + * @tc.require: AR000D487B + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, PasswordIndependence003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Back up the (passwd1) encryption single-version (passwd2) database. + */ + std::string singleExportFileName = g_exportFileDir + "/singleIndependence003.$$"; + std::string singleStoreId = "distributed_ExportSingle_009"; + KvStoreNbDelegate::Option option = {true, false, true, CipherType::DEFAULT, g_passwd2}; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + ASSERT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, g_passwd1), OK); + + /** + * @tc.steps: step2. Rekey The password by passwd3 + */ + g_kvNbDelegatePtr->Rekey(g_passwd3); + + /** + * @tc.steps: step3. Import the database using passwd3. + * @tc.expected: step3. Return INVALID_FILE. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, g_passwd3), INVALID_FILE); + + /** + * @tc.steps: step4. Import the database using passwd1. + * @tc.expected: step4. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, g_passwd1), OK); + + /** + * @tc.steps: step5. Repeat step 1 - 4. + */ + std::string multiExportFileName = g_exportFileDir + "/multiIndependence003.$$"; + std::string multiStoreId = "distributed_ExportMulti_010"; + KvStoreDelegate::Option multiOption = {true, false, true, CipherType::DEFAULT, g_passwd2}; + g_mgr.GetKvStore(multiStoreId, multiOption, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvDelegatePtr->Export(multiExportFileName, g_passwd1), OK); + remove(singleExportFileName.c_str()); + + EXPECT_EQ(g_kvDelegatePtr->Import(multiExportFileName, g_passwd3), INVALID_FILE); + EXPECT_EQ(g_kvDelegatePtr->Import(multiExportFileName, g_passwd1), OK); + + // clear resource + g_junkFilesList.push_back(multiExportFileName); + g_junkFilesList.push_back(singleExportFileName); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(multiStoreId), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); +} + +/** + * @tc.name: SeparaDbExportAndImport + * @tc.desc: Import and export after Separate database. + * @tc.type: FUNC + * @tc.require: AR000D487B + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, SeparaDbExportAndImport, TestSize.Level1) +{ + std::shared_ptr adapter = std::make_shared(); + EXPECT_TRUE(adapter != nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(adapter); + + std::string singleExportFileName = g_exportFileDir + "/SeparaDbExportAndImport.$$"; + std::string singleStoreId = "distributed_ExportSingle_010"; + KvStoreNbDelegate::Option option = {true, false, false}; + SecurityOption secOption{SecurityLabel::S3, SecurityFlag::SECE}; + option.secOption = secOption; + + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + + CipherPassword passwd; + EXPECT_EQ(g_kvNbDelegatePtr->Export(singleExportFileName, passwd), OK); + + g_kvNbDelegatePtr->Put(KEY_2, VALUE_2); + + EXPECT_EQ(g_kvNbDelegatePtr->Import(singleExportFileName, passwd), OK); + Value valueRead; + g_kvNbDelegatePtr->Get(KEY_2, valueRead); + EXPECT_EQ(valueRead, Value()); + g_kvNbDelegatePtr->Get(KEY_1, valueRead); + EXPECT_EQ(valueRead, VALUE_1); + g_kvNbDelegatePtr->Put(KEY_3, VALUE_3); + + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(g_passwd1), OK); + g_kvNbDelegatePtr->Get(KEY_3, valueRead); + EXPECT_EQ(valueRead, VALUE_3); + + // clear resource + g_junkFilesList.push_back(singleExportFileName); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + + option.passwd = g_passwd1; + option.isEncryptedDb = true; + g_mgr.GetKvStore(singleStoreId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(singleStoreId), OK); +} + +/** + * @tc.name: SeparaDbExportAndImport + * @tc.desc: Import and export after Separate database. + * @tc.type: FUNC + * @tc.require: AR000D487B + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesImportAndExportTest, SeparaDbNoPasswdRekey, TestSize.Level1) +{ + std::shared_ptr adapter = std::make_shared(); + EXPECT_TRUE(adapter != nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(adapter); + + KvStoreNbDelegate::Option option = {true, false, true}; + SecurityOption secOption{SecurityLabel::S3, SecurityFlag::SECE}; + option.secOption = secOption; + option.passwd = g_passwd1; + g_mgr.GetKvStore("SeparaDbNoPasswdRekey", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + + EXPECT_EQ(g_kvDelegateStatus, OK); + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(g_passwd2), OK); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + option.passwd = g_passwd2; + g_mgr.GetKvStore("SeparaDbNoPasswdRekey", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("SeparaDbNoPasswdRekey"), OK); +} +#endif diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_index_unit_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_index_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..895eb2f27652ec1d8fe988037ff480b41fc03c2c --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_index_unit_test.cpp @@ -0,0 +1,857 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_JSON +#include +#include +#include "db_common.h" +#include "db_constant.h" +#include "distributeddb_tools_unit_test.h" +#include "query.h" +#include "schema_constant.h" +#include "schema_utils.h" +#include "sqlite_import.h" +#include "sqlite_local_kvdb_connection.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + // Directory and delegate related + string g_testDir; + KvStoreConfig g_config; + const string USER_NAME = "TEST0"; + const string APP_NAME = "OHOS"; + KvStoreDelegateManager g_mgr(APP_NAME, USER_NAME); + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + DBStatus g_kvDelegateStatus2 = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr2 = nullptr; + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + auto g_kvNbDelegateCallback2 = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus2), std::ref(g_kvNbDelegatePtr2)); + + string GetKvStoreDirectory(const string &storeId, int databaseType) + { + string identifier = USER_NAME + "-" + APP_NAME + "-" + storeId; + string hashIdentifierName = DBCommon::TransferHashString(identifier); + string identifierName = DBCommon::TransferStringToHex(hashIdentifierName); + string filePath = g_testDir + "/" + identifierName + "/"; + if (databaseType == DBConstant::DB_TYPE_LOCAL) { // local + filePath += (DBConstant::LOCAL_SUB_DIR + "/" + DBConstant::LOCAL_DATABASE_NAME + + DBConstant::SQLITE_DB_EXTENSION); + } else if (databaseType == DBConstant::DB_TYPE_SINGLE_VER) { // single ver + filePath += (DBConstant::SINGLE_SUB_DIR + "/" + DBConstant::MAINDB_DIR + "/" + + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION); + } else if (databaseType == DBConstant::DB_TYPE_MULTI_VER) { // multi ver + filePath += (DBConstant::MULTI_SUB_DIR + "/" + DBConstant::MULTI_VER_DATA_STORE + + DBConstant::SQLITE_DB_EXTENSION); + } else { + filePath = ""; + } + + return filePath; + } + + // Query sqlite_master related + const string SQL_QUERY_INDEX = "SELECT COUNT(*) FROM sqlite_master where type = 'index' and name = "; + int CallbackReturnCount(void *data, int argc, char **argv, char **azColName) + { + if (argc == 1) { + int count = strtol(*(argv), nullptr, 10); // 10: decimal + if (data != nullptr) { + int *mid = static_cast(data); + *mid = count; + } + } + return 0; + } + + // Schema and value info related + FieldName GenerateFieldName(uint32_t serial, uint32_t level, bool fullLength) + { + FieldName result = "Serial_"; + result += to_string(serial); + result += "_Level_"; + result += to_string(level); + if (fullLength) { + while (result.size() < SchemaConstant::SCHEMA_FEILD_NAME_LENGTH_MAX) { + result.push_back('_'); + } + } + return result; + } + + FieldPath GenerateFieldPath(uint32_t totalLevel, uint32_t serial, bool fullLength) + { + FieldPath result; + for (uint32_t level = 0; level < totalLevel; level++) { + string fieldName = GenerateFieldName(serial, level, fullLength); + result.push_back(fieldName); + } + return result; + } + + string GenerateSchemaIndexArray(const vector &indexAll) + { + string result = "["; + for (auto &entry : indexAll) { + result += "\""; + result += SchemaUtils::FieldPathString(entry); + result += "\","; + } + if (!indexAll.empty()) { + result.pop_back(); + } + result += "]"; + return result; + } + + string GenerateEachSchemaDefine(const FieldPath &eachPath) + { + string result; + for (auto iter = eachPath.rbegin(); iter != eachPath.rend(); iter++) { + if (result.empty()) { + result = string("\"") + *iter + "\":\"INTEGER\""; + } else { + result = string("\"") + *iter + "\":{" + result + "}"; + } + } + return result; + } + + string GenerateSchemaString(const vector &define, const vector &index, int skipSize, + bool hasIndex, bool hasSkipSize) + { + string result = "{\"SCHEMA_VERSION\":\"1.0\",\"SCHEMA_MODE\":\"STRICT\",\"SCHEMA_DEFINE\":{"; + for (auto &entry : define) { + string defineStr = GenerateEachSchemaDefine(entry); + result += defineStr; + result += ","; + } + if (!define.empty()) { + result.pop_back(); + } + result += "}"; + if (hasIndex) { + result += ",\"SCHEMA_INDEXES\":"; + result += GenerateSchemaIndexArray(index); + } + if (hasSkipSize) { + result += ",\"SCHEMA_SKIPSIZE\":"; + result += to_string(skipSize); + } + result += "}"; + return result; + } + + string GenerateValueItem(const FieldPath &eachPath, int intValue) + { + string result; + for (auto iter = eachPath.rbegin(); iter != eachPath.rend(); iter++) { + if (result.empty()) { + result = string("\"") + *iter + "\":" + to_string(intValue); + } else { + result = string("\"") + *iter + "\":{" + result + "}"; + } + } + return result; + } + string GenerateValue(const vector &define, uint32_t skipSize) + { + int intValue = 0; + string result(skipSize, '*'); + result += "{"; + for (auto &entry : define) { + string defineStr = GenerateValueItem(entry, intValue++); + result += defineStr; + result += ","; + } + if (!define.empty()) { + result.pop_back(); + } + result += "}"; + return result; + } + + vector g_pathGroup1; + vector g_pathGroup2; + vector g_pathStrGroup1; + vector g_pathStrGroup2; + vector g_definePath; + string g_schemaString1; + string g_schemaString2; + string g_valueString1; + string g_valueString2; + + void ResetGlobalVariable() + { + g_pathGroup1.clear(); + g_pathGroup2.clear(); + g_pathStrGroup1.clear(); + g_pathStrGroup2.clear(); + g_definePath.clear(); + g_schemaString1.clear(); + g_schemaString2.clear(); + g_valueString1.clear(); + g_valueString2.clear(); + } + + void PrepareCommonInfo(bool fullLength) + { + int serial = 0; + for (uint32_t level = 1; level <= SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX; level++) { + FieldPath path = GenerateFieldPath(level, serial, fullLength); + string pathStr = SchemaUtils::FieldPathString(path); + g_pathGroup1.push_back(path); + g_pathStrGroup1.push_back(pathStr); + serial++; + } + for (uint32_t level = 1; level <= SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX; level++) { + FieldPath path = GenerateFieldPath(level, serial, fullLength); + string pathStr = SchemaUtils::FieldPathString(path); + g_pathGroup2.push_back(path); + g_pathStrGroup2.push_back(pathStr); + serial++; + } + } + + inline void CheckIndexFromDbFile(sqlite3 *db, const vector &indexToCheck, int expectCount) + { + for (auto &str : indexToCheck) { + string querySeq = SQL_QUERY_INDEX + "'" + str + "'"; + int count = -1; + EXPECT_EQ(sqlite3_exec(db, querySeq.c_str(), CallbackReturnCount, &count, nullptr), SQLITE_OK); + EXPECT_EQ(count, expectCount); + } + } +} + +class DistributedDBInterfacesIndexUnitTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown() {}; +}; + +void DistributedDBInterfacesIndexUnitTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesIndexUnitTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("[TestSuiteTearDown] rm test db files error!"); + } +} + +void DistributedDBInterfacesIndexUnitTest::SetUp() +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +namespace { + void PrepareInfoForCrudIndex001() + { + PrepareCommonInfo(false); + g_definePath.insert(g_definePath.end(), g_pathGroup1.begin(), g_pathGroup1.end()); + g_definePath.insert(g_definePath.end(), g_pathGroup2.begin(), g_pathGroup2.end()); + g_schemaString1 = GenerateSchemaString(g_definePath, g_pathGroup1, 0, true, false); + g_schemaString2 = GenerateSchemaString(g_definePath, g_definePath, 0, true, false); + LOGI("[PrepareInfoForCrudIndex001] g_schemaString1=%s", g_schemaString1.c_str()); + LOGI("[PrepareInfoForCrudIndex001] g_schemaString2=%s", g_schemaString2.c_str()); + } +} +/** + * @tc.name: CrudIndex001 + * @tc.desc: Test whether adding index is normal + * @tc.type: FUNC + * @tc.require: AR000DR9K8 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBInterfacesIndexUnitTest, CrudIndex001, TestSize.Level1) +{ + PrepareInfoForCrudIndex001(); + sqlite3 *db = nullptr; + string storeId = "CrudIndex001"; + string filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + KvStoreNbDelegate::Option option = {true, false, false}; + /** + * @tc.steps:step1. Specify the schema containing all levels of index to open the schema mode database. + * @tc.expected: step1. return OK. + */ + option.schema = g_schemaString1; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + /** + * @tc.steps:step2. Use the sql statement to get the count of the following index fields that will be added. + * @tc.expected: step2. count == 0. + */ + EXPECT_EQ(sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_READWRITE, nullptr), SQLITE_OK); + CheckIndexFromDbFile(db, g_pathStrGroup2, 0); + sqlite3_close(db); + /** + * @tc.steps:step3. Close the database. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + /** + * @tc.steps:step4. The original schema adds the above index fields, + * generates a new schema and opens the database with this schema. + * @tc.expected: step4. return OK. + */ + option.schema = g_schemaString2; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + /** + * @tc.steps:step5. Use the sql statement to get the count of the following index fields that are added. + * @tc.expected: step5. count == 1. + */ + EXPECT_EQ(sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_READWRITE, nullptr), SQLITE_OK); + CheckIndexFromDbFile(db, g_pathStrGroup2, 1); + sqlite3_close(db); + // Clear + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CrudIndex001"), OK); + ResetGlobalVariable(); +} + +namespace { + void PrepareInfoForCrudIndex002() + { + PrepareCommonInfo(false); + g_definePath.insert(g_definePath.end(), g_pathGroup1.begin(), g_pathGroup1.end()); + g_schemaString1 = GenerateSchemaString(g_definePath, g_pathGroup1, 0, true, false); + g_schemaString2 = GenerateSchemaString(g_definePath, vector(), 0, true, false); + LOGI("[PrepareInfoForCrudIndex002] g_schemaString1=%s", g_schemaString1.c_str()); + LOGI("[PrepareInfoForCrudIndex002] g_schemaString2=%s", g_schemaString2.c_str()); + } +} +/** + * @tc.name: CrudIndex002 + * @tc.desc: Test whether deleting index is normal + * @tc.type: FUNC + * @tc.require: AR000DR9K8 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBInterfacesIndexUnitTest, CrudIndex002, TestSize.Level1) +{ + PrepareInfoForCrudIndex002(); + sqlite3 *db = nullptr; + string storeId = "CrudIndex002"; + string filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + KvStoreNbDelegate::Option option = {true, false, false}; + /** + * @tc.steps:step1. Specify the schema containing all levels of index to open the schema mode database. + * @tc.expected: step1. return OK. + */ + option.schema = g_schemaString1; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + /** + * @tc.steps:step2. Use the sql statement to get the count of the following index fields that will be deleted. + * @tc.expected: step2. count == 1. + */ + EXPECT_EQ(sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_READWRITE, nullptr), SQLITE_OK); + CheckIndexFromDbFile(db, g_pathStrGroup1, 1); + sqlite3_close(db); + /** + * @tc.steps:step3. Close the database. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + /** + * @tc.steps:step4. The original schema delete the above index fields, + * generates a new schema and opens the database with this schema. + * @tc.expected: step4. return OK. + */ + option.schema = g_schemaString2; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + /** + * @tc.steps:step5. Use the sql statement to get the count of the following index fields that are deleted. + * @tc.expected: step5. count == 0. + */ + EXPECT_EQ(sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_READWRITE, nullptr), SQLITE_OK); + CheckIndexFromDbFile(db, g_pathStrGroup1, 0); + sqlite3_close(db); + // Clear + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CrudIndex002"), OK); + ResetGlobalVariable(); +} + +namespace { + void PrepareInfoForCrudIndex003() + { + PrepareCommonInfo(false); + g_definePath.insert(g_definePath.end(), g_pathGroup1.begin(), g_pathGroup1.end()); + g_definePath.insert(g_definePath.end(), g_pathGroup2.begin(), g_pathGroup2.end()); + g_schemaString1 = GenerateSchemaString(g_definePath, g_pathGroup1, 0, true, false); + g_schemaString2 = GenerateSchemaString(g_definePath, g_pathGroup2, 0, true, false); + LOGI("[PrepareInfoForCrudIndex003] g_schemaString1=%s", g_schemaString1.c_str()); + LOGI("[PrepareInfoForCrudIndex003] g_schemaString2=%s", g_schemaString2.c_str()); + } +} +/** + * @tc.name: CrudIndex003 + * @tc.desc: Test whether updating index is normal + * @tc.type: FUNC + * @tc.require: AR000DR9K8 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBInterfacesIndexUnitTest, CrudIndex003, TestSize.Level1) +{ + PrepareInfoForCrudIndex003(); + sqlite3 *db = nullptr; + string storeId = "CrudIndex003"; + string filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + KvStoreNbDelegate::Option option = {true, false, false}; + /** + * @tc.steps:step1. Specify the schema containing all levels of index to open the schema mode database. + * @tc.expected: step1. return OK. + */ + option.schema = g_schemaString1; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + /** + * @tc.steps:step2. Use the sql statement to get the count of the following index fields that will be deleted. + * @tc.expected: step2. count == 1. + */ + EXPECT_EQ(sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_READWRITE, nullptr), SQLITE_OK); + CheckIndexFromDbFile(db, g_pathStrGroup1, 1); + /** + * @tc.steps:step3. Use the sql statement to get the count of the following index fields that will be added. + * @tc.expected: step3. count == 0. + */ + CheckIndexFromDbFile(db, g_pathStrGroup2, 0); + sqlite3_close(db); + /** + * @tc.steps:step3. Close the database. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + /** + * @tc.steps:step4. The original schema update the above index fields, + * generates a new schema and opens the database with this schema. + * @tc.expected: step4. return OK. + */ + option.schema = g_schemaString2; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + /** + * @tc.steps:step5. Use the sql statement to get the count of the following index fields that are deleted. + * @tc.expected: step5. count == 0. + */ + EXPECT_EQ(sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_READWRITE, nullptr), SQLITE_OK); + CheckIndexFromDbFile(db, g_pathStrGroup1, 0); + /** + * @tc.steps:step5. Use the sql statement to get the count of the following index fields that are added. + * @tc.expected: step5. count == 1. + */ + CheckIndexFromDbFile(db, g_pathStrGroup2, 1); + sqlite3_close(db); + // Clear + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CrudIndex003"), OK); + ResetGlobalVariable(); +} + +namespace { + void PrepareInfoForCreateIndex001() + { + PrepareCommonInfo(true); + g_definePath.insert(g_definePath.end(), g_pathGroup1.begin(), g_pathGroup1.end()); + g_schemaString1 = GenerateSchemaString(g_definePath, g_definePath, 8, true, true); // skipsize 8 in schema + g_valueString1 = GenerateValue(g_definePath, 8); // skipsize 8 in value + g_valueString2 = GenerateValue(g_definePath, 10); // skipsize 10 in value + LOGI("[PrepareInfoForCreateIndex001] g_schemaString1=%s", g_schemaString1.c_str()); + LOGI("[PrepareInfoForCreateIndex001] g_valueString1=%s", g_valueString1.c_str()); + LOGI("[PrepareInfoForCreateIndex001] g_valueString2=%s", g_valueString2.c_str()); + } +} +/** + * @tc.name: CreateIndex001 + * @tc.desc: Test whether the index creation is normal + * @tc.type: FUNC + * @tc.require: AR000DR9K9 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBInterfacesIndexUnitTest, CreateIndex001, TestSize.Level1) +{ + PrepareInfoForCreateIndex001(); + sqlite3 *db = nullptr; + string storeId = "CreateIndex001"; + string filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + KvStoreNbDelegate::Option option = {true, false, false}; + /** + * @tc.steps:step1. Specify the schema containing all levels of index to open the schema mode database. + * The four-level index has 64 bytes per field. + * @tc.expected: step1. return OK. + */ + option.schema = g_schemaString1; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + /** + * @tc.steps:step2. Use the sql statement to get each index count count from the sqlite_master table; + * @tc.expected: step2. count == 1. + */ + EXPECT_EQ(sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_READWRITE, nullptr), SQLITE_OK); + CheckIndexFromDbFile(db, g_pathStrGroup1, 1); + sqlite3_close(db); + /** + * @tc.steps:step3. Write a value with 8 prefix bytes and the json part strictly conforms + * to the value of the schema. Call the query interface to query the inserted data. + * @tc.expected: step3. The insertion is successful and the number of entries obtained by the query is 1. + */ + Key key001{'1'}; + Value value001(g_valueString1.begin(), g_valueString1.end()); + EXPECT_EQ(g_kvNbDelegatePtr->Put(key001, value001), OK); + Query query = Query::Select().GreaterThanOrEqualTo(g_pathStrGroup1.front(), 0); + vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(query, entries), OK); + EXPECT_EQ(entries.size(), 1ul); + /** + * @tc.steps:step4. Write a value with 10 prefix bytes and the json part strictly conforms + * to the value of the schema. + * @tc.expected: step4. The insertion is failed. + */ + Key key002{'2'}; + Value value002(g_valueString2.begin(), g_valueString2.end()); + EXPECT_TRUE(g_kvNbDelegatePtr->Put(key002, value002) != OK); + // Clear + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CreateIndex001"), OK); + ResetGlobalVariable(); +} + +namespace { + void PrepareInfoForCreateIndex002() + { + for (uint32_t serial = 0; serial < SchemaConstant::SCHEMA_INDEX_COUNT_MAX; serial++) { + FieldPath path = GenerateFieldPath(SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX, serial, true); + string pathStr = SchemaUtils::FieldPathString(path); + g_pathGroup1.push_back(path); + g_pathStrGroup1.push_back(pathStr); + } + g_definePath.insert(g_definePath.end(), g_pathGroup1.begin(), g_pathGroup1.end()); + g_schemaString1 = GenerateSchemaString(g_definePath, g_definePath, 0, true, false); + LOGI("[PrepareInfoForCreateIndex002] g_schemaString1=%s", g_schemaString1.c_str()); + } +} +/** + * @tc.name: CreateIndex002 + * @tc.desc: Test whether it is possible to insert 32 four-level indexes with each filed being 64. + * @tc.type: FUNC + * @tc.require: AR000DR9K9 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBInterfacesIndexUnitTest, CreateIndex002, TestSize.Level1) +{ + PrepareInfoForCreateIndex002(); + sqlite3 *db = nullptr; + string storeId = "CreateIndex002"; + string filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + KvStoreNbDelegate::Option option = {true, false, false}; + /** + * @tc.steps:step1. Specifies that a schema with 32 four-level indexes + * with each filed being 64 opens the schema mode database + * @tc.expected: step1. return OK. + */ + option.schema = g_schemaString1; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + /** + * @tc.steps:step2. Use the sql statement to get each index count count from the sqlite_master table; + * @tc.expected: step2. count == 1. + */ + EXPECT_EQ(sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_READWRITE, nullptr), SQLITE_OK); + CheckIndexFromDbFile(db, g_pathStrGroup1, 1); + sqlite3_close(db); + // Clear + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CreateIndex002"), OK); + ResetGlobalVariable(); +} + +namespace { + void PrepareInfoForCheckSchemaSkipsize001() + { + PrepareCommonInfo(false); + g_definePath.insert(g_definePath.end(), g_pathGroup1.begin(), g_pathGroup1.end()); + g_schemaString1 = GenerateSchemaString(g_definePath, g_pathGroup1, 0, true, false); + g_valueString1 = GenerateValue(g_definePath, 0); + g_valueString2 = GenerateValue(g_definePath, 8); // skipsize 8 in value + LOGI("[PrepareInfoForCheckSchemaSkipsize001] g_schemaString1=%s", g_schemaString1.c_str()); + LOGI("[PrepareInfoForCheckSchemaSkipsize001] g_valueString1=%s", g_valueString1.c_str()); + LOGI("[PrepareInfoForCheckSchemaSkipsize001] g_valueString2=%s", g_valueString2.c_str()); + } +} +/** + * @tc.name: Check schema skipsize 001 + * @tc.desc: When SCHEMA_SKIPSIZE is not defined, check if the default is 0 + * @tc.type: FUNC + * @tc.require: AR000DR9K9 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBInterfacesIndexUnitTest, CheckSchemaSkipsize001, TestSize.Level1) +{ + PrepareInfoForCheckSchemaSkipsize001(); + string storeId = "CheckSchemaSkipsize001"; + string filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + KvStoreNbDelegate::Option option = {true, false, false}; + /** + * @tc.steps:step1. Specify an undefined skipsize schema to open the schema database. + * @tc.expected: step1. return OK. + */ + option.schema = g_schemaString1; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + /** + * @tc.steps:step2. Write a value without prefix and strictly in accordance with the schema. + * @tc.expected: step2. return OK. + */ + Key key001{'1'}; + Value value001(g_valueString1.begin(), g_valueString1.end()); + EXPECT_EQ(g_kvNbDelegatePtr->Put(key001, value001), OK); + /** + * @tc.steps:step3. Write a value whose prefix is 8 and strictly in accordance with the schema. + * @tc.expected: step3. return not OK. + */ + Key key002{'2'}; + Value value002(g_valueString2.begin(), g_valueString2.end()); + EXPECT_TRUE(g_kvNbDelegatePtr->Put(key002, value002) != OK); + // Clear + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CheckSchemaSkipsize001"), OK); + ResetGlobalVariable(); +} + +/** + * @tc.name: Check schema skipsize 002 + * @tc.desc: SCHEMA_SKIPSIZE range is [0,4MB-2] + * @tc.type: FUNC + * @tc.require: AR000DR9K9 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBInterfacesIndexUnitTest, CheckSchemaSkipsize002, TestSize.Level1) +{ + PrepareCommonInfo(false); + string storeId = "CheckSchemaSkipsize002"; + string filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + KvStoreNbDelegate::Option option = {true, false, false}; + /** + * @tc.steps:step1. Set "SCHEMA_SKIPSIZE" in the schema as -1 to create the schema database. + * @tc.expected: step1. return not OK. + */ + option.schema = GenerateSchemaString(g_pathGroup1, vector(), -1, false, true); // skipsize -1 in schema + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus != OK); + + /** + * @tc.steps:step2. Set "SCHEMA_SKIPSIZE" in the schema as 0 to create the schema database. + * @tc.expected: step2. return not OK. + */ + option.schema = GenerateSchemaString(g_pathGroup1, vector(), 0, false, true); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CheckSchemaSkipsize002"), OK); + + /** + * @tc.steps:step3. Set "SCHEMA_SKIPSIZE" in the schema as 8 to create the schema database. + * @tc.expected: step3. return OK. + */ + option.schema = GenerateSchemaString(g_pathGroup1, g_pathGroup1, 8, true, true); // skipsize 8 in schema + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CheckSchemaSkipsize002"), OK); + + /** + * @tc.steps:step4. Set "SCHEMA_SKIPSIZE" in the schema as 4MB-2 to create the schema database. + * @tc.expected: step6. return OK. + */ + option.schema = GenerateSchemaString(g_pathGroup1, vector(), + 4 * 1024 * 1024 - 2, false, true); // skipsize in schema, 4M - 2, 1024 is scale + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CheckSchemaSkipsize002"), OK); + + /** + * @tc.steps:step5. Set SCHEMA_SKIPSIZE in the schema as 4MB-1 to create the schema database. + * @tc.expected: step6. return not OK. + */ + option.schema = GenerateSchemaString(g_pathGroup1, vector(), + 4 * 1024 * 1024 - 1, false, true); // skipsize in schema, 4M - 1, 1024 is scale + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus != OK); + // Clear + ResetGlobalVariable(); +} + +namespace { + void PrepareInfoForCheckSchemaSkipsize003() + { + PrepareCommonInfo(false); + g_definePath.insert(g_definePath.end(), g_pathGroup1.begin(), g_pathGroup1.end()); + g_schemaString1 = GenerateSchemaString(g_definePath, g_pathGroup1, 20, true, true); // skipsize 20 in schema + g_valueString1 = GenerateValue(g_definePath, 19); // skipsize 19 in value + g_valueString2 = GenerateValue(g_definePath, 20); // skipsize 20 in value + LOGI("[PrepareInfoForCheckSchemaSkipsize003] g_schemaString1=%s", g_schemaString1.c_str()); + LOGI("[PrepareInfoForCheckSchemaSkipsize003] g_valueString1=%s", g_valueString1.c_str()); + LOGI("[PrepareInfoForCheckSchemaSkipsize003] g_valueString2=%s", g_valueString2.c_str()); + } +} +/** + * @tc.name: Check schema skipsize 003 + * @tc.desc: When "SCHEMA_SKIPSIZE" is greater than or equal to the size of Value, + * the Value verification must fail + * @tc.type: FUNC + * @tc.require: AR000DR9K9 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBInterfacesIndexUnitTest, CheckSchemaSkipsize003, TestSize.Level1) +{ + PrepareInfoForCheckSchemaSkipsize003(); + string storeId = "CheckSchemaSkipsize003"; + string filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + KvStoreNbDelegate::Option option = {true, false, false}; + /** + * @tc.steps:step1. Set "SCHEMA_SKIPSIZE" in the schema as 20 to create the schema database. + * @tc.expected: step1. return OK. + */ + option.schema = g_schemaString1; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + + /** + * @tc.steps:step5. Write a value whose prefix is 19 and strictly in accordance with the schema. + * @tc.expected: step5. return OK. + */ + Key key001{'1'}; + Value value001(g_valueString1.begin(), g_valueString1.end()); + EXPECT_TRUE(g_kvNbDelegatePtr->Put(key001, value001) != OK); + /** + * @tc.steps:step5. Write a value whose prefix is 20 and strictly in accordance with the schema. + * @tc.expected: step5. return OK. + */ + Key key002{'2'}; + Value value002(g_valueString2.begin(), g_valueString2.end()); + EXPECT_TRUE(g_kvNbDelegatePtr->Put(key002, value002) == OK); + // Clear + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("CheckSchemaSkipsize003"), OK); + ResetGlobalVariable(); +} + +/** + * @tc.name: schema compare with skipsize 004 + * @tc.desc: When the SCHEMA_SKIPSIZE definitions of two Schemas are different, + * they will be regarded as inconsistent and incompatible + * @tc.type: FUNC + * @tc.require: AR000DR9K9 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBInterfacesIndexUnitTest, SchemaCompareSkipsize004, TestSize.Level1) +{ + PrepareCommonInfo(false); + string storeId = "SchemaCompareSkipsize004"; + string filePath = GetKvStoreDirectory(storeId, DBConstant::DB_TYPE_SINGLE_VER); + KvStoreNbDelegate::Option option = {true, false, false}; + /** + * @tc.steps:step1. Set "SCHEMA_SKIPSIZE" in the schema as 0 to create the schema database. + * @tc.expected: step1. return OK. + */ + option.schema = GenerateSchemaString(g_pathGroup1, vector(), 0, false, true); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + + /** + * @tc.steps:step2. Modify the schema, SCHEMA_SKIPSIZE in the new schema is not defined, + * open the database repeatedly. + * @tc.expected: step2. return OK. + */ + option.schema = GenerateSchemaString(g_pathGroup1, vector(), 0, false, false); + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback2); + ASSERT_TRUE(g_kvNbDelegatePtr2 != nullptr); + EXPECT_EQ(g_kvDelegateStatus2, OK); + + /** + * @tc.steps:step3. Close the database. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr2), OK); + + /** + * @tc.steps:step4. SCHEMA_SKIPSIZE in the schema is not defined, reopen the database; + * @tc.expected: step4. return OK. + */ + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + + /** + * @tc.steps:step5. Modify the schema, set SCHEMA_SKIPSIZE to 8 in the new schema, + * and open the database repeatedly; + * @tc.expected: step5. return OK. + */ + option.schema = GenerateSchemaString(g_pathGroup1, vector(), 8, false, true); // skipsize 8 in schema + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback2); + EXPECT_TRUE(g_kvNbDelegatePtr2 == nullptr); + EXPECT_TRUE(g_kvDelegateStatus2 != OK); + + /** + * @tc.steps:step6. Close the database. + * @tc.expected: step6. return OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps:step4. Modify the schema, set SCHEMA_SKIPSIZE to 8 in the new schema, reopen the database; + * @tc.expected: step4. return OK. + */ + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvNbDelegatePtr2 == nullptr); + EXPECT_TRUE(g_kvDelegateStatus != OK); + EXPECT_EQ(g_mgr.DeleteKvStore("SchemaCompareSkipsize004"), OK); + ResetGlobalVariable(); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_local_batch_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_local_batch_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50a53dbbe4ead2514a4444e93c523744047969e1 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_local_batch_test.cpp @@ -0,0 +1,1138 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_constant.h" +#include "db_errno.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "sqlite_single_ver_natural_store.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + KvStoreConfig g_config; + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + + const int OBSERVER_SLEEP_TIME = 100; + const int BATCH_PRESET_SIZE_TEST = 10; + const int DIVIDE_BATCH_PRESET_SIZE = 5; + const int VALUE_OFFSET = 5; + const int DEFAULT_KEY_VALUE_SIZE = 10; + + const Key KEY1{'k', 'e', 'y', '1'}; + const Key KEY2{'k', 'e', 'y', '2'}; + const Value VALUE1{'v', 'a', 'l', 'u', 'e', '1'}; + const Value VALUE2{'v', 'a', 'l', 'u', 'e', '2'}; + + const std::string VALID_SCHEMA_STRICT_DEFINE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"INTEGER, NOT NULL\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\"]}"; + + CipherPassword g_passwd; + KvStoreNbDelegate::Option g_strictOpt = { + true, false, false, CipherType::DEFAULT, g_passwd, + VALID_SCHEMA_STRICT_DEFINE + }; + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + + static void CreatEntrys(int recordSize, vector &keys, vector &values, vector &entries) + { + keys.clear(); + values.clear(); + entries.clear(); + for (int i = 0; i < recordSize; i++) { + string temp = to_string(i); + Entry entry; + Key keyTemp; + Value valueTemp; + for (auto &iter : temp) { + entry.key.push_back(iter); + entry.value.push_back(iter); + keyTemp.push_back(iter); + valueTemp.push_back(iter); + } + keys.push_back(keyTemp); + values.push_back(valueTemp); + entries.push_back(entry); + } + } +} + +class DistributedDBInterfacesNBDelegateLocalBatchTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesNBDelegateLocalBatchTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesNBDelegateLocalBatchTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesNBDelegateLocalBatchTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvNbDelegatePtr = nullptr; +} + +void DistributedDBInterfacesNBDelegateLocalBatchTest::TearDown(void) +{ + if (g_kvNbDelegatePtr != nullptr) { + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_kvNbDelegatePtr = nullptr; + } +} + +/** + * @tc.name: PutLocalBatch001 + * @tc.desc: This test case use to verify the PutLocalBatch interface function + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, PutLocalBatch001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get singleVer kvStore by GetKvStore. + * @tc.expected: step1. Get database success. + */ + const KvStoreNbDelegate::Option option = {true, true}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_PutLocalBatch_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step2. Insert 10 records into database. + * @tc.expected: step2. Insert successfully. + */ + vector entries; + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i); + entries.push_back(entry); + } + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entries), OK); + + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Key key; + key.push_back(i); + Value value; + g_kvNbDelegatePtr->GetLocal(key, value); + EXPECT_EQ(key, value); + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutLocalBatch001 + * @tc.desc: Check for illegal parameters + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerPutLocalBatch001, TestSize.Level1) +{ + /** + * @tc.steps: step1. + * Create and construct three sets of vector , each set of three data contains records: + * (K1, V1) It is illegal for K1 to be greater than 1K, and V1 is 1K in size + * (K2, V2) K2 is legal, V2 is greater than 4M + * (K3, V3) are not legal. + */ + Key illegalKey; + DistributedDBToolsUnitTest::GetRandomKeyValue(illegalKey, DBConstant::MAX_KEY_SIZE + 1); // 1K + 1 + Value illegalValue; + DistributedDBToolsUnitTest::GetRandomKeyValue(illegalValue, DBConstant::MAX_VALUE_SIZE + 1); // 4M + 1 + vector entrysKeyIllegal = {KV_ENTRY_1, KV_ENTRY_2, {illegalKey, VALUE_3}}; + vector entrysValueIllegal = {KV_ENTRY_1, KV_ENTRY_2, {KEY_3, illegalValue}}; + vector entrysIllegal = {KV_ENTRY_1, KV_ENTRY_2, {illegalKey, illegalValue}}; + + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutLocalBatch_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step2. PutBatch operates on three sets of data. + * @tc.expected: step2. All three operations return INVALID_ARGS. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysKeyIllegal), INVALID_ARGS); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysValueIllegal), INVALID_ARGS); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysIllegal), INVALID_ARGS); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutLocalBatch_001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutLocalBatch002 + * @tc.desc: PutLocalBatch normal insert function test. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerPutLocalBatch002, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutLocalBatch_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step1. + * Create and build 4 groups of vector , which are: + * Vect of empty objects; + * Vect1 of a legal Entry record; + * 128 legal Entry records Vect2; + * 129 legal Entry records Vect3; + */ + vector entrysMaxNumber; + for (size_t i = 0; i < DBConstant::MAX_BATCH_SIZE; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i); + entrysMaxNumber.push_back(entry); + } + Key keyTemp = {'1', '1'}; + Value valueTemp; + Entry entryTemp = {keyTemp, VALUE_1}; + vector entrysOneRecord = {entryTemp}; + vector entrysOverSize = entrysMaxNumber; + entrysOverSize.push_back(entryTemp); + /** + * @tc.steps: step2. PutBatch operates on four sets of data. and use get check the result of Vect3. + * @tc.expected: step2. Returns INVALID_ARGS for 129 records, and returns OK for the rest. all get return NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysOverSize), INVALID_ARGS); + for (size_t i = 0; i < entrysOverSize.size(); i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysOverSize[i].key, valueTemp), NOT_FOUND); + } + /** + * @tc.steps: step3. Use get check the result of Vect2. + * @tc.expected: step3. Return OK and get the correct value. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysOneRecord), OK); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keyTemp, valueTemp), OK); + EXPECT_EQ(valueTemp, VALUE_1); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysMaxNumber), OK); + /** + * @tc.steps: step4. Use get check the result of Vect3. + * @tc.expected: step4. Return OK and get the correct value. + */ + for (size_t i = 0; i < entrysMaxNumber.size(); i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysMaxNumber[i].key, valueTemp), OK); + EXPECT_EQ(valueTemp, entrysMaxNumber[i].value); + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutLocalBatch_002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutLocalBatch003 + * @tc.desc: Check interface atomicity + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerPutLocalBatch003, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutLocalBatch_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step1. Create and construct a set of vector with a total of 128 data, + * including one illegal data. And call PutBatch interface to insert. + */ + vector entrysMaxNumber; + for (size_t i = 0; i < DBConstant::MAX_BATCH_SIZE; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i); + entrysMaxNumber.push_back(entry); + } + Key illegalKey; + Value valueTemp; + DistributedDBToolsUnitTest::GetRandomKeyValue(illegalKey, DBConstant::MAX_KEY_SIZE + 1); // 1K + 1 + entrysMaxNumber[0].key = illegalKey; + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysMaxNumber), INVALID_ARGS); + /** + * @tc.steps: step2. Use Get interface to query 128 corresponding key values. + * @tc.expected: step2. All Get interface return NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysMaxNumber[0].key, valueTemp), INVALID_ARGS); + for (size_t i = 1; i < entrysMaxNumber.size(); i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysMaxNumber[i].key, valueTemp), NOT_FOUND); + } + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutLocalBatch_003"), OK); + g_kvNbDelegatePtr = nullptr; +} + +static void PreparePutLocalBatch004(vector &entrys1, vector &entrys2, vector &entrys3) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutLocalBatch_004", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i); + entrys1.push_back(entry); + } + + for (int i = 0; i < DIVIDE_BATCH_PRESET_SIZE; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i + VALUE_OFFSET); + entrys2.push_back(entry); + } + + for (int i = DIVIDE_BATCH_PRESET_SIZE; i < BATCH_PRESET_SIZE_TEST; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i - VALUE_OFFSET); + entrys3.push_back(entry); + } +} + +/** + * @tc.name: SingleVerPutLocalBatch004 + * @tc.desc: Check interface data insertion and update functions. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerPutLocalBatch004, TestSize.Level1) +{ + /** + * @tc.steps: step1. + * Construct three groups of three vector : + * (1) entrys1: key1 ~ 10, corresponding to Value1 ~ 10; + * (2) entrys2: key1 ~ 5, corresponding to Value6 ~ 10; + * (3) entrys3: key6 ~ 10, corresponding to Value1 ~ 5; + */ + vector entrys1; + vector entrys2; + vector entrys3; + PreparePutLocalBatch004(entrys1, entrys2, entrys3); + /** + * @tc.steps: step2. PutBatch entrys2. + * @tc.expected: step2. PutBatch return OK. + */ + Value valueRead; + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrys2), OK); + /** + * @tc.steps: step3. Check PutBatch result. + * @tc.expected: step3. Get correct value of key1~5. Key6~10 return NOT_FOUND. + */ + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Key keyTemp; + keyTemp.push_back(i); + if (i < DIVIDE_BATCH_PRESET_SIZE) { + Value valueTemp; + valueTemp.push_back(i + VALUE_OFFSET); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, valueTemp); + continue; + } + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keyTemp, valueRead), NOT_FOUND); + } + /** + * @tc.steps: step4. PutBatch entrys1. + * @tc.expected: step4. PutBatch return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrys1), OK); + /** + * @tc.steps: step5. Check PutBatch result. + * @tc.expected: step5. Update and insert value of key1~10 to value1~10. + */ + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Key keyTemp; + keyTemp.push_back(i); + if (i < DIVIDE_BATCH_PRESET_SIZE) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, keyTemp); + continue; + } + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, keyTemp); + } + /** + * @tc.steps: step6. PutBatch entrys3. + * @tc.expected: step6. PutBatch return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrys3), OK); + /** + * @tc.steps: step7. Check PutBatch result of key1~10. + * @tc.expected: step7. Update value of key5~10 to value1~5. + */ + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Key keyTemp; + keyTemp.push_back(i); + if (i < DIVIDE_BATCH_PRESET_SIZE) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, keyTemp); + continue; + } + Value valueTemp; + valueTemp.push_back(i - VALUE_OFFSET); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, valueTemp); + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutLocalBatch_004"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerDeleteLocalBatch001 + * @tc.desc: Check for illegal parameters. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerDeleteLocalBatch001, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerDeleteLocalBatch_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step1. Create and construct a set of vector , containing a total of 10 data keys1 ~ 10, + * Value1 ~ 10, and call Putbatch interface to insert data. + * @tc.expected: step1. PutBatch successfully. + */ + vector entries; + vector keys; + vector values; + Value valueRead; + CreatEntrys(BATCH_PRESET_SIZE_TEST, keys, values, entries); + vector entrysBase = entries; + vector keysBase = keys; + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysBase), OK); + /** + * @tc.steps: step2. Use Get to check data in database. + * @tc.expected: step2. Get value1~10 by key1~10 successfully. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysBase[i].key, valueRead), OK); + } + /** + * @tc.steps: step3. Use DeleteBatch interface to transfer 10 + 119 extra keys (total 129). + * @tc.expected: step3. Return INVALID_ARGS. + */ + CreatEntrys(DBConstant::MAX_BATCH_SIZE + 1, keys, values, entries); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keys), INVALID_ARGS); + /** + * @tc.steps: step4. Use Get to check data in database. + * @tc.expected: step4. Key1~10 still in database. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysBase[i].key, valueRead), OK); + } + /** + * @tc.steps: step5. Use the DeleteBatch interface to pass in 10 included + * keys6 ~ 10 + 123 additional key values ​​(128 in total). + * @tc.expected: step5. DeleteBatch OK. + */ + CreatEntrys(DBConstant::MAX_BATCH_SIZE + DIVIDE_BATCH_PRESET_SIZE, keys, values, entries); + keys.erase(keys.begin(), keys.begin() + DIVIDE_BATCH_PRESET_SIZE); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keys), OK); + /** + * @tc.steps: step6. Use Get to check key1~10 in database. + * @tc.expected: step6. Key1~5 in database, key6~10 have been deleted. + */ + for (size_t i = 0; i < DIVIDE_BATCH_PRESET_SIZE; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysBase[i].key, valueRead), OK); + } + for (size_t i = DIVIDE_BATCH_PRESET_SIZE; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysBase[i].key, valueRead), NOT_FOUND); + } + /** + * @tc.steps: step7. Repeat Putbatch key1~10, value1~10. + * @tc.expected: step7. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysBase), OK); + + Key illegalKey; + DistributedDBToolsUnitTest::GetRandomKeyValue(illegalKey, DBConstant::MAX_KEY_SIZE + 1); // 1K + 1 + keysBase.push_back(illegalKey); + /** + * @tc.steps: step8. Use DeleteBatch interface to pass in 10 + 1(larger than 1K) keys. + * @tc.expected: step8. Return INVALID_ARGS. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keysBase), INVALID_ARGS); + /** + * @tc.steps: step9. Use Get to check key1~10 in database. + * @tc.expected: step9. Delete those data failed. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysBase[i].key, valueRead), OK); + } + /** + * @tc.steps: step10. Use DeleteBatch interface to pass in 10(in database) + 1 valid keys. + * @tc.expected: step10. Delete those data successfully. + */ + keysBase.back().erase(keysBase.back().begin(), keysBase.back().begin() + 1); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keysBase), OK); + /** + * @tc.steps: step11. Check data. + * @tc.expected: step11. DeleteBatch successfully. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(entrysBase[i].key, valueRead), NOT_FOUND); + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerDeleteLocalBatch_001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerDeleteLocalBatch002 + * @tc.desc: Check normal delete batch ability. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerDeleteLocalBatch002, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerDeleteLocalBatch_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step1. Create a group of vector , containing a total of 10 data keys1 ~ 10, Value1 ~ 10, + * call the Putbatch interface to insert data. + * @tc.expected: step1. Insert to database successfully. + */ + vector entries; + vector keysBase; + vector values; + CreatEntrys(BATCH_PRESET_SIZE_TEST, keysBase, values, entries); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entries), OK); + /** + * @tc.steps: step2. Check data. + * @tc.expected: step2. Get key1~10 successfully. + */ + Value valueRead; + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keysBase[i], valueRead), OK); + } + /** + * @tc.steps: step3. DeleteBatch key1~5. + * @tc.expected: step3. Return OK. + */ + vector keys(keysBase.begin(), keysBase.begin() + DIVIDE_BATCH_PRESET_SIZE); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keys), OK); + /** + * @tc.steps: step4. Check key1~10. + * @tc.expected: step4. Key1~5 deleted, key6~10 existed. + */ + for (size_t i = 0; i < DIVIDE_BATCH_PRESET_SIZE; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keysBase[i], valueRead), NOT_FOUND); + } + for (size_t i = DIVIDE_BATCH_PRESET_SIZE; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keysBase[i], valueRead), OK); + } + /** + * @tc.steps: step5. DeleteBatch key1~10. + * @tc.expected: step5. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keysBase), OK); + /** + * @tc.steps: step6. Check key1~10. + * @tc.expected: step6. Key1~10 deleted successfully. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(keysBase[i], valueRead), NOT_FOUND); + } + /** + * @tc.steps: step7. DeleteBatch key1~10 once again. + * @tc.expected: step7. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keysBase), OK); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerDeleteLocalBatch_002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutLocalBatchObserver001 + * @tc.desc: Test the observer function of PutLocalBatch() interface. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerPutLocalBatchObserver001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerPutLocalBatchObserver_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, observer), OK); + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + vector entrysBase; + vector keysBase; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST + 1, entrysBase, keysBase); + + vector entries(entrysBase.begin(), entrysBase.end() - 1); + EXPECT_EQ(entries.size(), 10UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entries), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entries, observer->GetEntriesInserted())); + /** + * @tc.steps:step4. Delete the batch data. + * @tc.expected: step4. Returns OK. + */ + vector keys(keysBase.begin() + 5, keysBase.end()); + EXPECT_EQ(keys.size(), 6UL); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keys), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + vector entrysDel(entrysBase.begin() + 5, entrysBase.end() - 1); + EXPECT_EQ(entrysDel.size(), 5UL); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrysDel, observer->GetEntriesDeleted())); + /** + * @tc.steps:step5. UnRegister the observer. + * @tc.expected: step5. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutLocalBatchObserver_001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutLocalBatchObserver002 + * @tc.desc: Test the observer function of PutLocalBatch() for invalid input. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerPutLocalBatchObserver002, TestSize.Level4) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerPutLocalBatchObserver_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, observer), OK); + /** + * @tc.steps:step3. Put 129 batch data. + * @tc.expected: step3. Returns INVALID_ARGS. + */ + vector entrys1; + vector keys1; + DistributedDBUnitTest::GenerateRecords(DBConstant::MAX_BATCH_SIZE + 1, entrys1, keys1); + + EXPECT_EQ(entrys1.size(), 129UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrys1), INVALID_ARGS); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(observer->GetEntriesInserted().empty()); + /** + * @tc.steps:step4. Put invalid batch data. + * @tc.expected: step4. Returns INVALID_ARGS. + */ + vector entrys2; + vector keys2; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys2, keys2); + EXPECT_EQ(entrys2.size(), 10UL); + + vector entrysInvalid; + vector keysInvalid; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrysInvalid, keysInvalid, + DBConstant::MAX_KEY_SIZE + 10); + EXPECT_EQ(entrysInvalid.size(), 10UL); + entrys2[0].key = entrysInvalid[0].key; + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrys2), INVALID_ARGS); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(observer->GetEntriesInserted().empty()); + /** + * @tc.steps:step5. Put MAX valid value batch data. + * @tc.expected: step5. Returns OK. + */ + vector entrys3; + vector keys3; + + DistributedDBUnitTest::GenerateRecords(DBConstant::MAX_BATCH_SIZE, entrys3, keys3); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrys3), OK); + LOGD("sleep begin"); + // sleep 20 seconds + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME * 10)); + LOGD("sleep end"); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrys3, observer->GetEntriesInserted())); + /** + * @tc.steps:step6. UnRegister the observer. + * @tc.expected: step6. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + + /** + * @tc.steps:step7. Close the kv store. + * @tc.expected: step7. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutLocalBatchObserver_002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutLocalBatchObserver003 + * @tc.desc: Test the observer function of PutLocalBatch() update function. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerPutLocalBatchObserver003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerPutLocalBatchObserver_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, observer), OK); + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + vector entrysAdd; + vector keysAdd; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrysAdd, keysAdd); + + EXPECT_EQ(entrysAdd.size(), 10UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysAdd), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrysAdd, observer->GetEntriesInserted())); + /** + * @tc.steps:step4. Update the batch data. + * @tc.expected: step4. Returns OK. + */ + vector entrysUpdate; + vector keysUpdate; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrysUpdate, keysUpdate, DEFAULT_KEY_VALUE_SIZE, + DEFAULT_KEY_VALUE_SIZE + 10); + + EXPECT_EQ(entrysUpdate.size(), 10UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrysUpdate), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrysUpdate, observer->GetEntriesUpdated())); + /** + * @tc.steps:step5. UnRegister the observer. + * @tc.expected: step5. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutLocalBatchObserver_003"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutLocalBatchObserver004 + * @tc.desc: Test the observer function of PutLocalBatch(), same keys handle. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerPutLocalBatchObserver004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerPutLocalBatchObserver_004", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, observer), OK); + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + vector entrys1; + vector keys1; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys1, keys1); + vector entrys2; + vector keys2; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys2, keys2, DEFAULT_KEY_VALUE_SIZE, + DEFAULT_KEY_VALUE_SIZE + 10); + entrys1.insert(entrys1.end(), entrys2.begin(), entrys2.end()); + + EXPECT_EQ(entrys1.size(), 20UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrys1), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrys2, observer->GetEntriesInserted())); + EXPECT_EQ(observer->GetEntriesUpdated().size(), 0UL); + + vector entrys3; + vector keys3; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys3, keys3, DEFAULT_KEY_VALUE_SIZE, + DEFAULT_KEY_VALUE_SIZE + 20); + vector entrys4; + vector keys4; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys4, keys4, DEFAULT_KEY_VALUE_SIZE, + DEFAULT_KEY_VALUE_SIZE + 30); + entrys3.insert(entrys3.end(), entrys4.begin(), entrys4.end()); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entrys3), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrys4, observer->GetEntriesUpdated())); + EXPECT_EQ(observer->GetEntriesInserted().size(), 0UL); + + /** + * @tc.steps:step4. UnRegister the observer. + * @tc.expected: step4. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + + /** + * @tc.steps:step5. Close the kv store. + * @tc.expected: step5. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutLocalBatchObserver_004"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerDeleteLocalBatchObserver001 + * @tc.desc: Test the observer function of DeleteLocalBatch() interface. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, SingleVerDeleteLocalBatchObserver001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerDeleteLocalBatchObserver_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, observer), OK); + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + vector entries; + vector keys; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entries, keys); + EXPECT_EQ(entries.size(), 10UL); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entries), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entries, observer->GetEntriesInserted())); + /** + * @tc.steps:step4. Delete the batch data. + * @tc.expected: step4. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keys), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entries, observer->GetEntriesDeleted())); + /** + * @tc.steps:step5. UnRegister the observer. + * @tc.expected: step5. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerDeleteLocalBatchObserver_001"), OK); + g_kvNbDelegatePtr = nullptr; +} +#ifndef OMIT_JSON +/** + * @tc.name: LocalDataBatchNotCheckSchema001 + * @tc.desc: Local data does not check schema. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, LocalDataBatchNotCheckSchema001, TestSize.Level1) +{ + g_mgr.GetKvStore("distributed_LocalDataBatchNotCheckSchema_001", g_strictOpt, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step1. Put one data whose value has more fields than the schema. + * @tc.expected: step1. Return OK, because PutLocal does not verify the validity of the schema. + */ + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string moreData = "{\"field_name1\":true,\"field_name2\":10,\"field_name3\":10}"; + Value value(moreData.begin(), moreData.end()); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(key, value), OK); + Value getValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(key, getValue), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsValueEqual(getValue, value)); + + /** + * @tc.steps:step2. Delete local data + * @tc.expected: step2. DeleteLocal return OK, GetLocal return NOT_FOUND + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocal(key), OK); + getValue.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(key, getValue), NOT_FOUND); + + /** + * @tc.steps:step3. PutLocalBatch local data whose value is mismatch with the schema. + * @tc.expected: step3. return OK. + */ + key.clear(); + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string invalidData = "{\"field_name1\":true, \"field_name2\":null}"; + value.assign(invalidData.begin(), invalidData.end()); + std::vector keys; + std::vector entries; + entries.push_back({key, value}); + keys.push_back(key); + + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string validData = "{\"field_name1\":true, \"field_name2\":0}"; + value.assign(validData.begin(), validData.end()); + entries.push_back({key, value}); + keys.push_back(key); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entries), OK); + std::vector getEntries; + Key keyPrefix; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries(keyPrefix, getEntries), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(entries, getEntries, true)); + + /** + * @tc.steps:step4. Delete local data + * @tc.expected: step4. DeleteLocal return OK, GetLocal return NOT_FOUND + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keys), OK); + getEntries.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries(keyPrefix, getEntries), NOT_FOUND); + EXPECT_TRUE(getEntries.empty()); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_LocalDataBatchNotCheckSchema_001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: LocalDataBatchNotCheckReadOnly001 + * @tc.desc: Local data does not check readOnly. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateLocalBatchTest, LocalDataBatchNotCheckReadOnly001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open the kv store with valid schema, and close it. + * @tc.expected: step1. opened & closeed successfully - return OK. + */ + g_mgr.GetKvStore("distributed_LocalDataBatchNotCheckReadOnly_001", g_strictOpt, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps:step2. Open the kv store with no schema. + * @tc.expected: step2. return OK. + */ + DistributedDB::KvStoreNbDelegate::Option option = g_strictOpt; + option.schema.clear(); + g_mgr.GetKvStore("distributed_LocalDataBatchNotCheckReadOnly_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step3. CRUD single local the data. + * @tc.expected: step3. return OK. + */ + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string valueData = "{\"field_name1\":true,\"field_name2\":20}"; + Value value(valueData.begin(), valueData.end()); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(key, value), OK); + + Value getValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(key, getValue), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsValueEqual(getValue, value)); + + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocal(key), OK); + getValue.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(key, getValue), NOT_FOUND); + + /** + * @tc.steps:step3. CRUD batch local the data. + * @tc.expected: step3. return OK. + */ + key.clear(); + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string invalidData = "{\"field_name1\":true, \"field_name2\":null}"; + value.assign(invalidData.begin(), invalidData.end()); + std::vector keys; + std::vector entries; + entries.push_back({key, value}); + keys.push_back(key); + + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string validData = "{\"field_name1\":true, \"field_name2\":0}"; + value.assign(validData.begin(), validData.end()); + entries.push_back({key, value}); + keys.push_back(key); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(entries), OK); + std::vector getEntries; + Key keyPrefix; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries(keyPrefix, getEntries), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(entries, getEntries, true)); + + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(keys), OK); + getEntries.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries(keyPrefix, getEntries), NOT_FOUND); + EXPECT_TRUE(getEntries.empty()); + + /** + * @tc.steps:step4. Close the kv store. + * @tc.expected: step4. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_LocalDataBatchNotCheckReadOnly_001"), OK); + g_kvNbDelegatePtr = nullptr; +} +#endif diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_schema_put_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_schema_put_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7cebfcaeddcdeb413ed1a6ce865f5aab335fde2f --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_schema_put_test.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_JSON +#include +#include +#include + +#include "distributeddb_tools_unit_test.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.h" +#include "query.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + const std::string VALID_SCHEMA_STRICT_DEFINE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"INTEGER, NOT NULL\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\"]}"; + const std::string VALID_SCHEMA_COMPA_DEFINE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"INTEGER, NOT NULL\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\"]}"; + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr("app0", "user0"); + std::string g_testDir; + KvStoreConfig g_config; + std::string g_storeName = "schema_put_test"; + + // define the g_kvNbDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvStore = nullptr; + CipherPassword g_passwd; + KvStoreNbDelegate::Option g_strictOpt = {true, false, false, CipherType::DEFAULT, g_passwd, + VALID_SCHEMA_STRICT_DEFINE}; + KvStoreNbDelegate::Option g_compOpt = {true, false, false, CipherType::DEFAULT, g_passwd, + VALID_SCHEMA_COMPA_DEFINE}; + + void KvStoreNbDelegateCallback( + DBStatus statusSrc, KvStoreNbDelegate* kvStoreSrc, DBStatus* statusDst, KvStoreNbDelegate** kvStoreDst) + { + *statusDst = statusSrc; + *kvStoreDst = kvStoreSrc; + } + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = std::bind(&KvStoreNbDelegateCallback, std::placeholders::_1, + std::placeholders::_2, &g_kvDelegateStatus, &g_kvStore); + + void CheckPutSchemaData(KvStoreNbDelegate *kvStore) + { + ASSERT_NE(kvStore, nullptr); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + /** + * @tc.steps:step1. Put one data whose value has less fields than the schema(less value is not null). + * @tc.expected: step1. return CONSTRAIN_VIOLATION. + */ + std::string lessData = "{\"field_name1\":true}"; + Value value(lessData.begin(), lessData.end()); + EXPECT_EQ(g_kvStore->Put(key, value), CONSTRAIN_VIOLATION); + + /** + * @tc.steps:step2. Put one data whose value has different fields with the schema(less value is not null). + * @tc.expected: step2. return CONSTRAIN_VIOLATION. + */ + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string filedDiffData = "{\"field_name1\":true,\"field_name3\":10}"; + value.assign(filedDiffData.begin(), filedDiffData.end()); + EXPECT_EQ(kvStore->Put(key, value), CONSTRAIN_VIOLATION); + + /** + * @tc.steps:step3. Put one data whose value has different type with the schema. + * @tc.expected: step3. return INVALID_FIELD_TYPE. + */ + std::string typeDiffData = "{\"field_name1\":30,\"field_name2\":10}"; + value.assign(typeDiffData.begin(), typeDiffData.end()); + EXPECT_EQ(kvStore->Put(key, value), INVALID_FIELD_TYPE); + + /** + * @tc.steps:step4. Put one data whose value has constrain violation with the schema. + * @tc.expected: step4. return CONSTRAIN_VIOLATION. + */ + std::string constrainDiffData = "{\"field_name1\":false,\"field_name2\":null}"; + value.assign(constrainDiffData.begin(), constrainDiffData.end()); + EXPECT_EQ(kvStore->Put(key, value), CONSTRAIN_VIOLATION); + + /** + * @tc.steps:step5. Put one data whose value has invalid json. + * @tc.expected: step5. return INVALID_FORMAT. + */ + std::string invalidJsonData = "{\"field_name1\":false,\"field_name2\":10"; + value.assign(invalidJsonData.begin(), invalidJsonData.end()); + EXPECT_EQ(kvStore->Put(key, value), INVALID_FORMAT); + + /** + * @tc.steps:step6. Put one data whose value is empty. + * @tc.expected: step6. return INVALID_FORMAT. + */ + value.clear(); + EXPECT_EQ(kvStore->Put(key, value), INVALID_FORMAT); + + /** + * @tc.steps:step7. Put one data whose value is match with the schema. + * @tc.expected: step7. return INVALID_FORMAT. + */ + std::string validJsonData = "{\"field_name1\":false,\"field_name2\":10}"; + value.assign(validJsonData.begin(), validJsonData.end()); + EXPECT_EQ(kvStore->Put(key, value), OK); + } + + void CheckPutBatchSchemaData(KvStoreNbDelegate *kvStore) + { + ASSERT_NE(kvStore, nullptr); + /** + * @tc.steps:step1. Put the batch data, one data is invalid. + * @tc.expected: step1. return INVALID_FORMAT. + */ + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string invalidData = "{\"field_name1\":true, \"field_name2\":null}"; + Value value(invalidData.begin(), invalidData.end()); + std::vector entries; + entries.push_back({key, value}); + + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string validData = "{\"field_name1\":true, \"field_name2\":0}"; + value.assign(validData.begin(), validData.end()); + entries.push_back({key, value}); + + EXPECT_NE(kvStore->PutBatch(entries), INVALID_FORMAT); + + entries.clear(); + entries.push_back({key, value}); + + /** + * @tc.steps:step2. Put the batch data, both valid. + * @tc.expected: step2. return OK. + */ + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + validData = "{\"field_name1\":null, \"field_name2\":30}"; + value.assign(validData.begin(), validData.end()); + entries.push_back({key, value}); + + EXPECT_EQ(kvStore->PutBatch(entries), OK); + } +} +class DistributedDBInterfacesNBDelegateSchemaPutTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesNBDelegateSchemaPutTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesNBDelegateSchemaPutTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesNBDelegateSchemaPutTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBInterfacesNBDelegateSchemaPutTest::TearDown(void) +{ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvStore), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(g_storeName), OK); + g_kvStore = nullptr; + g_kvDelegateStatus = INVALID_ARGS; +} + +/** + * @tc.name: PutValueStrictSchemaCheck001 + * @tc.desc: Check the value in the strict schema mode. + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBDelegateSchemaPutTest, PutValueStrictSchemaCheck001, TestSize.Level1) +{ + g_mgr.GetKvStore(g_storeName, g_strictOpt, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvStore != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step1. Put one data whose value has more fields than the schema. + * @tc.expected: step1. return INVALID_VALUE_FIELDS. + */ + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string moreData = "{\"field_name1\":true,\"field_name2\":10,\"field_name3\":10}"; + Value value(moreData.begin(), moreData.end()); + EXPECT_EQ(g_kvStore->Put(key, value), INVALID_VALUE_FIELDS); + /** + * @tc.steps:step2. Put the data whose value is mismatch with the schema. + * @tc.expected: step2. return not OK. + */ + CheckPutSchemaData(g_kvStore); + CheckPutBatchSchemaData(g_kvStore); +} + +/** + * @tc.name: PutValueReadOnlyCheck001 + * @tc.desc: Test writing the data into the no-schema kvStore which has schema originally. + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBDelegateSchemaPutTest, PutValueCompaSchemaCheck001, TestSize.Level1) +{ + g_mgr.GetKvStore(g_storeName, g_compOpt, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvStore != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps:step1. Put one data whose value has more fields than the schema. + * @tc.expected: step1. return OK. + */ + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string moreData = "{\"field_name1\":true,\"field_name2\":10,\"field_name3\":10}"; + Value value(moreData.begin(), moreData.end()); + EXPECT_EQ(g_kvStore->Put(key, value), OK); + /** + * @tc.steps:step2. Put the data whose value is mismatch with the schema. + * @tc.expected: step2. return not OK. + */ + CheckPutSchemaData(g_kvStore); + CheckPutBatchSchemaData(g_kvStore); +} + +/** + * @tc.name: PutValueReadOnlyCheck001 + * @tc.desc: Test writing the data into the no-schema kvStore which has schema originally. + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBDelegateSchemaPutTest, PutValueReadOnlyCheck001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open the kv store with valid schema, and close it. + */ + g_mgr.GetKvStore(g_storeName, g_compOpt, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvStore != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvStore), OK); + /** + * @tc.steps:step2. Open the kv store with no schema. + * @tc.expected: step2. return OK. + */ + DistributedDB::KvStoreNbDelegate::Option option = g_compOpt; + option.schema.clear(); + g_mgr.GetKvStore(g_storeName, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvStore != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps:step3. Put the data. + * @tc.expected: step3. return READ_ONLY. + */ + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + std::string valueData = "{\"field_name1\":true,\"field_name2\":20}"; + Value value(valueData.begin(), valueData.end()); + EXPECT_EQ(g_kvStore->Put(key, value), READ_ONLY); +} + +/** + * @tc.name: QueryDeleted001 + * @tc.desc: Test the query in the deleted scene. + * @tc.type: FUNC + * @tc.require: AR000DR9K5 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBDelegateSchemaPutTest, QueryDeleted001, TestSize.Level1) +{ + g_mgr.GetKvStore(g_storeName, g_strictOpt, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvStore != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step1. Put 2 schema data. + * @tc.expected: step1. return OK. + */ + Key key1; + std::string valueData = "{\"field_name1\":true,\"field_name2\":1}"; + Value value(valueData.begin(), valueData.end()); + DistributedDBToolsUnitTest::GetRandomKeyValue(key1); + EXPECT_EQ(g_kvStore->Put(key1, value), OK); + + Key key2; + valueData = "{\"field_name1\":true,\"field_name2\":2}"; + value.assign(valueData.begin(), valueData.end()); + DistributedDBToolsUnitTest::GetRandomKeyValue(key2); + EXPECT_EQ(g_kvStore->Put(key2, value), OK); + + /** + * @tc.steps:step2. Get the data through the query condition where the field value is 1. + * @tc.expected: step2. GetEntries return OK, and the data num is 1. + */ + std::vector entries; + KvStoreResultSet *resultSet = nullptr; + Query query = Query::Select().EqualTo("$.field_name2", 1); + EXPECT_EQ(g_kvStore->GetEntries(query, entries), OK); + EXPECT_EQ(g_kvStore->GetEntries(query, resultSet), OK); + ASSERT_NE(resultSet, nullptr); + EXPECT_EQ(resultSet->GetCount(), 1); + int count = 0; + EXPECT_EQ(g_kvStore->GetCount(query, count), OK); + EXPECT_EQ(count, 1); + EXPECT_EQ(g_kvStore->CloseResultSet(resultSet), OK); + + /** + * @tc.steps:step3. Delete the data whose field value is 1. + */ + EXPECT_EQ(g_kvStore->Delete(key1), OK); + + /** + * @tc.steps:step4. Get the data whose field value is 1. + * @tc.expected: step4. GetEntries return NOT_FOUND, and the data num is 0. + */ + EXPECT_EQ(g_kvStore->GetEntries(query, entries), NOT_FOUND); + EXPECT_EQ(g_kvStore->GetCount(query, count), NOT_FOUND); + EXPECT_EQ(g_kvStore->GetEntries(query, resultSet), OK); + ASSERT_NE(resultSet, nullptr); + EXPECT_EQ(resultSet->GetCount(), 0); + EXPECT_EQ(g_kvStore->CloseResultSet(resultSet), OK); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b75d8a6514f311f944def3dd28de001d8836a73 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_test.cpp @@ -0,0 +1,2019 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "platform_specific.h" +#include "process_system_api_adapter_impl.h" +#include "runtime_context.h" +#include "sqlite_single_ver_natural_store.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + string g_testDir; + KvStoreConfig g_config; + Key g_keyPrefix = {'A', 'B', 'C'}; + const int RESULT_SET_COUNT = 9; + const int RESULT_SET_INIT_POS = -1; + uint8_t g_testDict[RESULT_SET_COUNT] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; + + // define the g_kvNbDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + const int OBSERVER_SLEEP_TIME = 100; + const int BATCH_PRESET_SIZE_TEST = 10; + const int DIVIDE_BATCH_PRESET_SIZE = 5; + const int VALUE_OFFSET = 5; + + const int DEFAULT_KEY_VALUE_SIZE = 10; + + const int CON_PUT_THREAD_NUM = 4; + const int PER_THREAD_PUT_NUM = 100; + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + enum LockState { + UNLOCKED = 0, + LOCKED + }; + + void InitResultSet() + { + Key testKey; + Value testValue; + for (int i = 0; i < RESULT_SET_COUNT; i++) { + testKey.clear(); + testValue.clear(); + // set key + testKey = g_keyPrefix; + testKey.push_back(g_testDict[i]); + // set value + testValue.push_back(g_testDict[i]); + // insert entry + EXPECT_EQ(g_kvNbDelegatePtr->Put(testKey, testValue), OK); + } + } + + void ReadResultSet(KvStoreResultSet *readResultSet) + { + // index from 0 to 8(first to last) + for (int i = 0; i < RESULT_SET_COUNT; i++) { + Entry entry; + std::vector cursorKey = g_keyPrefix; + cursorKey.push_back(g_testDict[i]); + std::vector cursorValue; + cursorValue.push_back(g_testDict[i]); + EXPECT_TRUE(readResultSet->MoveToNext()); + EXPECT_EQ(readResultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, cursorKey); + EXPECT_EQ(entry.value, cursorValue); + EXPECT_TRUE(!readResultSet->IsBeforeFirst()); + EXPECT_TRUE(!readResultSet->IsAfterLast()); + } + // change index to 8(last) + EXPECT_EQ(readResultSet->GetPosition(), RESULT_SET_COUNT - 1); + EXPECT_TRUE(!readResultSet->IsFirst()); + EXPECT_TRUE(readResultSet->IsLast()); + EXPECT_TRUE(!readResultSet->IsBeforeFirst()); + EXPECT_TRUE(!readResultSet->IsAfterLast()); + } + + void CheckResultSetValue(KvStoreResultSet *readResultSet, DBStatus errCode, int position) + { + Entry entry; + EXPECT_EQ(readResultSet->GetPosition(), position); + EXPECT_EQ(readResultSet->GetEntry(entry), errCode); + if (errCode == OK) { + std::vector cursorKey; + std::vector cursorValue; + if (position > RESULT_SET_INIT_POS && position < RESULT_SET_COUNT) { + uint8_t keyPostfix = g_testDict[position]; + // set key + cursorKey = g_keyPrefix; + cursorKey.push_back(keyPostfix); + // set value + cursorValue.push_back(keyPostfix); + } + // check key and value + EXPECT_EQ(entry.key, cursorKey); + EXPECT_EQ(entry.value, cursorValue); + } + } + + std::vector g_entriesForConcurrency; + void PutData(KvStoreNbDelegate *kvStore, int flag) + { + for (int i = 0; i < PER_THREAD_PUT_NUM; i++) { + int index = flag * PER_THREAD_PUT_NUM + i; + kvStore->Put(g_entriesForConcurrency[index].key, g_entriesForConcurrency[index].value); + } + LOGD("%dth put has been finished", flag); + } + + bool CheckDataTimestamp(const std::string &storeId) + { + std::string identifier = USER_ID + "-" + APP_ID + "-" + storeId; + std::string hashIdentifier = DBCommon::TransferHashString(identifier); + std::string identifierName = DBCommon::TransferStringToHex(hashIdentifier); + std::string storeDir = g_testDir + "/" + identifierName + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::MAINDB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + sqlite3 *db = nullptr; + EXPECT_EQ(sqlite3_open_v2(storeDir.c_str(), &db, SQLITE_OPEN_READWRITE, nullptr), SQLITE_OK); + if (db == nullptr) { + return false; + } + + static const std::string selectSQL = "select timestamp from sync_data order by rowid;"; + sqlite3_stmt *statement = nullptr; + EXPECT_EQ(sqlite3_prepare(db, selectSQL.c_str(), -1, &statement, NULL), SQLITE_OK); + std::vector timeVect; + while (sqlite3_step(statement) == SQLITE_ROW) { + timeVect.push_back(sqlite3_column_int64(statement, 0)); + } + + sqlite3_finalize(statement); + statement = nullptr; + (void)sqlite3_close_v2(db); + db = nullptr; + EXPECT_EQ(timeVect.size(), g_entriesForConcurrency.size()); + bool resultCheck = true; + if (g_entriesForConcurrency.size() > 1) { + for (size_t i = 1; i < timeVect.size(); i++) { + if (timeVect[i] <= timeVect[i - 1]) { + resultCheck = false; + break; + } + } + } + + return resultCheck; + } +} +class DistributedDBInterfacesNBDelegateTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesNBDelegateTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesNBDelegateTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); +} + +void DistributedDBInterfacesNBDelegateTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvNbDelegatePtr = nullptr; + g_kvDelegatePtr = nullptr; +} + +void DistributedDBInterfacesNBDelegateTest::TearDown(void) +{ + if (g_kvDelegatePtr != nullptr) { + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_kvNbDelegatePtr = nullptr; + } + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); +} + +/** + * @tc.name: CombineTest001 + * @tc.desc: Test the NbDelegate for combined operation. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, CombineTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_nb_delegate_test", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + Key key; + key = {'A', 'C', 'Q'}; + Value value; + value = {'G', 'D', 'O'}; + Value valueRead; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, observer), OK); + /** + * @tc.steps:step3. Put the local data. + * @tc.expected: step3. Put returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(key, value), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + /** + * @tc.steps:step4. Check the local data. + * @tc.expected: step4. The get data is equal to the put data. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(key, valueRead), OK); + /** + * @tc.steps:step5. Delete the local data. + * @tc.expected: step5. Delete returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocal(key), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + /** + * @tc.steps:step6. Check the local data. + * @tc.expected: step6. Couldn't find the deleted data. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(key, valueRead), NOT_FOUND); + /** + * @tc.steps:step7. UnRegister the observer. + * @tc.expected: step7. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + Key key1; + key1 = {'D', 'B', 'N'}; + Value value1; + value1 = {'P', 'D', 'G'}; + + Key key2 = key1; + Value value2; + key2.push_back('U'); + value2 = {'C'}; + /** + * @tc.steps:step8. Put the data. + * @tc.expected: step8. Put returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(key1, value1), OK); + Value valueRead2; + /** + * @tc.steps:step9. Check the data. + * @tc.expected: step9. Getting the put data returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(key1, valueRead2), OK); + /** + * @tc.steps:step10. Put another data. + * @tc.expected: step10. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(key2, value2), OK); + std::vector vect; + /** + * @tc.steps:step10. Get the batch data using the prefix key. + * @tc.expected: step10. Results OK and the batch data size is equal to the put data size. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(key1, vect), OK); + EXPECT_EQ(vect.size(), 2UL); + /** + * @tc.steps:step11. Delete one data. + * @tc.expected: step11. Results OK and couldn't get the deleted data. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Delete(key1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Get(key1, valueRead2), NOT_FOUND); + + LOGD("Close store"); + /** + * @tc.steps:step12. Close the kv store. + * @tc.expected: step12. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_delegate_test"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: CreateMemoryDb001 + * @tc.desc: Create memory database after. + * @tc.type: FUNC + * @tc.require: AR000CRAKN + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, CreateMemoryDb001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create Memory database by GetKvStore. + * @tc.expected: step1. Create successfully. + */ + const KvStoreNbDelegate::Option option = {true, true}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_Memorykvstore_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *kvNbDelegatePtr001 = g_kvNbDelegatePtr; + + /** + * @tc.steps: step2. Duplicate create Memory database by GetKvStore. + * @tc.expected: step2. Duplicate create successfully. + */ + g_mgr.GetKvStore("distributed_Memorykvstore_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step3. Duplicate create Memory database by GetKvStore. + * @tc.expected: step3. Duplicate create successfully. + */ + g_mgr.GetKvStore("distributed_Memorykvstore_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *kvNbDelegatePtr002 = g_kvNbDelegatePtr; + + g_mgr.CloseKvStore(kvNbDelegatePtr001); + g_mgr.CloseKvStore(kvNbDelegatePtr002); +} + +/** + * @tc.name: CreateMemoryDb002 + * @tc.desc: The MemoryDB cannot be created or open, when the physical database has been opened + * @tc.type: FUNC + * @tc.require: AR000CRAKN + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, CreateMemoryDb002, TestSize.Level1) +{ + KvStoreNbDelegate::Option option = {true, true}; + /** + * @tc.steps: step1. Create SingleVer database by GetKvStore. + * @tc.expected: step1. Create database success. + */ + g_mgr.GetKvStore("distributed_Memorykvstore_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreNbDelegate *delegate1 = g_kvNbDelegatePtr; + g_kvNbDelegatePtr = nullptr; + + /** + * @tc.steps: step2. Create Memory database by GetKvStore. + * @tc.expected: step2. Create Memory database fail. + */ + option.isMemoryDb = false; + g_mgr.GetKvStore("distributed_Memorykvstore_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus != OK); + g_mgr.CloseKvStore(delegate1); + delegate1 = nullptr; +} + +/** + * @tc.name: CreateMemoryDb003 + * @tc.desc: The physical database cannot be created or open, when the MemoryDB has been opened. + * @tc.type: FUNC + * @tc.require: AR000CRAKN + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, CreateMemoryDb003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get singleVer kvStore by GetKvStore. + * @tc.expected: step1. Get database success. + */ + KvStoreDelegate::Option option; + g_mgr.GetKvStore("distributed_Memorykvstore_003", option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step2. Create Memory database by GetKvStore. + * @tc.expected: step2. Create Memory database fail. + */ + KvStoreNbDelegate::Option nbOption = {true, true}; + g_mgr.GetKvStore("distributed_Memorykvstore_003", nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr == nullptr); + EXPECT_TRUE(g_kvDelegateStatus != OK); + g_mgr.CloseKvStore(g_kvDelegatePtr); + g_kvDelegatePtr = nullptr; +} + +/** + * @tc.name: OperMemoryDbData001 + * @tc.desc: Operate memory database + * @tc.type: FUNC + * @tc.require: AR000CRAKN + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, OperMemoryDbData001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create Memory database by GetKvStore. + */ + const KvStoreNbDelegate::Option option = {true, true}; + g_mgr.GetKvStore("distributed_OperMemorykvstore_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step2. Put (KEY_1,VALUE_1)(KEY_2,VALUE_2) to Memory database. + * @tc.expected: step2. Success. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_2), OK); + + /** + * @tc.steps: step3. Get (KEY_1,VALUE_1)(KEY_2,VALUE_2) to Memory database. + * @tc.expected: step3. Success. + */ + Value readValueKey1; + Value readValueKey2; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValueKey1), OK); + EXPECT_EQ(readValueKey1, VALUE_1); + + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValueKey2), OK); + EXPECT_EQ(readValueKey2, VALUE_2); + + /** + * @tc.steps: step4. Delete K1 from Memory database. + * @tc.expected: step4. Success. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_1), OK); + + /** + * @tc.steps: step5. Get K1 from Memory database. + * @tc.expected: step5. NOT_FOUND. + */ + readValueKey1.clear(); + readValueKey2.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValueKey1), NOT_FOUND); + + /** + * @tc.steps: step6. Update K2 value from Memory database. + * @tc.expected: step6. Get the right value after the update. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_3), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValueKey2), OK); + EXPECT_EQ(readValueKey2, VALUE_3); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); +} + +/** + * @tc.name: CloseMemoryDb001 + * @tc.desc: Operate memory database after reopen memory database + * @tc.type: FUNC + * @tc.require: AR000CRAKN + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, CloseMemoryDb001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create Memory database by GetKvStore. + */ + const KvStoreNbDelegate::Option option = {true, true}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_CloseMemorykvstore_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step2/3. Put and get to Memory database. + * @tc.expected: step2/3. Success and the value is right. + */ + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + + /** + * @tc.steps: step4. Close the Memory database. + * @tc.expected: step4. Success. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step5. Reopen the Memory database. + * @tc.expected: step5. Success. + */ + g_mgr.GetKvStore("distributed_CloseMemorykvstore_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step6. Get the key1 which has been put into the Memory database. + * @tc.expected: step6. Return NOT_FOUND. + */ + readValue.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), NOT_FOUND); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); +} + +/** + * @tc.name: ResultSetTest001 + * @tc.desc: Test the NbDelegate for result set function. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, ResultSetTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. initialize result set. + * @tc.expected: step1. Success. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_nb_delegate_result_set_test", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + InitResultSet(); + + /** + * @tc.steps: step2. get entries using result set. + * @tc.expected: step2. Success. + */ + KvStoreResultSet *readResultSet = nullptr; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(g_keyPrefix, readResultSet), OK); + ASSERT_TRUE(readResultSet != nullptr); + EXPECT_EQ(readResultSet->GetCount(), RESULT_SET_COUNT); + + /** + * @tc.steps: step3. result function check. + * @tc.expected: step3. Success. + */ + CheckResultSetValue(readResultSet, NOT_FOUND, RESULT_SET_INIT_POS); + // index from 0 to 8(first to last) + ReadResultSet(readResultSet); + // change index to 9(after last) + EXPECT_TRUE(!readResultSet->MoveToNext()); + CheckResultSetValue(readResultSet, NOT_FOUND, RESULT_SET_COUNT); + // change index to 8(last) + EXPECT_TRUE(readResultSet->MoveToPrevious()); + CheckResultSetValue(readResultSet, OK, RESULT_SET_COUNT - 1); + // change index to 0(first) + EXPECT_TRUE(readResultSet->MoveToFirst()); + CheckResultSetValue(readResultSet, OK, RESULT_SET_INIT_POS + 1); + // change index to 8(last) + EXPECT_TRUE(readResultSet->MoveToLast()); + CheckResultSetValue(readResultSet, OK, RESULT_SET_COUNT - 1); + // move to -4: change index to -1 + EXPECT_TRUE(!readResultSet->MoveToPosition(RESULT_SET_INIT_POS - 3)); + CheckResultSetValue(readResultSet, NOT_FOUND, RESULT_SET_INIT_POS); + // move to 10: change index to 9 + EXPECT_TRUE(!readResultSet->MoveToPosition(RESULT_SET_COUNT + 1)); + CheckResultSetValue(readResultSet, NOT_FOUND, RESULT_SET_COUNT); + // change index to 2 + EXPECT_TRUE(readResultSet->MoveToPosition(RESULT_SET_INIT_POS + 3)); + CheckResultSetValue(readResultSet, OK, RESULT_SET_INIT_POS + 3); + // move 0: change index to 2 + EXPECT_TRUE(readResultSet->Move(0)); + CheckResultSetValue(readResultSet, OK, RESULT_SET_INIT_POS + 3); + // change index to 6 + EXPECT_TRUE(readResultSet->Move(RESULT_SET_INIT_POS + 5)); + CheckResultSetValue(readResultSet, OK, RESULT_SET_INIT_POS + 7); + // change index to 3 + EXPECT_TRUE(readResultSet->Move(RESULT_SET_INIT_POS - 2)); + CheckResultSetValue(readResultSet, OK, RESULT_SET_INIT_POS + 4); + // move -5: change index to -1 + EXPECT_TRUE(!readResultSet->Move(-5)); + CheckResultSetValue(readResultSet, NOT_FOUND, RESULT_SET_INIT_POS); + + // move INT_MIN: change index to -1 + EXPECT_TRUE(!readResultSet->Move(INT_MIN)); + CheckResultSetValue(readResultSet, NOT_FOUND, RESULT_SET_INIT_POS); + + EXPECT_TRUE(readResultSet->Move(5)); + EXPECT_TRUE(!readResultSet->Move(INT_MAX)); + CheckResultSetValue(readResultSet, NOT_FOUND, RESULT_SET_COUNT); + + /** + * @tc.steps: step4. clear the result set resource. + * @tc.expected: step4. Success. + */ + EXPECT_EQ(g_kvNbDelegatePtr->CloseResultSet(readResultSet), OK); + EXPECT_TRUE(readResultSet == nullptr); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_delegate_result_set_test"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: PutBatchVerify001 + * @tc.desc: This test case use to verify the putBatch interface function + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, PutBatchVerify001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get singleVer kvStore by GetKvStore. + * @tc.expected: step1. Get database success. + */ + const KvStoreNbDelegate::Option option = {true, true}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_PutBatchVerify_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step2. Insert 10 records into database. + * @tc.expected: step2. Insert successfully. + */ + vector entries; + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i); + entries.push_back(entry); + } + + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entries), OK); + + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Key key; + key.push_back(i); + Value value; + g_kvNbDelegatePtr->Get(key, value); + EXPECT_EQ(key, value); + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutBatch001 + * @tc.desc: Check for illegal parameters + * @tc.type: FUNC + * @tc.require: AR000DPTQ8 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerPutBatch001, TestSize.Level1) +{ + /** + * @tc.steps: step1. + * Create and construct three sets of vector , each set of three data contains records: + * (K1, V1) It is illegal for K1 to be greater than 1K, and V1 is 1K in size + * (K2, V2) K2 is legal, V2 is greater than 4M + * (K3, V3) are not legal. + */ + Key illegalKey; + DistributedDBToolsUnitTest::GetRandomKeyValue(illegalKey, DBConstant::MAX_KEY_SIZE + 1); // 1K + 1 + Value illegalValue; + DistributedDBToolsUnitTest::GetRandomKeyValue(illegalValue, DBConstant::MAX_VALUE_SIZE + 1); // 4M + 1 + vector entrysKeyIllegal = {KV_ENTRY_1, KV_ENTRY_2, {illegalKey, VALUE_3}}; + vector entrysValueIllegal = {KV_ENTRY_1, KV_ENTRY_2, {KEY_3, illegalValue}}; + vector entrysIllegal = {KV_ENTRY_1, KV_ENTRY_2, {illegalKey, illegalValue}}; + + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutBatch_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step2. PutBatch operates on three sets of data. + * @tc.expected: step2. All three operations return INVALID_ARGS. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysKeyIllegal), INVALID_ARGS); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysValueIllegal), INVALID_ARGS); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysIllegal), INVALID_ARGS); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatch_001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutBatch002 + * @tc.desc: PutBatch normal insert function test. + * @tc.type: FUNC + * @tc.require: AR000DPTQ8 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerPutBatch002, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutBatch_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step1. + * Create and build 4 groups of vector , which are: + * Vect of empty objects; + * Vect1 of a legal Entry record; + * 128 legal Entry records Vect2; + * 129 legal Entry records Vect3; + */ + vector entrysMaxNumber; + for (size_t i = 0; i < DBConstant::MAX_BATCH_SIZE; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i); + entrysMaxNumber.push_back(entry); + } + Key keyTemp = {'1', '1'}; + Value valueTemp; + Entry entryTemp = {keyTemp, VALUE_1}; + vector entrysOneRecord = {entryTemp}; + vector entrysOverSize = entrysMaxNumber; + entrysOverSize.push_back(entryTemp); + /** + * @tc.steps: step2. PutBatch operates on four sets of data. and use get check the result of Vect3. + * @tc.expected: step2. Returns INVALID_ARGS for 129 records, and returns OK for the rest. all get return NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysOverSize), INVALID_ARGS); + for (size_t i = 0; i < entrysOverSize.size(); i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysOverSize[i].key, valueTemp), NOT_FOUND); + } + /** + * @tc.steps: step3. Use get check the result of Vect2. + * @tc.expected: step3. Return OK and get the correct value. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysOneRecord), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Get(keyTemp, valueTemp), OK); + EXPECT_EQ(valueTemp, VALUE_1); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysMaxNumber), OK); + /** + * @tc.steps: step4. Use get check the result of Vect3. + * @tc.expected: step4. Return OK and get the correct value. + */ + for (size_t i = 0; i < entrysMaxNumber.size(); i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysMaxNumber[i].key, valueTemp), OK); + EXPECT_EQ(valueTemp, entrysMaxNumber[i].value); + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatch_002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutBatch003 + * @tc.desc: Check interface atomicity + * @tc.type: FUNC + * @tc.require: AR000DPTQ8 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerPutBatch003, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutBatch_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step1. Create and construct a set of vector with a total of 128 data, + * including one illegal data. And call PutBatch interface to insert. + */ + vector entrysMaxNumber; + for (size_t i = 0; i < DBConstant::MAX_BATCH_SIZE; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i); + entrysMaxNumber.push_back(entry); + } + Key illegalKey; + Value valueTemp; + DistributedDBToolsUnitTest::GetRandomKeyValue(illegalKey, DBConstant::MAX_KEY_SIZE + 1); // 1K + 1 + entrysMaxNumber[0].key = illegalKey; + + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysMaxNumber), INVALID_ARGS); + /** + * @tc.steps: step2. Use Get interface to query 128 corresponding key values. + * @tc.expected: step2. All Get interface return NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysMaxNumber[0].key, valueTemp), INVALID_ARGS); + for (size_t i = 1; i < entrysMaxNumber.size(); i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysMaxNumber[i].key, valueTemp), NOT_FOUND); + } + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatch_003"), OK); + g_kvNbDelegatePtr = nullptr; +} + +static void PreparePutBatch004(vector &entrys1, vector &entrys2, vector &entrys3) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutBatch_004", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i); + entrys1.push_back(entry); + } + + for (int i = 0; i < DIVIDE_BATCH_PRESET_SIZE; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i + VALUE_OFFSET); + entrys2.push_back(entry); + } + + for (int i = DIVIDE_BATCH_PRESET_SIZE; i < BATCH_PRESET_SIZE_TEST; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(i - VALUE_OFFSET); + entrys3.push_back(entry); + } +} + +/** + * @tc.name: SingleVerPutBatch004 + * @tc.desc: Check interface data insertion and update functions. + * @tc.type: FUNC + * @tc.require: AR000DPTQ8 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerPutBatch004, TestSize.Level1) +{ + /** + * @tc.steps: step1. + * Construct three groups of three vector : + * (1) entrys1: key1 ~ 10, corresponding to Value1 ~ 10; + * (2) entrys2: key1 ~ 5, corresponding to Value6 ~ 10; + * (3) entrys3: key6 ~ 10, corresponding to Value1 ~ 5; + */ + vector entrys1; + vector entrys2; + vector entrys3; + PreparePutBatch004(entrys1, entrys2, entrys3); + /** + * @tc.steps: step2. PutBatch entrys2. + * @tc.expected: step2. PutBatch return OK. + */ + Value valueRead; + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys2), OK); + /** + * @tc.steps: step3. Check PutBatch result. + * @tc.expected: step3. Get correct value of key1~5. Key6~10 return NOT_FOUND. + */ + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Key keyTemp; + keyTemp.push_back(i); + if (i < DIVIDE_BATCH_PRESET_SIZE) { + Value valueTemp; + valueTemp.push_back(i + VALUE_OFFSET); + EXPECT_EQ(g_kvNbDelegatePtr->Get(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, valueTemp); + continue; + } + EXPECT_EQ(g_kvNbDelegatePtr->Get(keyTemp, valueRead), NOT_FOUND); + } + /** + * @tc.steps: step4. PutBatch entrys1. + * @tc.expected: step4. PutBatch return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys1), OK); + /** + * @tc.steps: step5. Check PutBatch result. + * @tc.expected: step5. Update and insert value of key1~10 to value1~10. + */ + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Key keyTemp; + keyTemp.push_back(i); + if (i < DIVIDE_BATCH_PRESET_SIZE) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, keyTemp); + continue; + } + EXPECT_EQ(g_kvNbDelegatePtr->Get(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, keyTemp); + } + /** + * @tc.steps: step6. PutBatch entrys3. + * @tc.expected: step6. PutBatch return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys3), OK); + /** + * @tc.steps: step7. Check PutBatch result of key1~10. + * @tc.expected: step7. Update value of key5~10 to value1~5. + */ + for (int i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + Key keyTemp; + keyTemp.push_back(i); + if (i < DIVIDE_BATCH_PRESET_SIZE) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, keyTemp); + continue; + } + Value valueTemp; + valueTemp.push_back(i - VALUE_OFFSET); + EXPECT_EQ(g_kvNbDelegatePtr->Get(keyTemp, valueRead), OK); + EXPECT_EQ(valueRead, valueTemp); + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatch_004"), OK); + g_kvNbDelegatePtr = nullptr; +} + +static void CreatEntrys(int recordSize, vector &keys, vector &values, vector &entries) +{ + keys.clear(); + values.clear(); + entries.clear(); + for (int i = 0; i < recordSize; i++) { + string temp = to_string(i); + Entry entry; + Key keyTemp; + Value valueTemp; + for (auto &iter : temp) { + entry.key.push_back(iter); + entry.value.push_back(iter); + keyTemp.push_back(iter); + valueTemp.push_back(iter); + } + keys.push_back(keyTemp); + values.push_back(valueTemp); + entries.push_back(entry); + } +} + +/** + * @tc.name: SingleVerDeleteBatch001 + * @tc.desc: Check for illegal parameters. + * @tc.type: FUNC + * @tc.require: AR000DPTQ8 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerDeleteBatch001, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutBatch_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step1. Create and construct a set of vector , containing a total of 10 data keys1 ~ 10, + * Value1 ~ 10, and call Putbatch interface to insert data. + * @tc.expected: step1. PutBatch successfully. + */ + vector entries; + vector keys; + vector values; + Value valueRead; + CreatEntrys(BATCH_PRESET_SIZE_TEST, keys, values, entries); + vector entrysBase = entries; + vector keysBase = keys; + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysBase), OK); + /** + * @tc.steps: step2. Use Get to check data in database. + * @tc.expected: step2. Get value1~10 by key1~10 successfully. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysBase[i].key, valueRead), OK); + } + /** + * @tc.steps: step3. Use DeleteBatch interface to transfer 10 + 119 extra keys (total 129). + * @tc.expected: step3. Return INVALID_ARGS. + */ + CreatEntrys(DBConstant::MAX_BATCH_SIZE + 1, keys, values, entries); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys), INVALID_ARGS); + /** + * @tc.steps: step4. Use Get to check data in database. + * @tc.expected: step4. Key1~10 still in database. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysBase[i].key, valueRead), OK); + } + /** + * @tc.steps: step5. Use the DeleteBatch interface to pass in 10 included + * keys6 ~ 10 + 123 additional key values ​​(128 in total). + * @tc.expected: step5. DeleteBatch OK. + */ + CreatEntrys(DBConstant::MAX_BATCH_SIZE + DIVIDE_BATCH_PRESET_SIZE, keys, values, entries); + keys.erase(keys.begin(), keys.begin() + DIVIDE_BATCH_PRESET_SIZE); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys), OK); + /** + * @tc.steps: step6. Use Get to check key1~10 in database. + * @tc.expected: step6. Key1~5 in database, key6~10 have been deleted. + */ + for (size_t i = 0; i < DIVIDE_BATCH_PRESET_SIZE; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysBase[i].key, valueRead), OK); + } + for (size_t i = DIVIDE_BATCH_PRESET_SIZE; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysBase[i].key, valueRead), NOT_FOUND); + } + /** + * @tc.steps: step7. Repeat Putbatch key1~10, value1~10. + * @tc.expected: step7. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysBase), OK); + + Key illegalKey; + DistributedDBToolsUnitTest::GetRandomKeyValue(illegalKey, DBConstant::MAX_KEY_SIZE + 1); // 1K + 1 + keysBase.push_back(illegalKey); + /** + * @tc.steps: step8. Use DeleteBatch interface to pass in 10 + 1(larger than 1K) keys. + * @tc.expected: step8. Return INVALID_ARGS. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keysBase), INVALID_ARGS); + /** + * @tc.steps: step9. Use Get to check key1~10 in database. + * @tc.expected: step9. Delete those data failed. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysBase[i].key, valueRead), OK); + } + /** + * @tc.steps: step10. Use DeleteBatch interface to pass in 10(in database) + 1 valid keys. + * @tc.expected: step10. Delete those data successfully. + */ + keysBase.back().erase(keysBase.back().begin(), keysBase.back().begin() + 1); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keysBase), OK); + /** + * @tc.steps: step11. Check data. + * @tc.expected: step11. DeleteBatch successfully. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(entrysBase[i].key, valueRead), NOT_FOUND); + } + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatch_001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerDeleteBatch002 + * @tc.desc: Check normal delete batch ability. + * @tc.type: FUNC + * @tc.require: AR000DPTQ8 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerDeleteBatch002, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.GetKvStore("distributed_SingleVerPutBatch_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps: step1. Create a group of vector , containing a total of 10 data keys1 ~ 10, Value1 ~ 10, + * call the Putbatch interface to insert data. + * @tc.expected: step1. Insert to database successfully. + */ + vector entries; + vector keysBase; + vector values; + CreatEntrys(BATCH_PRESET_SIZE_TEST, keysBase, values, entries); + + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entries), OK); + /** + * @tc.steps: step2. Check data. + * @tc.expected: step2. Get key1~10 successfully. + */ + Value valueRead; + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(keysBase[i], valueRead), OK); + } + /** + * @tc.steps: step3. DeleteBatch key1~5. + * @tc.expected: step3. Return OK. + */ + vector keys(keysBase.begin(), keysBase.begin() + DIVIDE_BATCH_PRESET_SIZE); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys), OK); + /** + * @tc.steps: step4. Check key1~10. + * @tc.expected: step4. Key1~5 deleted, key6~10 existed. + */ + for (size_t i = 0; i < DIVIDE_BATCH_PRESET_SIZE; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(keysBase[i], valueRead), NOT_FOUND); + } + for (size_t i = DIVIDE_BATCH_PRESET_SIZE; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(keysBase[i], valueRead), OK); + } + /** + * @tc.steps: step5. DeleteBatch key1~10. + * @tc.expected: step5. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keysBase), OK); + /** + * @tc.steps: step6. Check key1~10. + * @tc.expected: step6. Key1~10 deleted successfully. + */ + for (size_t i = 0; i < BATCH_PRESET_SIZE_TEST; i++) { + EXPECT_EQ(g_kvNbDelegatePtr->Get(keysBase[i], valueRead), NOT_FOUND); + } + /** + * @tc.steps: step7. DeleteBatch key1~10 once again. + * @tc.expected: step7. Return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keysBase), OK); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatch_002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutBatchObserver001 + * @tc.desc: Test the observer function of PutBatch() interface. + * @tc.type: FUNC + * @tc.require: AR000DPTTA + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerPutBatchObserver001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerPutBatchObserver_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, observer), OK); + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + vector entrysBase; + vector keysBase; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST + 1, entrysBase, keysBase); + + vector entries(entrysBase.begin(), entrysBase.end() - 1); + EXPECT_EQ(entries.size(), 10UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entries), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entries, observer->GetEntriesInserted())); + /** + * @tc.steps:step4. Delete the batch data. + * @tc.expected: step4. Returns OK. + */ + vector keys(keysBase.begin() + 5, keysBase.end()); + EXPECT_EQ(keys.size(), 6UL); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + vector entrysDel(entrysBase.begin() + 5, entrysBase.end() - 1); + EXPECT_EQ(entrysDel.size(), 5UL); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrysDel, observer->GetEntriesDeleted())); + /** + * @tc.steps:step5. UnRegister the observer. + * @tc.expected: step5. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatchObserver_001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutBatchObserver002 + * @tc.desc: Test the observer function of PutBatch() for invalid input. + * @tc.type: FUNC + * @tc.require: AR000DPTTA + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerPutBatchObserver002, TestSize.Level4) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerPutBatchObserver_002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, observer), OK); + /** + * @tc.steps:step3. Put 129 batch data. + * @tc.expected: step3. Returns INVALID_ARGS. + */ + vector entrys1; + vector keys1; + DistributedDBUnitTest::GenerateRecords(DBConstant::MAX_BATCH_SIZE + 1, entrys1, keys1); + + EXPECT_EQ(entrys1.size(), 129UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys1), INVALID_ARGS); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(observer->GetEntriesInserted().empty()); + /** + * @tc.steps:step4. Put invalid batch data. + * @tc.expected: step4. Returns INVALID_ARGS. + */ + vector entrys2; + vector keys2; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys2, keys2); + EXPECT_EQ(entrys2.size(), 10UL); + + vector entrysInvalid; + vector keysInvalid; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrysInvalid, keysInvalid, + DBConstant::MAX_KEY_SIZE + 10); + EXPECT_EQ(entrysInvalid.size(), 10UL); + entrys2[0].key = entrysInvalid[0].key; + + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys2), INVALID_ARGS); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(observer->GetEntriesInserted().empty()); + /** + * @tc.steps:step5. Put MAX valid value batch data. + * @tc.expected: step5. Returns OK. + */ + vector entrys3; + vector keys3; + + DistributedDBUnitTest::GenerateRecords(DBConstant::MAX_BATCH_SIZE, entrys3, keys3); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys3), OK); + LOGD("sleep begin"); + // sleep 20 seconds + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME * 10)); + LOGD("sleep end"); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrys3, observer->GetEntriesInserted())); + /** + * @tc.steps:step6. UnRegister the observer. + * @tc.expected: step6. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + /** + * @tc.steps:step7. Close the kv store. + * @tc.expected: step7. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatchObserver_002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutBatchObserver003 + * @tc.desc: Test the observer function of PutBatch() update function. + * @tc.type: FUNC + * @tc.require: AR000DPTTA + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerPutBatchObserver003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerPutBatchObserver_003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, observer), OK); + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + vector entrysAdd; + vector keysAdd; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrysAdd, keysAdd); + + EXPECT_EQ(entrysAdd.size(), 10UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysAdd), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrysAdd, observer->GetEntriesInserted())); + /** + * @tc.steps:step4. Update the batch data. + * @tc.expected: step4. Returns OK. + */ + vector entrysUpdate; + vector keysUpdate; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrysUpdate, keysUpdate, DEFAULT_KEY_VALUE_SIZE, + DEFAULT_KEY_VALUE_SIZE + 10); + + EXPECT_EQ(entrysUpdate.size(), 10UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysUpdate), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrysUpdate, observer->GetEntriesUpdated())); + /** + * @tc.steps:step5. UnRegister the observer. + * @tc.expected: step5. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatchObserver_003"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPutBatchObserver004 + * @tc.desc: Test the observer function of PutBatch(), same keys handle. + * @tc.type: FUNC + * @tc.require: AR000DPTTA + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerPutBatchObserver004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerPutBatchObserver_004", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, observer), OK); + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + vector entrys1; + vector keys1; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys1, keys1); + vector entrys2; + vector keys2; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys2, keys2, DEFAULT_KEY_VALUE_SIZE, + DEFAULT_KEY_VALUE_SIZE + 10); + entrys1.insert(entrys1.end(), entrys2.begin(), entrys2.end()); + + EXPECT_EQ(entrys1.size(), 20UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys1), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrys2, observer->GetEntriesInserted())); + EXPECT_EQ(observer->GetEntriesUpdated().size(), 0UL); + + vector entrys3; + vector keys3; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys3, keys3, DEFAULT_KEY_VALUE_SIZE, + DEFAULT_KEY_VALUE_SIZE + 20); + vector entrys4; + vector keys4; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entrys4, keys4, DEFAULT_KEY_VALUE_SIZE, + DEFAULT_KEY_VALUE_SIZE + 30); + entrys3.insert(entrys3.end(), entrys4.begin(), entrys4.end()); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys3), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entrys4, observer->GetEntriesUpdated())); + EXPECT_EQ(observer->GetEntriesInserted().size(), 0UL); + + /** + * @tc.steps:step4. UnRegister the observer. + * @tc.expected: step4. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + /** + * @tc.steps:step5. Close the kv store. + * @tc.expected: step5. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerPutBatchObserver_004"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerDeleteBatchObserver001 + * @tc.desc: Test the observer function of DeleteBatch() interface. + * @tc.type: FUNC + * @tc.require: AR000DPTTA + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerDeleteBatchObserver001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("distributed_SingleVerDeleteBatchObserver_001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, observer), OK); + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + vector entries; + vector keys; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entries, keys); + EXPECT_EQ(entries.size(), 10UL); + + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entries), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entries, observer->GetEntriesInserted())); + /** + * @tc.steps:step4. Delete the batch data. + * @tc.expected: step4. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entries, observer->GetEntriesDeleted())); + /** + * @tc.steps:step5. UnRegister the observer. + * @tc.expected: step5. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_SingleVerDeleteBatchObserver_001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerConcurrentPut001 + * @tc.desc: Test put the data concurrently, and check the timestamp. + * @tc.type: FUNC + * @tc.require: AR000DPTTA + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerConcurrentPut001, TestSize.Level4) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("concurrentPutTest", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + std::vector entries; + for (size_t i = 0; i < CON_PUT_THREAD_NUM * PER_THREAD_PUT_NUM; i++) { + Entry entry; + DistributedDBToolsUnitTest::GetRandomKeyValue(entry.key, DEFAULT_KEY_VALUE_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry.value); + g_entriesForConcurrency.push_back(std::move(entry)); + } + + /** + * @tc.steps:step2. Put data concurrently in 4 threads. + * @tc.expected: step2. Put OK, and the timestamp order is same with the rowid. + */ + std::thread thread1(std::bind(PutData, g_kvNbDelegatePtr, 0)); // 0th thread. + std::thread thread2(std::bind(PutData, g_kvNbDelegatePtr, 1)); // 1th thread. + std::thread thread3(std::bind(PutData, g_kvNbDelegatePtr, 2)); // 2th thread. + std::thread thread4(std::bind(PutData, g_kvNbDelegatePtr, 3)); // 3th thread. + + thread1.join(); + thread2.join(); + thread3.join(); + thread4.join(); + + EXPECT_EQ(CheckDataTimestamp("concurrentPutTest"), true); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("concurrentPutTest"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerGetLocalEntries001 + * @tc.desc: Test GetLocalEntries interface for the single ver database. + * @tc.type: FUNC + * @tc.require: AR000DPTTA + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerGetLocalEntries001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("concurrentPutTest", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Put one data whose key has prefix 'p' into the local zone. + */ + Entry entry1 = {{'p'}, {'q'}}; + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(entry1.key, entry1.value), OK); + + /** + * @tc.steps:step3. Get batch data whose key has prefix 'k' from the local zone. + * @tc.expected: step3. Get results NOT_FOUND. + */ + std::vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries({'k'}, entries), NOT_FOUND); + + /** + * @tc.steps:step4. Put two data whose key have prefix 'k' into the local zone. + */ + Entry entry2 = {{'k', '1'}, {'d'}}; + Entry entry3 = {{'k', '2'}, {'d'}}; + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(entry2.key, entry2.value), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(entry3.key, entry3.value), OK); + + /** + * @tc.steps:step5. Get batch data whose key has prefix 'k' from the local zone. + * @tc.expected: step5. Get results OK, and the entries size is 2. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries({'k'}, entries), OK); + EXPECT_EQ(entries.size(), 2UL); + + /** + * @tc.steps:step6. Get batch data whose key has empty prefix from the local zone. + * @tc.expected: step6. Get results OK, and the entries size is 3. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries({}, entries), OK); + EXPECT_EQ(entries.size(), 3UL); + + /** + * @tc.steps:step7. Delete one data whose key has prefix 'k' from the local zone. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocal(entry3.key), OK); + + /** + * @tc.steps:step8. Get batch data whose key has prefix 'k' from the local zone. + * @tc.expected: step8. Get results OK, and the entries size is 1. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries({'k'}, entries), OK); + EXPECT_EQ(entries.size(), 1UL); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("concurrentPutTest"), OK); + g_kvNbDelegatePtr = nullptr; +} + +static vector PreDataForQueryByPreFixKey() +{ + vector res; + for (int i = 0; i < 5; i++) { // Random 5 for test + Key key = DistributedDBToolsUnitTest::GetRandPrefixKey({'a', 'b'}, 1024); + std::string validData = "{\"field_name1\":null, \"field_name2\":" + std::to_string(rand()) + "}"; + Value value(validData.begin(), validData.end()); + res.push_back({key, value}); + } + + for (int i = 0; i < 5; i++) { // Random 5 for test + Key key = DistributedDBToolsUnitTest::GetRandPrefixKey({'a', 'c'}, 1024); + std::string validData = "{\"field_name1\":null, \"field_name2\":" + std::to_string(rand()) + "}"; + Value value(validData.begin(), validData.end()); + res.push_back({key, value}); + } + return res; +} + +/** + * @tc.name: QueryPreFixKey002 + * @tc.desc: The query method without filtering the field can query non-schma databases + * @tc.type: FUNC + * @tc.require: AR000EPARK + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, QueryPreFixKey002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create non-schma databases + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("QueryPreFixKey002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + vector entries = PreDataForQueryByPreFixKey(); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entries), OK); + + /** + * @tc.steps:step2. Get query object with prefixkey limit combination. + * @tc.expected: step2. Get results OK, and the entries size right. + */ + Query query = Query::Select().PrefixKey({'a', 'c'}); + std::vector entriesRes; + int errCode = g_kvNbDelegatePtr->GetEntries(query, entriesRes); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(entriesRes.size(), 5ul); + for (size_t i = 0; i < entriesRes.size(); i++) { + EXPECT_EQ(entriesRes[i].key.front(), 'a'); + EXPECT_EQ(entriesRes[i].key[1], 'c'); + } + int count = -1; + g_kvNbDelegatePtr->GetCount(query, count); + EXPECT_EQ(count, 5); + + Query query1 = Query::Select().PrefixKey({}).Limit(4, 0); + errCode = g_kvNbDelegatePtr->GetEntries(query1, entriesRes); + EXPECT_EQ(errCode, OK); + EXPECT_EQ(entriesRes.size(), 4ul); + + Query query2 = Query::Select().PrefixKey(Key(1025, 'a')); + errCode = g_kvNbDelegatePtr->GetEntries(query2, entriesRes); + EXPECT_EQ(errCode, INVALID_ARGS); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_TRUE(g_mgr.DeleteKvStore("QueryPreFixKey002") == OK); +} + +/** + * @tc.name: SingleVerGetSecurityOption001 + * @tc.desc: Test GetSecurityOption interface for the single ver database. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + * @tc.author: liuwenkai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerGetSecurityOption001, TestSize.Level1) +{ + SecurityOption savedOption; + std::shared_ptr adapter = std::make_shared(); + EXPECT_TRUE(adapter); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(adapter); + KvStoreNbDelegate::Option option = {true, false, false}; + + /** + * @tc.steps:step1. Create databases without securityOption. + * @tc.expected: step2. Returns a non-null kvstore but can not get SecurityOption. + */ + g_mgr.GetKvStore("SingleVerGetSecurityOption001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(g_kvNbDelegatePtr->GetSecurityOption(savedOption) == OK); + EXPECT_TRUE(savedOption.securityLabel == 0); + EXPECT_TRUE(savedOption.securityFlag == 0); + KvStoreNbDelegate *kvNbDelegatePtr1 = g_kvNbDelegatePtr; + + /** + * @tc.steps:step2. Create databases with new securityOption(Check ignore the new option). + * @tc.expected: step2. Returns non-null kvstore. + */ + option.secOption.securityLabel = S3; + option.secOption.securityFlag = 1; + g_mgr.GetKvStore("SingleVerGetSecurityOption001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(g_kvNbDelegatePtr->GetSecurityOption(savedOption) == OK); + EXPECT_TRUE(savedOption.securityLabel == 0); + EXPECT_TRUE(savedOption.securityFlag == 0); + + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr1), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore("SingleVerGetSecurityOption001") == OK); +} + +/** + * @tc.name: SingleVerGetSecurityOption002 + * @tc.desc: Test GetSecurityOption interface for the single ver database. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + * @tc.author: liuwenkai + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, SingleVerGetSecurityOption002, TestSize.Level1) +{ + SecurityOption savedOption; + std::shared_ptr adapter = std::make_shared(); + EXPECT_TRUE(adapter != nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(adapter); + KvStoreNbDelegate::Option option = {true, false, false}; + + /** + * @tc.steps:step1. Create databases with securityOption. + * @tc.expected: step2. Returns a non-null kvstore and get right SecurityOption. + */ + option.secOption.securityLabel = S3; + option.secOption.securityFlag = 1; + g_mgr.GetKvStore("SingleVerGetSecurityOption002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(g_kvNbDelegatePtr->GetSecurityOption(savedOption) == OK); + EXPECT_TRUE(savedOption.securityLabel == S3); + EXPECT_TRUE(savedOption.securityFlag == 1); + KvStoreNbDelegate *kvNbDelegatePtr1 = g_kvNbDelegatePtr; + + /** + * @tc.steps:step2. Create databases without securityOption. + * @tc.expected: step2. Returns a non-null kvstore and get right SecurityOption. + */ + option.secOption.securityLabel = 0; + option.secOption.securityFlag = 0; + g_mgr.GetKvStore("SingleVerGetSecurityOption002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(g_kvNbDelegatePtr->GetSecurityOption(savedOption) == OK); + EXPECT_TRUE(savedOption.securityLabel == S3); + EXPECT_TRUE(savedOption.securityFlag == 1); + + EXPECT_EQ(g_mgr.CloseKvStore(kvNbDelegatePtr1), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore("SingleVerGetSecurityOption002") == OK); +} + +/** + * @tc.name: MaxLogSize001 + * @tc.desc: Test the pragma cmd of the max log size limit. + * @tc.type: FUNC + * @tc.require: + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, MaxLogSize001, TestSize.Level2) +{ + /** + * @tc.steps:step1. Create database. + * @tc.expected: step1. Returns a non-null kvstore. + */ + KvStoreNbDelegate::Option option; + g_mgr.GetKvStore("MaxLogSize001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Setting the max log limit for the valid value. + * @tc.expected: step2. Returns OK. + */ + uint64_t logSize = DBConstant::MAX_LOG_SIZE_HIGH; + PragmaData pragLimit = static_cast(&logSize); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(SET_MAX_LOG_LIMIT, pragLimit), OK); + + logSize = DBConstant::MAX_LOG_SIZE_LOW; + pragLimit = static_cast(&logSize); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(SET_MAX_LOG_LIMIT, pragLimit), OK); + + logSize = 10 * 1024 * 1024; // 10M + pragLimit = static_cast(&logSize); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(SET_MAX_LOG_LIMIT, pragLimit), OK); + + /** + * @tc.steps:step3. Setting the max log limit for the invalid value. + * @tc.expected: step3. Returns INLIVAD_ARGS. + */ + logSize = DBConstant::MAX_LOG_SIZE_HIGH + 1; + pragLimit = static_cast(&logSize); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(SET_MAX_LOG_LIMIT, pragLimit), INVALID_ARGS); + + logSize = DBConstant::MAX_LOG_SIZE_LOW - 1; + pragLimit = static_cast(&logSize); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(SET_MAX_LOG_LIMIT, pragLimit), INVALID_ARGS); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore("MaxLogSize001") == OK); +} + +/** + * @tc.name: ForceCheckpoint002 + * @tc.desc: Test the checkpoint of the database. + * @tc.type: FUNC + * @tc.require: + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, MaxLogSize002, TestSize.Level2) +{ + /** + * @tc.steps:step1. Create database. + * @tc.expected: step1. Returns a non-null kvstore. + */ + KvStoreNbDelegate::Option option; + g_mgr.GetKvStore("MaxLogSize002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Put the random entry into the database. + * @tc.expected: step2. Returns OK. + */ + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key, 30); // for 30B random key + DistributedDBToolsUnitTest::GetRandomKeyValue(value, 3 * 1024 * 1024); // 3M value + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), OK); + DistributedDBToolsUnitTest::GetRandomKeyValue(key, 40); // for 40B random key + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), OK); + DistributedDBToolsUnitTest::GetRandomKeyValue(key, 20); // for 20B random key + DistributedDBToolsUnitTest::GetRandomKeyValue(value, 1 * 1024 * 1024); // 1M value + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), OK); + + /** + * @tc.steps:step3. Get the resultset. + * @tc.expected: step3. Returns OK. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(Key{}, resultSet), OK); + EXPECT_EQ(resultSet->GetCount(), 3); // size of all the entries is 3 + EXPECT_EQ(resultSet->MoveToFirst(), true); + + /** + * @tc.steps:step4. Put more data into the database. + * @tc.expected: step4. Returns OK. + */ + uint64_t logSize = 6 * 1024 * 1024; // 6M for initial test. + PragmaData pragLimit = static_cast(&logSize); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(SET_MAX_LOG_LIMIT, pragLimit), OK); + DistributedDBToolsUnitTest::GetRandomKeyValue(key, 10); // for 10B random key(different size) + DistributedDBToolsUnitTest::GetRandomKeyValue(value, 3 * 1024 * 1024); // 3MB + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), OK); + DistributedDBToolsUnitTest::GetRandomKeyValue(key, 15); // for 15B random key(different size) + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), OK); + + /** + * @tc.steps:step4. Put more data into the database while the log size is over the limit. + * @tc.expected: step4. Returns LOG_OVER_LIMITS. + */ + DistributedDBToolsUnitTest::GetRandomKeyValue(value, 25); // for 25B random key(different size) + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), LOG_OVER_LIMITS); + EXPECT_EQ(g_kvNbDelegatePtr->Delete(key), LOG_OVER_LIMITS); + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), LOG_OVER_LIMITS); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(key, value), LOG_OVER_LIMITS); + EXPECT_EQ(g_kvNbDelegatePtr->RemoveDeviceData("deviceA"), LOG_OVER_LIMITS); + /** + * @tc.steps:step4. Change the max log size limit, and put the data. + * @tc.expected: step4. Returns OK. + */ + logSize *= 10; // 10 multiple size + pragLimit = static_cast(&logSize); + EXPECT_EQ(g_kvNbDelegatePtr->Pragma(SET_MAX_LOG_LIMIT, pragLimit), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), OK); + g_kvNbDelegatePtr->CloseResultSet(resultSet); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("MaxLogSize002"), OK); +} + +/** + * @tc.name: MaxLogCheckPoint001 + * @tc.desc: Pragma the checkpoint command. + * @tc.type: FUNC + * @tc.require: + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, MaxLogCheckPoint001, TestSize.Level2) +{ + /** + * @tc.steps:step1. Create database. + * @tc.expected: step1. Returns a non-null kvstore. + */ + KvStoreNbDelegate::Option option; + g_mgr.GetKvStore("MaxLogCheckPoint001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Put the random entry into the database. + * @tc.expected: step2. Returns OK. + */ + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key, 30); // for 30B random key(different size) + DistributedDBToolsUnitTest::GetRandomKeyValue(value, 1 * 1024 * 1024); // 1M + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Delete(key), OK); + + /** + * @tc.steps:step3. Get the disk file size, execute the checkpoint and get the disk file size. + * @tc.expected: step3. Returns OK and the file size is less than the size before checkpoint. + */ + uint64_t sizeBeforeChk = 0; + g_mgr.GetKvStoreDiskSize("MaxLogCheckPoint001", sizeBeforeChk); + EXPECT_GT(sizeBeforeChk, 1 * 1024 * 1024ULL); // more than 1M + int param = 0; + PragmaData paraData = static_cast(¶m); + g_kvNbDelegatePtr->Pragma(EXEC_CHECKPOINT, paraData); + uint64_t sizeAfterChk = 0; + g_mgr.GetKvStoreDiskSize("MaxLogCheckPoint001", sizeAfterChk); + EXPECT_LT(sizeAfterChk, 100 * 1024ULL); // less than 100K + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("MaxLogCheckPoint001"), OK); +} + +/** + * @tc.name: CreateMemoryDbWithoutPath + * @tc.desc: Create memory database without path. + * @tc.type: FUNC + * @tc.require: AR000CRAKN + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, CreateMemoryDbWithoutPath, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create Memory database by GetKvStore without path. + * @tc.expected: step1. Create successfully. + */ + KvStoreDelegateManager mgr(APP_ID, USER_ID); + const KvStoreNbDelegate::Option option = {true, true}; + mgr.GetKvStore("memory_without_path", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); +} + +/** + * @tc.name: OpenStorePathCheckTest001 + * @tc.desc: Test open store with same label but different path. + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, OpenStorePathCheckTest001, TestSize.Level1) +{ + std::string dir1 = g_testDir + "/dbDir1"; + EXPECT_EQ(OS::MakeDBDirectory(dir1), E_OK); + std::string dir2 = g_testDir + "/dbDir2"; + EXPECT_EQ(OS::MakeDBDirectory(dir2), E_OK); + + KvStoreDelegateManager mgr1(APP_ID, USER_ID); + mgr1.SetKvStoreConfig({dir1}); + + KvStoreNbDelegate *delegate1 = nullptr; + auto callback1 = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(delegate1)); + + KvStoreNbDelegate::Option option; + mgr1.GetKvStore(STORE_ID_1, option, callback1); + EXPECT_EQ(g_kvDelegateStatus, OK); + ASSERT_NE(delegate1, nullptr); + + KvStoreNbDelegate *delegate2 = nullptr; + auto callback2 = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(delegate2)); + KvStoreDelegateManager mgr2(APP_ID, USER_ID); + mgr2.SetKvStoreConfig({dir2}); + mgr2.GetKvStore(STORE_ID_1, option, callback2); + EXPECT_EQ(g_kvDelegateStatus, INVALID_ARGS); + ASSERT_EQ(delegate2, nullptr); + + mgr1.CloseKvStore(delegate1); + mgr1.DeleteKvStore(STORE_ID_1); + mgr2.CloseKvStore(delegate2); + mgr2.DeleteKvStore(STORE_ID_1); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_publish_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_publish_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb6dd6a3a514af0edce8735f2905f8f83ff4cc5d --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_publish_test.cpp @@ -0,0 +1,792 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + const Key NULL_KEY; + const int OBSERVER_SLEEP_TIME = 100; + + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + string g_testDir; + KvStoreConfig g_config; + + // define the g_kvNbDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + + void KvStoreNbDelegateCallback(DBStatus statusSrc, KvStoreNbDelegate *kvStoreSrc, + DBStatus *statusDst, KvStoreNbDelegate **kvStoreDst) + { + *statusDst = statusSrc; + *kvStoreDst = kvStoreSrc; + } + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, &g_kvDelegateStatus, &g_kvNbDelegatePtr); + + // define parameters for conflict callback + bool g_isNeedInsertEntryInCallback = false; + int g_conflictCallbackTimes = 0; + Entry g_localEntry; + Entry g_syncEntry; + bool g_isLocalLastest = false; + bool g_isSyncNull = false; + void ResetCallbackArg(bool isLocalLastest = false) + { + g_conflictCallbackTimes = 0; + g_localEntry.key.clear(); + g_localEntry.value.clear(); + g_syncEntry.key.clear(); + g_syncEntry.value.clear(); + g_isLocalLastest = isLocalLastest; + g_isSyncNull = true; + g_isNeedInsertEntryInCallback = false; + } + + void ConflictCallback(const Entry &local, const Entry *sync, bool isLocalLastest) + { + g_conflictCallbackTimes++; + g_localEntry = local; + if (sync != nullptr) { + g_syncEntry = *sync; + g_isSyncNull = false; + } else { + g_isSyncNull = true; + } + g_isLocalLastest = isLocalLastest; + if (g_isNeedInsertEntryInCallback) { + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + } + } +} + +class DistributedDBInterfacesNBPublishTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesNBPublishTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesNBPublishTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesNBPublishTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvNbDelegatePtr = nullptr; +} + +void DistributedDBInterfacesNBPublishTest::TearDown(void) {} + +/** + * @tc.name: SingleVerPublishKey001 + * @tc.desc: Publish nonexistent key + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey001, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_2), OK); + /** + * @tc.steps:step1. PublishLocal key2. + * @tc.expected: step1. return NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_2, true, true, nullptr), NOT_FOUND); + /** + * @tc.steps:step2. Get value of key1 and key2 both from local and sync table + * @tc.expected: step2. value of key1 and key2 are correct + */ + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_2, readValue), NOT_FOUND); + /** + * @tc.steps:step3. PublishLocal key2. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_2, true, false, nullptr), NOT_FOUND); + /** + * @tc.steps:step4. Get value of key1 and key2 both from local and sync table + * @tc.expected: step4. value of key1 and key2 are correct + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_2, readValue), NOT_FOUND); + /** + * @tc.steps:step5. PublishLocal key2. + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_2, false, true, nullptr), NOT_FOUND); + /** + * @tc.steps:step6. Get value of key1 and key2 both from local and sync table + * @tc.expected: step6. value of key1 and key2 are correct + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_2, readValue), NOT_FOUND); + /** + * @tc.steps:step7. PublishLocal key2. + * @tc.expected: step7. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_2, false, false, nullptr), NOT_FOUND); + /** + * @tc.steps:step8. Get value of key1 and key2 both from local and sync table + * @tc.expected: step8. value of key1 and key2 are correct + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_2, readValue), NOT_FOUND); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey002 + * @tc.desc: Publish no conflict key without deleting key from local table + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey002, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_2, VALUE_2), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_3), OK); + + KvStoreObserverUnitTest *observerLocal = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observerLocal != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_LOCAL_ONLY, observerLocal), OK); + + KvStoreObserverUnitTest *observerSync = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observerSync != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_NATIVE, observerSync), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, false, true, nullptr), OK); + /** + * @tc.steps:step2. Get value of key1 from local table + * @tc.expected: step2. value of key1 is value1 + */ + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step3. Get value of key2 from local table + * @tc.expected: step3. value of key2 is value2 + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + /** + * @tc.steps:step4. Get value of key1 from sync table + * @tc.expected: step4. value of key1 is value1 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step5. Get value of key2 from sync table + * @tc.expected: step5. value of key2 is value3 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_3); + /** + * @tc.steps:step6. Check observer data. + * @tc.expected: step6. return OK. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + vector entriesRet = {{KEY_1, VALUE_1}}; + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entriesRet, observerSync->GetEntriesInserted())); + EXPECT_EQ(observerLocal->GetCallCount(), 0UL); + + // finilize + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observerLocal), OK); + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observerSync), OK); + delete observerLocal; + observerLocal = nullptr; + delete observerSync; + observerSync = nullptr; + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey003 + * @tc.desc: Publish no conflict key with deleting key from local table + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey003, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_2, VALUE_2), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_3), OK); + + KvStoreObserverUnitTest *observerLocal = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observerLocal != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_LOCAL_ONLY, observerLocal), OK); + + KvStoreObserverUnitTest *observerSync = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observerSync != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_NATIVE, observerSync), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, true, true, nullptr), OK); + /** + * @tc.steps:step2. Get value of key1 from local table + * @tc.expected: step2. value of key1 is value1 + */ + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), NOT_FOUND); + /** + * @tc.steps:step3. Get value of key2 from local table + * @tc.expected: step3. value of key2 is value2 + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + /** + * @tc.steps:step4. Get value of key1 from sync table + * @tc.expected: step4. value of key1 is value1 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step5. Get value of key2 from sync table + * @tc.expected: step5. value of key2 is value3 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_3); + /** + * @tc.steps:step6. Check observer data. + * @tc.expected: step6. return OK. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + vector entriesRet = {{KEY_1, VALUE_1}}; + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entriesRet, observerSync->GetEntriesInserted())); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entriesRet, observerLocal->GetEntriesDeleted())); + + // finilize + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observerLocal), OK); + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observerSync), OK); + delete observerLocal; + observerLocal = nullptr; + delete observerSync; + observerSync = nullptr; + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey003"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey004 + * @tc.desc: Publish conflict key and update timestamp + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey004, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey004", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_2, VALUE_2), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_3), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_4), OK); + + KvStoreObserverUnitTest *observerLocal = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observerLocal != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_LOCAL_ONLY, observerLocal), OK); + + KvStoreObserverUnitTest *observerSync = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observerSync != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_NATIVE, observerSync), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, false, true, nullptr), OK); + /** + * @tc.steps:step2. Get value of key1 from local table + * @tc.expected: step2. value of key1 is value1 + */ + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step3. Get value of key2 from local table + * @tc.expected: step3. value of key2 is value2 + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + /** + * @tc.steps:step4. Get value of key1 from sync table + * @tc.expected: step4. value of key1 is value1 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step5. Get value of key2 from sync table + * @tc.expected: step5. value of key2 is value3 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_3); + /** + * @tc.steps:step6. Check observer data. + * @tc.expected: step6. return OK. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + vector entriesRet = {{KEY_1, VALUE_1}}; + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entriesRet, observerSync->GetEntriesUpdated())); + EXPECT_EQ(observerLocal->GetCallCount(), 0UL); + + // finilize + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observerLocal), OK); + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observerSync), OK); + delete observerLocal; + observerLocal = nullptr; + delete observerSync; + observerSync = nullptr; + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey004"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey005 + * @tc.desc: Publish conflict key but do not update timestamp + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey005, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey005", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_2, VALUE_2), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_3), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_4), OK); + + KvStoreObserverUnitTest *observerLocal = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observerLocal != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_LOCAL_ONLY, observerLocal), OK); + + KvStoreObserverUnitTest *observerSync = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observerSync != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_NATIVE, observerSync), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, true, false, nullptr), STALE); + /** + * @tc.steps:step2. Get value of key1 from local table + * @tc.expected: step2. value of key1 is value1 + */ + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step3. Get value of key2 from local table + * @tc.expected: step3. value of key2 is value2 + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + /** + * @tc.steps:step4. Get value of key1 from sync table + * @tc.expected: step4. value of key1 is value4 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_4); + /** + * @tc.steps:step5. Get value of key2 from sync table + * @tc.expected: step5. value of key2 is value3 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, readValue), OK); + EXPECT_EQ(readValue, VALUE_3); + /** + * @tc.steps:step6. Check observer data. + * @tc.expected: step6. return OK. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(observerSync->GetCallCount(), 0UL); + EXPECT_EQ(observerLocal->GetCallCount(), 0UL); + + // finilize + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observerLocal), OK); + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observerSync), OK); + delete observerLocal; + observerLocal = nullptr; + delete observerSync; + observerSync = nullptr; + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey005"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey006 + * @tc.desc: Publish no conflict key and onConflict() not null + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey006, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey006", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + ResetCallbackArg(); + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, true, false, ConflictCallback), OK); + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), NOT_FOUND); + /** + * @tc.steps:step2. Check onConflict callback. + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_conflictCallbackTimes, 0); + /** + * @tc.steps:step3. Get value of key1 from sync table + * @tc.expected: step3. value of key1 is value4 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey006"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey007 + * @tc.desc: Publish conflict key and onConflict() not null + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey007, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey007", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + ResetCallbackArg(true); + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, true, false, ConflictCallback), OK); + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step2. Check onConflict callback. + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_conflictCallbackTimes, 1); + EXPECT_EQ(g_isLocalLastest, false); + Entry entryRet = {KEY_1, VALUE_1}; + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntryEqual(g_localEntry, entryRet)); + entryRet = {KEY_1, VALUE_2}; + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntryEqual(g_syncEntry, entryRet)); + /** + * @tc.steps:step3. Get value of key1 from sync table + * @tc.expected: step3. value of key1 is value4 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey007"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey008 + * @tc.desc: Publish conflict key and onConflict() not null, need update timestamp + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey008, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey008", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + ResetCallbackArg(false); + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, true, true, ConflictCallback), OK); + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step2. Check onConflict callback. + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_conflictCallbackTimes, 1); + EXPECT_EQ(g_isLocalLastest, true); + Entry entryRet = {KEY_1, VALUE_1}; + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntryEqual(g_localEntry, entryRet)); + entryRet = {KEY_1, VALUE_2}; + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntryEqual(g_syncEntry, entryRet)); + /** + * @tc.steps:step3. Get value of key1 from sync table + * @tc.expected: step3. value of key1 is value4 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_2); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey008"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey009 + * @tc.desc: Publish conflict key (deleted) and onConflict() not null + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey009, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey009", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_1), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + ResetCallbackArg(true); + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, true, false, ConflictCallback), OK); + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step2. Check onConflict callback. + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_conflictCallbackTimes, 1); + EXPECT_EQ(g_isLocalLastest, false); + EXPECT_EQ(g_isSyncNull, true); + Entry entryRet = {KEY_1, VALUE_1}; + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntryEqual(g_localEntry, entryRet)); + /** + * @tc.steps:step3. Get value of key1 from sync table + * @tc.expected: step3. value of key1 is value4 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), NOT_FOUND); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey009"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey010 + * @tc.desc: Publish conflict key (deleted) and onConflict() not null, need update timestamp + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey010, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey010", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_1), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + ResetCallbackArg(false); + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, true, true, ConflictCallback), OK); + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step2. Check onConflict callback. + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_conflictCallbackTimes, 1); + EXPECT_EQ(g_isLocalLastest, true); + EXPECT_EQ(g_isSyncNull, true); + Entry entryRet = {KEY_1, VALUE_1}; + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntryEqual(g_localEntry, entryRet)); + /** + * @tc.steps:step3. Get value of key1 from sync table + * @tc.expected: step3. value of key1 is value4 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), NOT_FOUND); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey010"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SingleVerPublishKey011 + * @tc.desc: Publish conflict key (deleted) and onConflict() not null, put(k1,v1) in callback method + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBPublishTest, SingleVerPublishKey011, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_publish_SingleVerPublishKey011", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_1), OK); + /** + * @tc.steps:step1. PublishLocal key1. + * @tc.expected: step1. return OK. + */ + ResetCallbackArg(true); + g_isNeedInsertEntryInCallback = true; + EXPECT_EQ(g_kvNbDelegatePtr->PublishLocal(KEY_1, true, false, ConflictCallback), OK); + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + /** + * @tc.steps:step2. Check onConflict callback. + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_conflictCallbackTimes, 1); + EXPECT_EQ(g_isLocalLastest, false); + EXPECT_EQ(g_isSyncNull, true); + Entry entryRet = {KEY_1, VALUE_1}; + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntryEqual(g_localEntry, entryRet)); + /** + * @tc.steps:step3. Get value of key1 from sync table + * @tc.expected: step3. value of key1 is value4 + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_EQ(readValue, VALUE_1); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_publish_SingleVerPublishKey011"), OK); + g_kvNbDelegatePtr = nullptr; +} diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_transaction_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_transaction_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71ae88466c43691ef496618d330ff24ef91794c3 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_transaction_test.cpp @@ -0,0 +1,1104 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_common.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + const int BATCH_BASE_SIZE = 60; + const Key NULL_KEY; + const Key NULL_VALUE; + const int CONFLICT_ALL = 15; + const auto OLD_VALUE_TYPE = KvStoreNbConflictData::ValueType::OLD_VALUE; + const auto NEW_VALUE_TYPE = KvStoreNbConflictData::ValueType::NEW_VALUE; + + const int OBSERVER_SLEEP_TIME = 30; + + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + string g_testDir; + KvStoreConfig g_config; + + // define the g_kvNbDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + + struct SingleVerConflictData { + KvStoreNbConflictType type = CONFLICT_NATIVE_ALL; + Key key; + Value oldValue; + Value newValue; + bool oldIsDeleted = false; + bool newIsDeleted = false; + bool oldIsNative = false; + bool newIsNative = false; + int getoldValueErrCode = 0; + int getNewValueErrCode = 0; + bool operator==(const SingleVerConflictData &comparedData) const + { + if (this->type == comparedData.type && + this->key == comparedData.key && + this->oldValue == comparedData.oldValue && + this->newValue == comparedData.newValue && + this->oldIsDeleted == comparedData.oldIsDeleted && + this->newIsDeleted == comparedData.newIsDeleted && + this->oldIsNative == comparedData.oldIsNative && + this->newIsNative == comparedData.newIsNative && + this->getoldValueErrCode == comparedData.getoldValueErrCode && + this->getNewValueErrCode == comparedData.getNewValueErrCode) { + return true; + } + LOGD("type = %d, ctype = %d", this->type, comparedData.type); + DBCommon::PrintHexVector(this->key, __LINE__, "key"); + DBCommon::PrintHexVector(comparedData.key, __LINE__, "ckey"); + DBCommon::PrintHexVector(this->oldValue, __LINE__, "oldValue"); + DBCommon::PrintHexVector(comparedData.oldValue, __LINE__, "coldValue"); + DBCommon::PrintHexVector(this->newValue, __LINE__, "newValue"); + DBCommon::PrintHexVector(comparedData.newValue, __LINE__, "cnewValue"); + + LOGD("oldIsDeleted = %d, coldIsDeleted = %d", this->oldIsDeleted, comparedData.oldIsDeleted); + LOGD("newIsDeleted = %d, cnewIsDeleted = %d", this->newIsDeleted, comparedData.newIsDeleted); + LOGD("oldIsNative = %d, coldIsNative = %d", this->oldIsNative, comparedData.oldIsNative); + LOGD("newIsNative = %d, cnewIsNative = %d", this->newIsNative, comparedData.newIsNative); + LOGD("getoldValueErrCode = %d, cgetoldValueErrCode = %d", this->getoldValueErrCode, + comparedData.getoldValueErrCode); + LOGD("getNewValueErrCode = %d, cgetNewValueErrCode = %d", this->getNewValueErrCode, + comparedData.getNewValueErrCode); + + return false; + } + }; + std::vector g_conflictData; + + void KvStoreNbDelegateCallback(DBStatus statusSrc, KvStoreNbDelegate *kvStoreSrc, + DBStatus *statusDst, KvStoreNbDelegate **kvStoreDst) + { + *statusDst = statusSrc; + *kvStoreDst = kvStoreSrc; + } + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, &g_kvDelegateStatus, &g_kvNbDelegatePtr); + + void NotifierCallback(const KvStoreNbConflictData &data) + { + Key key; + Value oldValue; + Value newValue; + data.GetKey(key); + data.GetValue(OLD_VALUE_TYPE, oldValue); + LOGD("Get new value status:%d", data.GetValue(NEW_VALUE_TYPE, newValue)); + LOGD("Type:%d", data.GetType()); + DBCommon::PrintHexVector(oldValue, __LINE__); + DBCommon::PrintHexVector(newValue, __LINE__); + LOGD("Type:IsDeleted %d vs %d, IsNative %d vs %d", data.IsDeleted(OLD_VALUE_TYPE), + data.IsDeleted(NEW_VALUE_TYPE), data.IsNative(OLD_VALUE_TYPE), data.IsNative(NEW_VALUE_TYPE)); + g_conflictData.push_back({data.GetType(), key, oldValue, newValue, data.IsDeleted(OLD_VALUE_TYPE), + data.IsDeleted(NEW_VALUE_TYPE), data.IsNative(OLD_VALUE_TYPE), data.IsNative(NEW_VALUE_TYPE), + data.GetValue(OLD_VALUE_TYPE, oldValue), data.GetValue(NEW_VALUE_TYPE, newValue)}); + } +} + +class DistributedDBInterfacesNBTransactionTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesNBTransactionTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + g_mgr.SetProcessLabel("DistributedDBInterfacesNBTransactionTest", "test"); +} + +void DistributedDBInterfacesNBTransactionTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesNBTransactionTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvNbDelegatePtr = nullptr; +} + +void DistributedDBInterfacesNBTransactionTest::TearDown(void) {} + +/** + * @tc.name: start001 + * @tc.desc: Test the nb transaction start twice. + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, start001, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_start001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + /** + * @tc.steps:step2. Start transaction again. + * @tc.expected: step2. return DB_ERROR. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), DB_ERROR); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_start001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: start002 + * @tc.desc: Test the nb transaction begin and end not match. + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, start002, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_start002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Rollback transaction. + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + /** + * @tc.steps:step3. Commit transaction. + * @tc.expected: step3. return DB_ERROR. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), DB_ERROR); + /** + * @tc.steps:step4. Start transaction. + * @tc.expected: step4. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step5. Commit transaction. + * @tc.expected: step5. return DB_ERROR. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + /** + * @tc.steps:step6. Rollback transaction. + * @tc.expected: step6. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), DB_ERROR); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_start002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: start003 + * @tc.desc: Test the nb transaction rollback automatically when db close. + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, start003, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_start003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + /** + * @tc.steps:step2. Put (key1,value2) + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps:step3. Close db + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + /** + * @tc.steps:step4. Open db again + * @tc.expected: step4. return OK. + */ + g_mgr.GetKvStore("distributed_nb_transaction_start003", option, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + /** + * @tc.steps:step5. Get key1 + * @tc.expected: step5. return OK, value of key1 is value1. + */ + Value value; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, value), OK); + EXPECT_EQ(value, VALUE_1); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_start003"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: start004 + * @tc.desc: Test the nb operations return BUSY after transaction started. + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, start004, TestSize.Level4) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_start004", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Local data and sync data can be simultaneously operated in transactions. + * @tc.expected: step2. From September 2020 return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY_3, VALUE_3), OK); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocal(KEY_1), OK); + + CipherPassword password; + EXPECT_EQ(g_kvNbDelegatePtr->Rekey(password), BUSY); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(KEY_3, OBSERVER_CHANGES_NATIVE, observer), BUSY); + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), NOT_FOUND); + delete observer; + observer = nullptr; + + std::string filePath = g_testDir + "test.txt"; + EXPECT_EQ(g_kvNbDelegatePtr->Export(filePath, password), BUSY); + + KvStoreResultSet *readResultSet = nullptr; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(KEY_4, readResultSet), BUSY); + EXPECT_EQ(g_kvNbDelegatePtr->CloseResultSet(readResultSet), INVALID_ARGS); + /** + * @tc.steps:step1. Commit transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_start004"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: commit001 + * @tc.desc: Test the nb transaction commit without start. + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, commit001, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_commit001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps:step1. Commit transaction. + * @tc.expected: step1. return DB_ERROR. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), DB_ERROR); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_commit001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: commit002 + * @tc.desc: Test the nb transaction commit twice. + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, commit002, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_commit002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Get key1 + * @tc.expected: step2. return OK, value of key1 is value1. + */ + Value value1; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, value1), OK); + EXPECT_EQ(value1, VALUE_1); + /** + * @tc.steps:step3. Put (key2,value2) + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_2), OK); + /** + * @tc.steps:step4. Get key2 + * @tc.expected: step4. return OK, value of key2 is value2. + */ + Value value2; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_2, value2), OK); + EXPECT_EQ(value2, VALUE_2); + /** + * @tc.steps:step5. Commit transaction. + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + /** + * @tc.steps:step6. Commit transaction again. + * @tc.expected: step6. return DB_ERROR. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), DB_ERROR); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_commit002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: commit003 + * @tc.desc: Test the entry size exceed the maximum limit in one transaction + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, commit003, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_commit003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Put (key1,value1) + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps:step3. Delete key1 + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_1), OK); + /** + * @tc.steps:step4. PutBatch 64 records (from key2 to key65) + * @tc.expected: step4. return OK. + */ + vector entrysBase; + vector keysBase; + DistributedDBUnitTest::GenerateRecords(BATCH_BASE_SIZE + 5, entrysBase, keysBase); + + vector entrys1(entrysBase.begin() + 1, entrysBase.end()); + EXPECT_EQ(entrys1.size(), 64UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys1), OK); + /** + * @tc.steps:step5. DeleteBatch 63 records (from key2 to key64) + * @tc.expected: step5. return OK. + */ + vector keys1(keysBase.begin() + 1, keysBase.end() - 1); + EXPECT_EQ(keys1.size(), 63UL); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys1), OVER_MAX_LIMITS); + /** + * @tc.steps:step6. DeleteBatch 60 records (from key1 to key60) + * @tc.expected: step6. return OK. + */ + vector keys2(keysBase.begin(), keysBase.begin() + 60); + EXPECT_EQ(keys2.size(), 60UL); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys2), OK); + /** + * @tc.steps:step6. Commit. + * @tc.expected: step6. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + /** + * @tc.steps:step7. GetEntries. + * @tc.expected: step7. return OK. + */ + vector entriesExpect(entrysBase.begin() + 60, entrysBase.end()); + EXPECT_EQ(entriesExpect.size(), 5UL); + const Key prefix; + vector entriesRet; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(prefix, entriesRet), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(entriesExpect, entriesRet)); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_commit003"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: commit004 + * @tc.desc: Test the nb normal operations in one transaction + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, commit004, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_commit004", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_NATIVE, observer), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Put (key1,value1) and (key2,value2) + * @tc.expected: step2. put OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_2), OK); + /** + * @tc.steps:step3. Delete key2 + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_2), OK); + /** + * @tc.steps:step4. PutBatch 65 records (from key3 to key67) + * @tc.expected: step4. return OK. + */ + vector entrysBase; + vector keysBase; + DistributedDBUnitTest::GenerateRecords(BATCH_BASE_SIZE + 7, entrysBase, keysBase); + + vector entrys1(entrysBase.begin() + 2, entrysBase.end()); + EXPECT_EQ(entrys1.size(), 65UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys1), OK); + /** + * @tc.steps:step5. DeleteBatch 60 records (from key8 to key67) + * @tc.expected: step5. return OK. + */ + vector keys(keysBase.begin() + 7, keysBase.end()); + EXPECT_EQ(keys.size(), 60UL); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys), OK); + /** + * @tc.steps:step6. Commit. + * @tc.expected: step6. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + /** + * @tc.steps:step7. Check observer data. + * @tc.expected: step6. return OK. + */ + vector entriesRet; + Entry entry1 = {KEY_1, VALUE_1}; + entriesRet.push_back(entry1); + entriesRet.insert(entriesRet.end(), entrysBase.begin() + 2, entrysBase.begin() + 7); + EXPECT_EQ(entriesRet.size(), 6UL); + + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entriesRet, observer->GetEntriesInserted())); + /** + * @tc.steps:step8. GetEntries. + * @tc.expected: step8. return OK. + */ + const Key prefix; + vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(prefix, entries), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(entriesRet, entries, true)); + + // finilize + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_commit004"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: commit005 + * @tc.desc: Test the conflict data report normally in one transaction + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, commit005, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_commit005", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(g_kvNbDelegatePtr->SetConflictNotifier(CONFLICT_ALL, NotifierCallback) == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_2), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Put (key1,value3) and (key2,value4) + * @tc.expected: step2. put OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_3), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_4), OK); + /** + * @tc.steps:step3. Delete key2 + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_2), OK); + /** + * @tc.steps:step4. put (key3 ,value5) (key3 ,value6) + * @tc.expected: step4. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_3, VALUE_5), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_3, VALUE_6), OK); + /** + * @tc.steps:step5. Commit. + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + /** + * @tc.steps:step6. Check conflict report data. + * @tc.expected: step6. return OK. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_conflictData.size(), 2UL); + if (g_conflictData.size() == 2) { + SingleVerConflictData expectNotifyData1 = {KvStoreNbConflictType::CONFLICT_NATIVE_ALL, + KEY_1, VALUE_1, VALUE_3, false, false, true, true, OK, OK}; + EXPECT_EQ(g_conflictData[0], expectNotifyData1); + + SingleVerConflictData expectNotifyData2 = {KvStoreNbConflictType::CONFLICT_NATIVE_ALL, + KEY_2, VALUE_2, NULL_VALUE, false, true, true, true, OK, DB_ERROR}; + EXPECT_EQ(g_conflictData[1], expectNotifyData2); + } + + // finilize + g_conflictData.clear(); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_commit005"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: commit006 + * @tc.desc: Test the conflict data report and observer function both be normal in one transaction + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, commit006, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_commit006", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_1), OK); + + EXPECT_TRUE(g_kvNbDelegatePtr->SetConflictNotifier(CONFLICT_ALL, NotifierCallback) == OK); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(observer != nullptr); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(NULL_KEY, OBSERVER_CHANGES_NATIVE, observer), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Put (key1,value2) + * @tc.expected: step2. put OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps:step3. Commit. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + /** + * @tc.steps:step4. Get value of key1. + * @tc.expected: step4. return OK, value of key1 is value2. + */ + Value readValue; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY_1, readValue), OK); + EXPECT_TRUE(readValue == VALUE_2); + /** + * @tc.steps:step5. Check observer data. + * @tc.expected: step5. return OK. + */ + vector entriesRet = {{KEY_1, VALUE_2}}; + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entriesRet, observer->GetEntriesInserted())); + /** + * @tc.steps:step6. Check conflict report data. + * @tc.expected: step6. return OK. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_conflictData.size(), 1UL); + if (g_conflictData.size() == 1) { + SingleVerConflictData expectNotifyData = {KvStoreNbConflictType::CONFLICT_NATIVE_ALL, + KEY_1, {}, VALUE_2, true, false, true, true, DB_ERROR, OK}; + EXPECT_EQ(g_conflictData[0], expectNotifyData); + } + + // finilize + g_conflictData.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(observer), OK); + delete observer; + observer = nullptr; + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_commit006"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: rollback001 + * @tc.desc: Test the transaction rollback without start. + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, rollback001, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_rollback001", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps:step1. Transaction rollback without start. + * @tc.expected: step1. return DB_ERROR. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), DB_ERROR); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_rollback001"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: rollback002 + * @tc.desc: Test the transaction rollback twice + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, rollback002, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_rollback002", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step1. Rollback. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + /** + * @tc.steps:step1. Transaction rollback without start. + * @tc.expected: step1. return DB_ERROR. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), DB_ERROR); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_rollback002"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: rollback003 + * @tc.desc: Test the Put operation rollback + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, rollback003, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_rollback003", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Put (key2,value2) + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_2), OK); + /** + * @tc.steps:step3. Rollback. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + /** + * @tc.steps:step4. GetEntries. + * @tc.expected: step4. return OK. + */ + const Key prefix; + vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(prefix, entries), OK); + EXPECT_EQ(entries.size(), 1UL); + if (entries.size() > 0) { + EXPECT_EQ(entries[0].key, KEY_1); + EXPECT_EQ(entries[0].value, VALUE_1); + } + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_rollback003"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: rollback004 + * @tc.desc: Test the PutBatch operation rollback + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, rollback004, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_rollback004", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. PutBatch 10 records + * @tc.expected: step2. return OK. + */ + vector entrysBase; + vector keysBase; + DistributedDBUnitTest::GenerateRecords(10, entrysBase, keysBase); + + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrysBase), OK); + /** + * @tc.steps:step3. Rollback. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + /** + * @tc.steps:step4. GetEntries. + * @tc.expected: step4. return OK. + */ + const Key prefix; + vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(prefix, entries), OK); + EXPECT_EQ(entries.size(), 1UL); + if (entries.size() > 0) { + EXPECT_EQ(entries[0].key, KEY_1); + EXPECT_EQ(entries[0].value, VALUE_1); + } + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_rollback004"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: rollback005 + * @tc.desc: Test the modify operation rollback + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, rollback005, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_rollback005", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Put (key1,value2) + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps:step3. Rollback. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + /** + * @tc.steps:step4. GetEntries. + * @tc.expected: step4. return OK. + */ + const Key prefix; + vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(prefix, entries), OK); + EXPECT_EQ(entries.size(), 1UL); + if (entries.size() > 0) { + EXPECT_EQ(entries[0].key, KEY_1); + EXPECT_EQ(entries[0].value, VALUE_1); + } + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_rollback005"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: rollback006 + * @tc.desc: Test the Delete operation rollback + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, rollback006, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_rollback006", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Delete key1 + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_1), OK); + /** + * @tc.steps:step3. Rollback. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + /** + * @tc.steps:step4. GetEntries. + * @tc.expected: step4. return OK. + */ + const Key prefix; + vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(prefix, entries), OK); + EXPECT_EQ(entries.size(), 1UL); + if (entries.size() > 0) { + EXPECT_EQ(entries[0].key, KEY_1); + EXPECT_EQ(entries[0].value, VALUE_1); + } + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_rollback006"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: rollback007 + * @tc.desc: Test the DeleteBatch operation rollback + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, rollback007, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_rollback007", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + vector entries; + vector keys; + DistributedDBUnitTest::GenerateRecords(10, entries, keys); + + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entries), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. DeleteBatch from key1 to key10 + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys), OK); + /** + * @tc.steps:step3. Rollback. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + /** + * @tc.steps:step4. GetEntries. + * @tc.expected: step4. return OK. + */ + const Key prefix; + vector entriesRet; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(prefix, entriesRet), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(entries, entriesRet)); + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_rollback007"), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: rollback008 + * @tc.desc: Test the multiple operations rollback + * @tc.type: FUNC + * @tc.require: AR000DPTQ9 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBInterfacesNBTransactionTest, rollback008, TestSize.Level1) +{ + const KvStoreNbDelegate::Option option = {true, false}; + g_mgr.GetKvStore("distributed_nb_transaction_rollback008", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_2, VALUE_2), OK); + /** + * @tc.steps:step1. Start transaction. + * @tc.expected: step1. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + /** + * @tc.steps:step2. Put (key3,value3) (key1,value4) + * @tc.expected: step2. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_3, VALUE_3), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_4), OK); + /** + * @tc.steps:step3. Delete key2 + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY_2), OK); + /** + * @tc.steps:step4. PutBatch 10 records (from key3 to key12) + * @tc.expected: step4. return OK. + */ + vector entrysBase; + vector keysBase; + DistributedDBUnitTest::GenerateRecords(12, entrysBase, keysBase); + + vector entrys1(entrysBase.begin() + 2, entrysBase.end()); + EXPECT_EQ(entrys1.size(), 10UL); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entrys1), OK); + /** + * @tc.steps:step5. DeleteBatch 5 records (from key3 to key7) + * @tc.expected: step5. return OK. + */ + vector keys(keysBase.begin() + 2, keysBase.begin() + 7); + EXPECT_EQ(keys.size(), 5UL); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys), OK); + /** + * @tc.steps:step6. Commit. + * @tc.expected: step6. return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + /** + * @tc.steps:step7. GetEntries. + * @tc.expected: step7. return OK. + */ + const Key prefix; + vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(prefix, entries), OK); + EXPECT_EQ(entries.size(), 2UL); + if (entries.size() > 1) { + EXPECT_EQ(entries[0].key, KEY_1); + EXPECT_EQ(entries[0].value, VALUE_1); + EXPECT_EQ(entries[1].key, KEY_2); + EXPECT_EQ(entries[1].value, VALUE_2); + } + + // finilize + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("distributed_nb_transaction_rollback008"), OK); + g_kvNbDelegatePtr = nullptr; +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_unpublish_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_unpublish_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5958850d9377d7ec816783e8af90876d4888624c --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_unpublish_test.cpp @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr("app0", "user0"); + string g_testDir; + KvStoreConfig g_config; + KvStoreObserverUnitTest *g_syncObserver = nullptr; + KvStoreObserverUnitTest *g_localObserver = nullptr; + Entry g_entry1; + Entry g_entry2; + + // define the g_kvNbDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + + const int OBSERVER_SLEEP_TIME = 100; + + void KvStoreNbDelegateCallback( + DBStatus statusSrc, KvStoreNbDelegate *kvStoreSrc, DBStatus &statusDst, KvStoreNbDelegate *&kvStoreDst) + { + statusDst = statusSrc; + kvStoreDst = kvStoreSrc; + } + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + + static void CheckUnpublishNotFound() + { + Value valueRead; + EXPECT_EQ(g_kvNbDelegatePtr->Get(g_entry1.key, valueRead), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->Get(g_entry2.key, valueRead), OK); + EXPECT_EQ(g_entry2.value, valueRead); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry1.key, valueRead), OK); + EXPECT_EQ(g_entry1.value, valueRead); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry2.key, valueRead), NOT_FOUND); + } + + static void RegisterObservers() + { + g_localObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(g_localObserver != nullptr); + g_syncObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(g_syncObserver != nullptr); + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, 3, g_syncObserver), OK); // sync data observer. + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, 4, g_localObserver), OK); // local data observer. + } +} + +class DistributedDBInterfacesNBUnpublishTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesNBUnpublishTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesNBUnpublishTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesNBUnpublishTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("unpublish_test", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entry1.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entry1.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entry2.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(g_entry2.value); +} + +void DistributedDBInterfacesNBUnpublishTest::TearDown(void) +{ + if (g_localObserver != nullptr) { + if (g_kvNbDelegatePtr != nullptr) { + g_kvNbDelegatePtr->UnRegisterObserver(g_localObserver); + } + delete g_localObserver; + g_localObserver = nullptr; + } + + if (g_syncObserver != nullptr) { + if (g_kvNbDelegatePtr != nullptr) { + g_kvNbDelegatePtr->UnRegisterObserver(g_syncObserver); + } + delete g_syncObserver; + g_syncObserver = nullptr; + } + if (g_kvNbDelegatePtr != nullptr) { + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_kvNbDelegatePtr = nullptr; + } + g_mgr.DeleteKvStore("unpublish_test"); +} + +/** + * @tc.name: CombineTest001 + * @tc.desc: Test unpublish one nonexistent data. + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBUnpublishTest, SingleVerUnPublishKey001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Put [k1, v1] into the local zone, [k2, v2] into the sync zone. + * @tc.expected: step1. Get results OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(g_entry1.key, g_entry1.value), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(g_entry2.key, g_entry2.value), OK); + + /** + * @tc.steps:step2. Unpublish the k1 with para of deletePublic: true, updateTimestamp: true. + * @tc.expected: step2. Unpublish returns NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, true, true), NOT_FOUND); + + /** + * @tc.steps:step3. Check the data in the local and sync zone. + * @tc.expected: step3. Both the data are not changed. + */ + CheckUnpublishNotFound(); + + /** + * @tc.steps:step4. Unpublish the k1 with para of deletePublic: true, updateTimestamp: false. + * @tc.expected: step4. Unpublish returns NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, true, false), NOT_FOUND); + + /** + * @tc.steps:step5. Check the data in the local and sync zone. + * @tc.expected: step5. Both the data are not changed. + */ + CheckUnpublishNotFound(); + + /** + * @tc.steps:step6. Unpublish the k1 with para of deletePublic: false, updateTimestamp: true. + * @tc.expected: step6. Unpublish returns NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, false, true), NOT_FOUND); + + /** + * @tc.steps:step7. Check the data in the local and sync zone. + * @tc.expected: step7. Both the data are not changed. + */ + CheckUnpublishNotFound(); + + /** + * @tc.steps:step8. Unpublish the k1 with para of deletePublic: false, updateTimestamp: false. + * @tc.expected: step8. Unpublish returns NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, false, false), NOT_FOUND); + + /** + * @tc.steps:step9. Check the data in the local and sync zone. + * @tc.expected: step9. Both the data are not changed. + */ + CheckUnpublishNotFound(); +} + +/** + * @tc.name: SingleVerUnPublishKey002 + * @tc.desc: Test unpublish existent data(no conflict with the local data). + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBUnpublishTest, SingleVerUnPublishKey002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Put [k1, v1], [k2, v2] into the sync zone. + * @tc.expected: step1. Put returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(g_entry1.key, g_entry1.value), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(g_entry2.key, g_entry2.value), OK); + + /** + * @tc.steps:step2. Register the obsevers for the sync data and the local data. + */ + RegisterObservers(); + + /** + * @tc.steps:step3. Unpublish the k1 with para of deletePublic: false, updateTimestamp: false. + * @tc.expected: step3. Unpublish returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, false, false), OK); + + /** + * @tc.steps:step4. Get the value of k1 from the local zone. + * @tc.expected: step4. Get returns OK, and the value is v1. + */ + Value valueRead; + EXPECT_EQ(g_kvNbDelegatePtr->Get(g_entry1.key, valueRead), OK); + EXPECT_EQ(g_entry1.value, valueRead); + EXPECT_EQ(g_kvNbDelegatePtr->Get(g_entry2.key, valueRead), OK); + EXPECT_EQ(g_entry2.value, valueRead); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry1.key, valueRead), OK); + EXPECT_EQ(g_entry1.value, valueRead); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry2.key, valueRead), NOT_FOUND); + + /** + * @tc.steps:step5. Check the observer. + * @tc.expected: step5. local observer received one inserted data. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_syncObserver->GetCallCount(), 0UL); + EXPECT_EQ(g_localObserver->GetCallCount(), 1UL); + EXPECT_EQ(g_localObserver->GetEntriesInserted().size(), 1UL); + + g_localObserver->ResetToZero(); + g_syncObserver->ResetToZero(); + + /** + * @tc.steps:step6. Unpublish the k2 with para of deletePublic: true, updateTimestamp: false. + * @tc.expected: step6. Unpublish returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry2.key, true, false), OK); + + /** + * @tc.steps:step7. Get the value of k2 from the local zone and the sync zone. + * @tc.expected: step7. GetLocal returns OK, and the value is equal to v2. Get returns NOT_FOUND. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Get(g_entry2.key, valueRead), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry2.key, valueRead), OK); + EXPECT_EQ(g_entry2.value, valueRead); + + /** + * @tc.steps:step8. Check the observer. + * @tc.expected: step8. Sync observer received 1 delete data, and the local observer received one inserted data. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_syncObserver->GetCallCount(), 1UL); + EXPECT_EQ(g_localObserver->GetCallCount(), 1UL); + EXPECT_EQ(g_localObserver->GetEntriesInserted().size(), 1UL); + EXPECT_EQ(g_syncObserver->GetEntriesDeleted().size(), 1UL); +} + +/** + * @tc.name: SingleVerUnPublishKey003 + * @tc.desc: Test unpublish one existent data(conflict with the local data). + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBUnpublishTest, SingleVerUnPublishKey003, TestSize.Level1) +{ + Value value3; + Value value4; + DistributedDBToolsUnitTest::GetRandomKeyValue(value3); + DistributedDBToolsUnitTest::GetRandomKeyValue(value4); + + /** + * @tc.steps:step1. Put [k1, v1] into the sync zone, [k1, v3][k2, v2] into the local zone, + * and put the [k2, v4] into the sync zone. + * @tc.expected: step1. Put returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(g_entry1.key, g_entry1.value), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(g_entry1.key, value3), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(g_entry2.key, g_entry2.value), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_kvNbDelegatePtr->Put(g_entry2.key, value4), OK); + + /** + * @tc.steps:step2. Register the obsevers for the sync data and the local data. + */ + RegisterObservers(); + + /** + * @tc.steps:step3. Unpublish the k2 with para of deletePublic: false, updateTimestamp: false. + * @tc.expected: step3. Unpublish returns LOCAL_DEFEAT. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, false, false), LOCAL_DEFEAT); + Value valueRead; + + /** + * @tc.steps:step4. Check the data of k1 in the local zone and the observer changes. + * @tc.expected: step4. Value of k1 is v3, and the observer has no change. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry1.key, valueRead), OK); + EXPECT_EQ(valueRead, value3); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_localObserver->GetCallCount(), 0UL); + + /** + * @tc.steps:step5. Unpublish the k1 with para of deletePublic: false, updateTimestamp: true. + * @tc.expected: step5. Unpublish returns LOCAL_COVERED. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, false, true), LOCAL_COVERED); + + /** + * @tc.steps:step6. Check the data of k1 in the local zone and the observer changes. + * @tc.expected: step6. Value of k1 is v1, and the observer received one updated data. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry1.key, valueRead), OK); + EXPECT_EQ(valueRead, g_entry1.value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_localObserver->GetCallCount(), 1UL); + EXPECT_EQ(g_localObserver->GetEntriesUpdated().size(), 1UL); + g_localObserver->ResetToZero(); + + /** + * @tc.steps:step7. Unpublish the k2 with para of deletePublic: false, updateTimestamp: false. + * @tc.expected: step7. Unpublish returns LOCAL_COVERED. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry2.key, false, false), LOCAL_COVERED); + + /** + * @tc.steps:step8. Check the data of k2 in the local zone and the observer changes. + * @tc.expected: step8. Value of k2 is v2, and the observer received one updated data. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry2.key, valueRead), OK); + EXPECT_EQ(valueRead, value4); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_localObserver->GetCallCount(), 1UL); + EXPECT_EQ(g_localObserver->GetEntriesUpdated().size(), 1UL); +} + +/** + * @tc.name: SingleVerUnPublishKey004 + * @tc.desc: Test unpublish one deleted data(no conflict with the local data). + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBUnpublishTest, SingleVerUnPublishKey004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Put [k1, v1] into the sync zone, [k2, v2] into the local zone, delete k1 from sync zone. + * @tc.expected: step1. Put returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(g_entry1.key, g_entry1.value), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(g_entry2.key, g_entry2.value), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_kvNbDelegatePtr->Delete(g_entry1.key), OK); + RegisterObservers(); + + /** + * @tc.steps:step2. Unpublish the k1 with para of deletePublic: false, updateTimestamp: false. + * @tc.expected: step2. Unpublish returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, false, false), OK); + + /** + * @tc.steps:step3. Check the observer and the data change. + * @tc.expected: step3. Observer have no changes. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_localObserver->GetCallCount(), 0UL); + EXPECT_EQ(g_syncObserver->GetCallCount(), 0UL); + Value valueRead; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry1.key, valueRead), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry2.key, valueRead), OK); + EXPECT_EQ(valueRead, g_entry2.value); +} + +/** + * @tc.name: SingleVerUnPublishKey005 + * @tc.desc: Test unpublish one existent data(conflict with the local data). + * @tc.type: FUNC + * @tc.require: AR000DPTQ5 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBUnpublishTest, SingleVerUnPublishKey005, TestSize.Level1) +{ + Value value3; + Value value4; + DistributedDBToolsUnitTest::GetRandomKeyValue(value3); + DistributedDBToolsUnitTest::GetRandomKeyValue(value4); + + /** + * @tc.steps:step1. Put [k1, v1] [k2, v2]into the sync zone, and delete the k1 from sync zone. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(g_entry1.key, g_entry1.value), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Put(g_entry2.key, g_entry2.value), OK); + EXPECT_EQ(g_kvNbDelegatePtr->Delete(g_entry1.key), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step2. Put [k1, v3] [k2, v4]into the local zone, and delete the k2 from sync zone. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(g_entry1.key, value3), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(g_entry2.key, value4), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_kvNbDelegatePtr->Delete(g_entry2.key), OK); + + /** + * @tc.steps:step2. Register the obsevers for the sync data and the local data. + */ + RegisterObservers(); + + /** + * @tc.steps:step3. Unpublish the k1 with para of deletePublic: false, updateTimestamp: false. + * @tc.expected: step3. Unpublish returns LOCAL_DEFEAT. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, false, false), LOCAL_DEFEAT); + + /** + * @tc.steps:step4. Check the value of k1 in the local zone and the observer changes. + * @tc.expected: step4. value of k1 is still v3, and the observer has no changes. + */ + Value valueRead; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry1.key, valueRead), OK); + EXPECT_EQ(valueRead, value3); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_localObserver->GetCallCount(), 0UL); + EXPECT_EQ(g_syncObserver->GetCallCount(), 0UL); + + /** + * @tc.steps:step5. Unpublish the k2 with para of deletePublic: false, updateTimestamp: false. + * @tc.expected: step5. Unpublish returns LOCAL_DELETED. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry2.key, false, false), LOCAL_DELETED); + + /** + * @tc.steps:step6. Check the value of k2 in the local zone and the observer changes. + * @tc.expected: step6. value of k2 is not found, and the local observer has one deleted data. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry2.key, valueRead), NOT_FOUND); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_localObserver->GetCallCount(), 1UL); + EXPECT_EQ(g_localObserver->GetEntriesDeleted().size(), 1UL); + g_localObserver->ResetToZero(); + + /** + * @tc.steps:step7. Unpublish the k1 with para of deletePublic: false, updateTimestamp: true. + * @tc.expected: step7. Unpublish returns LOCAL_DELETED. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnpublishToLocal(g_entry1.key, false, true), LOCAL_DELETED); + + /** + * @tc.steps:step8. Check the value of k1 in the local zone and the observer changes. + * @tc.expected: step8. value of k1 is not found, and the local observer has one deleted data. + */ + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(g_entry1.key, valueRead), NOT_FOUND); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_EQ(g_localObserver->GetCallCount(), 1UL); + EXPECT_EQ(g_localObserver->GetEntriesDeleted().size(), 1UL); + g_localObserver->ResetToZero(); +} diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_query_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_query_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c59b1713e0f283109159a4678ba73446851b0121 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_query_test.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "distributeddb_tools_unit_test.h" +#include "get_query_info.h" +#include "log_print.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace std; + +namespace { + const std::string TEST_FIELD_NAME = "$.test"; + + bool CheckQueryContainer(Query &query, std::list &checkList) + { + const std::list queryList = GetQueryInfo::GetQueryExpression(query).GetQueryExpression(); + if (queryList.size() != checkList.size()) { + return false; + } + auto queryIter = queryList.begin(); + + for (auto checkIter = checkList.begin(); checkIter != checkList.end(); checkIter++, queryIter++) { + EXPECT_EQ(checkIter->operFlag, queryIter->operFlag); + EXPECT_EQ(checkIter->type, queryIter->type); + EXPECT_EQ(checkIter->fieldName, queryIter->fieldName); + if (checkIter->fieldValue.size() != queryIter->fieldValue.size()) { + return false; + } + for (size_t i = 0; i < checkIter->fieldValue.size(); i++) { + EXPECT_EQ(memcmp(&(checkIter->fieldValue[i]), &(queryIter->fieldValue[i]), 8), 0); // only need check 8 + EXPECT_EQ(checkIter->fieldValue[i].stringValue, queryIter->fieldValue[i].stringValue); + } + } + return true; + } + + template + std::list CreateCheckList(QueryObjType operFlag, const std::string &fieldName, const T &queryValue) + { + FieldValue fieldValue; + QueryValueType type = GetQueryValueType::GetFieldTypeAndValue(queryValue, fieldValue); + std::vector values{fieldValue}; + if (type == QueryValueType::VALUE_TYPE_BOOL) { + std::list result{{operFlag, fieldName, QueryValueType::VALUE_TYPE_BOOL, values}}; + return result; + } else if (type == QueryValueType::VALUE_TYPE_INTEGER) { + std::list result{{operFlag, fieldName, QueryValueType::VALUE_TYPE_INTEGER, values}}; + return result; + } else if (type == QueryValueType::VALUE_TYPE_LONG) { + std::list result{{operFlag, fieldName, QueryValueType::VALUE_TYPE_LONG, values}}; + return result; + } else if (type == QueryValueType::VALUE_TYPE_DOUBLE) { + std::list result{{operFlag, fieldName, QueryValueType::VALUE_TYPE_DOUBLE, values}}; + return result; + } else if (type == QueryValueType::VALUE_TYPE_STRING) { + std::list result{{operFlag, fieldName, QueryValueType::VALUE_TYPE_STRING, values}}; + return result; + } else { + std::list result{{operFlag, fieldName, QueryValueType::VALUE_TYPE_INVALID, values}}; + return result; + } + } + + void CheckQueryCompareOper() + { + Query query1 = Query::Select().NotEqualTo(TEST_FIELD_NAME, 123); // random test data + std::list result = CreateCheckList(QueryObjType::NOT_EQUALTO, TEST_FIELD_NAME, 123); // random num + EXPECT_TRUE(CheckQueryContainer(query1, result)); + + Query query2 = Query::Select().EqualTo(TEST_FIELD_NAME, true); + result.clear(); + result = CreateCheckList(QueryObjType::EQUALTO, TEST_FIELD_NAME, true); + EXPECT_TRUE(CheckQueryContainer(query2, result)); + + Query query3 = Query::Select().GreaterThan(TEST_FIELD_NAME, 0); + result.clear(); + result = CreateCheckList(QueryObjType::GREATER_THAN, TEST_FIELD_NAME, 0); + EXPECT_TRUE(CheckQueryContainer(query3, result)); + + Query query4 = Query::Select().LessThan(TEST_FIELD_NAME, INT_MAX); + result.clear(); + result = CreateCheckList(QueryObjType::LESS_THAN, TEST_FIELD_NAME, INT_MAX); + EXPECT_TRUE(CheckQueryContainer(query4, result)); + + Query query5 = Query::Select().GreaterThanOrEqualTo(TEST_FIELD_NAME, 1.56); // random test data + result.clear(); + result = CreateCheckList(QueryObjType::GREATER_THAN_OR_EQUALTO, TEST_FIELD_NAME, 1.56); // random test data + EXPECT_TRUE(CheckQueryContainer(query5, result)); + + Query query6 = Query::Select().LessThanOrEqualTo(TEST_FIELD_NAME, 100); // random test data + result.clear(); + result = CreateCheckList(QueryObjType::LESS_THAN_OR_EQUALTO, TEST_FIELD_NAME, 100); // random test data + EXPECT_TRUE(CheckQueryContainer(query6, result)); + } +} + +class DistributedDBInterfacesQueryTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesQueryTest::SetUpTestCase(void) +{ +} + +void DistributedDBInterfacesQueryTest::TearDownTestCase(void) +{ +} + +void DistributedDBInterfacesQueryTest::SetUp(void) +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBInterfacesQueryTest::TearDown(void) +{ +} + +/** + * @tc.name: Query001 + * @tc.desc: Check the legal single query operation to see if the generated container is correct + * @tc.type: FUNC + * @tc.require: AR000DR9K6 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesQueryTest, Query001, TestSize.Level1) +{ + Query query = Query::Select(); + Query queryCopy = query; + Query queryMove = std::move(query); + + CheckQueryCompareOper(); + + std::string testValue = "testValue"; + Query query7 = Query::Select().Like(TEST_FIELD_NAME, testValue); + std::list result = CreateCheckList(QueryObjType::LIKE, TEST_FIELD_NAME, testValue); + EXPECT_TRUE(CheckQueryContainer(query7, result)); + + Query query8 = Query::Select().NotLike(TEST_FIELD_NAME, "testValue"); + result.clear(); + result = CreateCheckList(QueryObjType::NOT_LIKE, TEST_FIELD_NAME, testValue); + EXPECT_TRUE(CheckQueryContainer(query8, result)); + + vector fieldValues{1, 1, 1}; + Query query9 = Query::Select().In(TEST_FIELD_NAME, fieldValues); + FieldValue fieldValue; + fieldValue.integerValue = 1; + std::vector values{fieldValue, fieldValue, fieldValue}; + std::list result1{{QueryObjType::IN, TEST_FIELD_NAME, QueryValueType::VALUE_TYPE_INTEGER, values}}; + EXPECT_TRUE(CheckQueryContainer(query9, result1)); + + Query query10 = Query::Select().NotIn(TEST_FIELD_NAME, fieldValues); + std::list result2{{QueryObjType::NOT_IN, TEST_FIELD_NAME, + QueryValueType::VALUE_TYPE_INTEGER, values}}; + EXPECT_TRUE(CheckQueryContainer(query10, result2)); + + Query query11 = Query::Select().OrderBy(TEST_FIELD_NAME, false); + result.clear(); + result = CreateCheckList(QueryObjType::ORDERBY, TEST_FIELD_NAME, false); + EXPECT_TRUE(CheckQueryContainer(query11, result)); + + Query query12 = Query::Select().Limit(1, 2); + values.pop_back(); + values.back().integerValue = 2; + std::list result3{{QueryObjType::LIMIT, string(), QueryValueType::VALUE_TYPE_INTEGER, values}}; + EXPECT_TRUE(CheckQueryContainer(query12, result3)); + + Query query13 = Query::Select().IsNull(TEST_FIELD_NAME); + std::list result4{{QueryObjType::IS_NULL, TEST_FIELD_NAME, + QueryValueType::VALUE_TYPE_NULL, std::vector()}}; + EXPECT_TRUE(CheckQueryContainer(query13, result4)); +} + +/** + * @tc.name: Query002 + * @tc.desc: Check for illegal query conditions + * @tc.type: FUNC + * @tc.require: AR000DR9K6 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesQueryTest, Query002, TestSize.Level1) +{ + float testValue = 1.1; + Query query = Query::Select().NotEqualTo(".test", testValue); + EXPECT_FALSE(GetQueryInfo::GetQueryExpression(query).GetErrFlag()); + + EXPECT_FALSE(GetQueryInfo::GetQueryExpression(Query::Select().GreaterThan(TEST_FIELD_NAME, true)).GetErrFlag()); + + EXPECT_FALSE(GetQueryInfo::GetQueryExpression(Query::Select().LessThan("$.test.12test", true)).GetErrFlag()); +} + +/** + * @tc.name: Query003 + * @tc.desc: Check combination condition + * @tc.type: FUNC + * @tc.require: AR000DR9K6 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesQueryTest, Query003, TestSize.Level1) +{ + Query query = Query::Select().EqualTo(TEST_FIELD_NAME, true).And().GreaterThan(TEST_FIELD_NAME, 1); + QueryExpression queryExpression = GetQueryInfo::GetQueryExpression(query); + EXPECT_TRUE(queryExpression.GetErrFlag()); + EXPECT_EQ(queryExpression.GetQueryExpression().size(), 3UL); + + Query query1 = Query::Select().GreaterThan(TEST_FIELD_NAME, 1).OrderBy(TEST_FIELD_NAME); + QueryExpression queryExpression1 = GetQueryInfo::GetQueryExpression(query1); + EXPECT_TRUE(queryExpression1.GetErrFlag()); + EXPECT_EQ(queryExpression1.GetQueryExpression().size(), 2UL); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_register_syncdb_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_register_syncdb_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c46b69b1adb9ae9325293b9b59af52b9ca12248 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_register_syncdb_test.cpp @@ -0,0 +1,1871 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_common.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "platform_specific.h" +#include "securec.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const int OBSERVER_SLEEP_TIME = 100; + const bool LOCAL_ONLY = false; + const string STORE_ID = STORE_ID_SYNC; + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + KvStoreObserverUnitTest *g_observer = nullptr; + + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + DBStatus g_snapshotDelegateStatus = INVALID_ARGS; + KvStoreSnapshotDelegate *g_snapshotDelegatePtr = nullptr; + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + // the type of g_snapshotDelegateCallback is function + auto g_snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_snapshotDelegateStatus), std::ref(g_snapshotDelegatePtr)); + + vector TransStrToVector(const string &input) + { + vector output(input.begin(), input.end()); + return output; + } + + void PrintfEntryList(std::list inEntryList) + { + LOGI("begin print entry list! EntryList size [%zu]", inEntryList.size()); + for (const auto &entry : inEntryList) { + string temp = DBCommon::VectorToHexString(entry.value); + + LOGI("key[%s]", DBCommon::VectorToHexString(entry.key).c_str()); + LOGI("value[%s]", temp.c_str()); + LOGI("value size[%zu]", temp.size()); + } + } + + bool TestEntryList(const list &entries, const list &expectEntries) + { + bool checkRes = true; + EXPECT_EQ(entries.size(), expectEntries.size()); + bool findEntry = false; + for (const auto &entry : entries) { + findEntry = false; + for (const auto &expectEntry : expectEntries) { + if (entry.key != expectEntry.key) { + continue; + } + if (entry.value != expectEntry.value) { + LOGE("entry[%s]:[%s]", DBCommon::VectorToHexString(entry.value).c_str(), + DBCommon::VectorToHexString(expectEntry.value).c_str()); + checkRes = false; + goto END; + } else { + findEntry = true; + break; + } + } + if (!findEntry) { + LOGE("No value can matches!"); + checkRes = false; + goto END; + } + } + END: + if (!checkRes) { + PrintfEntryList(entries); + PrintfEntryList(expectEntries); + } + return checkRes; + } + + Entry GetEntry(const string &keyStr, const string &valueStr) + { + Entry entry; + entry.key = TransStrToVector(keyStr); + entry.value = TransStrToVector(valueStr); + return entry; + } +} + +class DistributedDBInterfacesRegisterSyncDBTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesRegisterSyncDBTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + string dir = g_testDir + "/multi_ver"; + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } +} + +void DistributedDBInterfacesRegisterSyncDBTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesRegisterSyncDBTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /* + * Here, we create STORE_ID before test, + * and it will be closed in TearDown(). + */ + KvStoreDelegate::Option option = {true, LOCAL_ONLY}; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + + g_observer = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(g_observer != nullptr); + g_observer->ResetToZero(); +} + +void DistributedDBInterfacesRegisterSyncDBTest::TearDown(void) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + if (g_kvDelegatePtr != nullptr) { + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore(STORE_ID) == OK); + } + + if (g_observer != nullptr) { + delete g_observer; + g_observer = nullptr; + } +} + +/** + * @tc.name: RegisterObserver001 + * @tc.desc: normal register observer success. + * @tc.type: FUNC + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver001, TestSize.Level1) +{ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver002 + * @tc.desc: register(null object) observer success + * @tc.type: FUNC + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver002, TestSize.Level1) +{ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(nullptr) == INVALID_ARGS); +} + +/** + * @tc.name: RegisterObserver003 + * @tc.desc: Test the new data and check the processing result of the callback function. + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + Key key; + Value value; + key.push_back('a'); + value.push_back('a'); + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->Put + * @tc.expected: step2. Return OK. + */ + DBStatus status = g_kvDelegatePtr->Put(key, value); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver004 + * @tc.desc: register observer success and putbach callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + vector entries; + for (int i = 1; i < 11; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + } + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->PutBatch + * @tc.expected: step2. Return OK. + */ + DBStatus status = g_kvDelegatePtr->PutBatch(entries); + + EXPECT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + LOGI("observer count:%lu", g_observer->GetCallCount()); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver005 + * @tc.desc: register observer success and putbach callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver005, TestSize.Level1) +{ + vector entries; + for (int i = 1; i < 6; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + } + DBStatus status = g_kvDelegatePtr->PutBatch(entries); + EXPECT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + entries.clear(); + for (int i = 1; i < 11; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + } + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->PutBatch + * @tc.expected: step2. Return OK. + */ + status = g_kvDelegatePtr->PutBatch(entries); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver006 + * @tc.desc: register observer success and update callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver006, TestSize.Level1) +{ + Key key; + Value value1; + Value value2; + key.push_back(1); + value1.push_back(8); + value2.push_back(10); + DBStatus status = g_kvDelegatePtr->Put(key, value1); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->Put(k1,v2) + * @tc.expected: step2. Return OK. + */ + status = g_kvDelegatePtr->Put(key, value2); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver007 + * @tc.desc: register observer success and delete callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver007, TestSize.Level1) +{ + Key key; + Value value; + key.push_back(1); + value.push_back(8); + DBStatus status = g_kvDelegatePtr->Put(key, value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->Delete + * @tc.expected: step2. Return OK. + */ + status = g_kvDelegatePtr->Delete(key); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver008 + * @tc.desc: register observer success and delete callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver008, TestSize.Level1) +{ + Key key; + key.push_back(1); + + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->Delete with no value + * @tc.expected: step2. Return OK. + */ + DBStatus status = g_kvDelegatePtr->Delete(key); + EXPECT_EQ(status, OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Do not print log. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 0); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver009 + * @tc.desc: register observer success and deletebatch callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver009, TestSize.Level1) +{ + vector entries; + vector keys; + for (int i = 1; i < 6; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + keys.push_back(entry.key); + } + DBStatus status = g_kvDelegatePtr->PutBatch(entries); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->DeleteBatch + * @tc.expected: step2. Return OK. + */ + status = g_kvDelegatePtr->DeleteBatch(keys); + EXPECT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver010 + * @tc.desc: register observer success and delete callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver010, TestSize.Level1) +{ + vector entries; + vector keys; + for (int i = 1; i < 6; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + } + for (int i = 1; i < 11; i++) { + Key key; + key.push_back(i); + keys.push_back(key); + } + DBStatus status = g_kvDelegatePtr->PutBatch(entries); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->DeleteBatch + * @tc.expected: step2. Return OK. + */ + status = g_kvDelegatePtr->DeleteBatch(keys); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver011 + * @tc.desc: register observer success and DeleteBatch callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver011, TestSize.Level1) +{ + vector keys; + for (int i = 1; i < 11; i++) { + Key key; + key.push_back(i); + keys.push_back(key); + } + + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->DeleteBatch with no value + * @tc.expected: step2. Return OK. + */ + DBStatus status = g_kvDelegatePtr->DeleteBatch(keys); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Do not print logy. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 0); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver012 + * @tc.desc: register observer success and clear callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver012, TestSize.Level1) +{ + vector entries; + vector keys; + for (int i = 1; i < 20; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + keys.push_back(entry.key); + } + DBStatus status = g_kvDelegatePtr->PutBatch(entries); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->Clear + * @tc.expected: step2. Return OK. + */ + status = g_kvDelegatePtr->Clear(); + EXPECT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver013 + * @tc.desc: register observer success and clear callback + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver013, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + + /** + * @tc.steps:step2. Test g_kvDelegatePtr->Clear with no key and value + * @tc.expected: step2. Return OK. + */ + DBStatus status = g_kvDelegatePtr->Clear(); + EXPECT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: RegisterObserver014 + * @tc.desc: Test the function of modifying a record, adding a record, + * deleting a record, and checking the processing result of the callback function. + * @tc.type: FUNC + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, RegisterObserver014, TestSize.Level1) +{ + Key key; + Value value; + key.push_back(1); + value.push_back(1); + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + + key.clear(); + value.clear(); + key.push_back(2); + value.push_back(2); + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test KvStoreDelegate.RegisterObserver + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + key.clear(); + value.clear(); + key.push_back(1); + value.push_back(4); + + /** + * @tc.steps:step2. Put(k1,v4) + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + key.clear(); + value.clear(); + key.push_back(3); + value.push_back(3); + + /** + * @tc.steps:step4. Put(k3,v3) + * @tc.expected: step4. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step5. Check the result of KvStoreObserver.OnChange + * @tc.expected: step5. Print log normally. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 2); + value.push_back(4); + + key.clear(); + key.push_back(2); + + /** + * @tc.steps:step6. Delete(k2) + * @tc.expected: step6. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Delete(key) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step7. Check the result of KvStoreObserver.OnChange + * @tc.expected: step7. Print log normally. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 3); + + key.clear(); + key.push_back(4); + + /** + * @tc.steps:step8. Delete(k4) + * @tc.expected: step8. Return OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Delete(key), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step9. Check the result of KvStoreObserver.OnChange + * @tc.expected: step9. Print log normally. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 3); + + /** + * @tc.steps:step10. Clear data + * @tc.expected: step10. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step11. Check the result of KvStoreObserver.OnChange + * @tc.expected: step11. Print log normally. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 4); + + /** + * @tc.steps:step12. Clear data repeat + * @tc.expected: step12. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step13. Check the result of KvStoreObserver.OnChange + * @tc.expected: step13. Print log normally. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 5); + + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: SnapshotRegisterObserver001 + * @tc.desc: register a normal observer for snapshot + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver002 + * @tc.desc: register a null observer for snapshot register a null observer for snapshot + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(nullptr, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver003 + * @tc.desc: register observer success and put callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot with + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + Key key; + Value value; + key.push_back(1); + value.push_back(1); + + /** + * @tc.steps:step2. Clear data repeat + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +static void CreatEntrysData(size_t size, uint8_t value, vector &entries) +{ + for (size_t i = 0; i < size; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back(value); + entries.push_back(entry); + } +} + +/** + * @tc.name: SnapshotRegisterObserver004 + * @tc.desc: register observer success and putBatch callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot with + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + vector entries; + CreatEntrysData(10, '8', entries); + + /** + * @tc.steps:step2. Put data by PutBatch + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver005 + * @tc.desc: register observer success and putBatch callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver005, TestSize.Level1) +{ + vector entries; + CreatEntrysData(6, '8', entries); + + DBStatus status = g_kvDelegatePtr->PutBatch(entries); + EXPECT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + entries.clear(); + CreatEntrysData(10, '8', entries); + + /** + * @tc.steps:step2. Put data by PutBatch + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver006 + * @tc.desc: register observer success and update callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver006, TestSize.Level1) +{ + Key key; + Value value; + key.push_back(1); + value.push_back(1); + + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + value.clear(); + value.push_back(2); + + /** + * @tc.steps:step2. Put data(k1,v2) + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver007 + * @tc.desc: register observer success and Delete callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver007, TestSize.Level1) +{ + Key key; + Value value; + key.push_back(1); + value.push_back(1); + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Delete the k1,v1 + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Delete(key) == OK); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver008 + * @tc.desc: register observer success and Delete null value callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver008, TestSize.Level1) +{ + Key key; + key.push_back(1); + + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Delete the k1, null value + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Delete(key), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 0); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver009 + * @tc.desc: register observer success and DeleteBatch callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver009, TestSize.Level1) +{ + vector entries; + vector keys; + for (int i = 1; i < 11; i++) { + Entry entry; + Key key; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + keys.push_back(entry.key); + } + + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Delete with no put + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->DeleteBatch(keys) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + LOGI("observer count:%lu", g_observer->GetCallCount()); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 0); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver010 + * @tc.desc: register observer success and DeleteBatch callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver010, TestSize.Level1) +{ + vector entries; + for (int i = 1; i < 6; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + } + + DBStatus status = g_kvDelegatePtr->PutBatch(entries); + EXPECT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + vector keys; + for (int i = 1; i < 11; i++) { + Key key; + key.push_back(i); + keys.push_back(key); + } + + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Delete the keys and value normally + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->DeleteBatch(keys) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver011 + * @tc.desc: register observer success and DeleteBatch callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver011, TestSize.Level1) +{ + EXPECT_TRUE(g_kvDelegateStatus == OK); + + vector keys; + for (int i = 1; i < 11; i++) { + Key key; + key.push_back(i); + keys.push_back(key); + } + + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Delete with no put + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->DeleteBatch(keys) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 0); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver012 + * @tc.desc: register observer success and Clear callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver012, TestSize.Level1) +{ + vector entries; + for (int i = 1; i < 20; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + } + DBStatus status = g_kvDelegatePtr->PutBatch(entries); + EXPECT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Clear data + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver013 + * @tc.desc: register observer success and Clear callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver013, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Clear data + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Do not print log normally. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotRegisterObserver014 + * @tc.desc: register observer success and operate callback + * @tc.require: AR000BVDFQ AR000CQDVJ + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotRegisterObserver014, TestSize.Level1) +{ + Key key; + Value value; + key.push_back(1); + value.push_back(1); + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + key.clear(); + value.clear(); + key.push_back(2); + value.push_back(2); + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + key.clear(); + value.clear(); + key.push_back(1); + value.push_back(4); + + /** + * @tc.steps:step2. Put data(k1, v4) + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step3. Check the result of KvStoreObserver.OnChange + * @tc.expected: step3. Print log normally. + */ + LOGI("observer count:%lu", g_observer->GetCallCount()); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + key.clear(); + value.clear(); + key.push_back(3); + value.push_back(3); + + /** + * @tc.steps:step4. Put data(k3, v3) + * @tc.expected: step4. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step5. Check the result of KvStoreObserver.OnChange + * @tc.expected: step5. Print log normally. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 2); + + key.clear(); + key.push_back(2); + + /** + * @tc.steps:step6. Delete(k2) + * @tc.expected: step6. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Delete(key) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step7. Check the result of KvStoreObserver.OnChange + * @tc.expected: step7. Print log normally. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 3); + + key.clear(); + key.push_back(4); + + /** + * @tc.steps:step8.Delete(k4) + * @tc.expected: step8. Return OK. + */ + EXPECT_EQ(g_kvDelegatePtr->Delete(key), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step9. Check the result of KvStoreObserver.OnChange + * @tc.expected: step9. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 3); + + /** + * @tc.steps:step10. Clear data + * @tc.expected: step10. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step11. Check the result of KvStoreObserver.OnChange + * @tc.expected: step11. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 4); + + /** + * @tc.steps:step12. Clear data repeat + * @tc.expected: step12. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps:step13. Check the result of KvStoreObserver.OnChange + * @tc.expected: step13. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 5); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: UnRegisterObserver001 + * @tc.desc: Unregister a normal observer + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, UnRegisterObserver001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test g_kvDelegatePtr->GetKvStoreSnapshot + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + + /** + * @tc.steps:step2. Test UnRegister Observer + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); +} + +/** + * @tc.name: UnRegisterObserver002 + * @tc.desc: Unregister a null observer + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, UnRegisterObserver002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test UnRegister Observer with an null observer + * @tc.expected: step1. Return INVALID_ARGS. + */ + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(nullptr) == INVALID_ARGS); +} + +/** + * @tc.name: UnRegisterObserver003 + * @tc.desc: Unregister a unregister observer + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, UnRegisterObserver003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test UnRegister Observer with observer do not have been registered + * @tc.expected: step1. Return NOT_FOUND. + */ + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == NOT_FOUND); +} + +/** + * @tc.name: UnRegisterObserver004 + * @tc.desc: Unregister a and check callback + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, UnRegisterObserver004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Register Observer + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->RegisterObserver(g_observer) == OK); + Key key; + Value value; + key.push_back(1); + value.push_back(1); + + /** + * @tc.steps:step2. Put data(k1,v1), Check the result of KvStoreObserver.OnChange + * @tc.expected: step2. Return OK. Print log normally. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step3. Unregister Observer + * @tc.expected: step3. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->UnRegisterObserver(g_observer) == OK); + key.clear(); + value.clear(); + key.push_back(2); + value.push_back(2); + + /** + * @tc.steps:step4. Put data(k2,v2) + * @tc.expected: step4. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + + /** + * @tc.steps:step5. Check the result of KvStoreObserver.OnChange + * @tc.expected: step5. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + vector entries; + vector keys; + for (int i = 11; i < 21; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + keys.push_back(entry.key); + } + + /** + * @tc.steps:step6. PutBatch 10 data + * @tc.expected: step6. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + + /** + * @tc.steps:step7. Check the result of KvStoreObserver.OnChange + * @tc.expected: step7. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + key.clear(); + value.clear(); + key.push_back(1); + value.push_back(2); + + /** + * @tc.steps:step8. Put data(k1, v2) + * @tc.expected: step8. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + + /** + * @tc.steps:step9. Check the result of KvStoreObserver.OnChange + * @tc.expected: step9. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step10. Delete data(k1) + * @tc.expected: step10. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Delete(key) == OK); + + /** + * @tc.steps:step11. Check the result of KvStoreObserver.OnChange + * @tc.expected: step11. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step12. Delete data(k1) + * @tc.expected: step12. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Delete(key) == OK); + + /** + * @tc.steps:step11. Check the result of KvStoreObserver.OnChange + * @tc.expected: step11. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step12. DeleteBatch data + * @tc.expected: step12. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->DeleteBatch(keys) == OK); + + /** + * @tc.steps:step13. Check the result of KvStoreObserver.OnChange + * @tc.expected: step13. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step14. Clear all data + * @tc.expected: step14. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + + /** + * @tc.steps:step15. Check the result of KvStoreObserver.OnChange + * @tc.expected: step15. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step16. Clear all data repeat + * @tc.expected: step16. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + + /** + * @tc.steps:step17. Check the result of KvStoreObserver.OnChange + * @tc.expected: step17. Do not print log. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); +} + +/** + * @tc.name: SnapshotUnRegisterObserver001 + * @tc.desc: Unregister a snapshot observer + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotUnRegisterObserver001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get KvStore Snapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotUnRegisterObserver002 + * @tc.desc: Unregister a null snapshot observer + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotUnRegisterObserver002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get KvStore Snapshot with snapshot is null + * @tc.expected: step1. Return ERROR. + */ + KvStoreSnapshotDelegate *snapshot = nullptr; + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot) == DB_ERROR); + g_snapshotDelegatePtr = nullptr; +} + +/** + * @tc.name: SnapshotUnRegisterObserver003 + * @tc.desc: Unregister a unregister snapshot observer + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotUnRegisterObserver003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get KvStore Snapshot with snapshot is not registered + * @tc.expected: step1. Return OK. + */ + KvStoreSnapshotDelegate *snapshot = new (std::nothrow) KvStoreSnapshotDelegateImpl(nullptr, nullptr); + ASSERT_TRUE(snapshot != nullptr); + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(snapshot) == OK); + g_snapshotDelegatePtr = nullptr; +} + +static void SnapshotUnRegisterObserver004Inner() +{ + /** + * @tc.steps:step1. Get KvStore Snapshot + * @tc.expected: step1. Return OK. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + Key key; + Value value; + key.push_back(1); // random key + value.push_back(1); // random value + + /** + * @tc.steps:step2. Put data(k1,v1), Check the result of KvStoreObserver.OnChange + * @tc.expected: step2. Return OK. Print log normally. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step3. Release KvStore Snapshot + * @tc.expected: step3. Return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; + key.clear(); + value.clear(); + key.push_back(2); // random key + value.push_back(2); // random value + + /** + * @tc.steps:step4/5. Put data(k2,v2), Check the result of KvStoreObserver.OnChange + * @tc.expected: step4/5. Return OK. Do not print log. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(g_observer->GetCallCount() == 1); +} + +/** + * @tc.name: SnapshotUnRegisterObserver004 + * @tc.desc: Check a unregister snapshot observer + * @tc.require: AR000BVDFP AR000CQDVI + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, SnapshotUnRegisterObserver004, TestSize.Level1) +{ + SnapshotUnRegisterObserver004Inner(); + + vector entries; + vector keys; + for (int i = 11; i < 21; i++) { + Entry entry; + entry.key.push_back(i); + entry.value.push_back('8'); + entries.push_back(entry); + keys.push_back(entry.key); + } + + /** + * @tc.steps:step6/7. PutBatch 10 data, Check the result of KvStoreObserver.OnChange + * @tc.expected: step6/7. Return OK. Do not print log. + */ + EXPECT_TRUE(g_kvDelegatePtr->PutBatch(entries) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + Key key; + Value value; + key.push_back(1); + value.push_back(2); + + /** + * @tc.steps:step8/9. Put data(k1,v2), Check the result of KvStoreObserver.OnChange + * @tc.expected: step8/9. Return OK. Do not print log. + */ + EXPECT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step10/11. Delete(k1), Check the result of KvStoreObserver.OnChange + * @tc.expected: step10/11. Return OK. Do not print log. + */ + EXPECT_TRUE(g_kvDelegatePtr->Delete(key) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step12/13. Delete a not exist key, Check the result of KvStoreObserver.OnChange + * @tc.expected: step12/13. Return OK. Do not print log. + */ + EXPECT_TRUE(g_kvDelegatePtr->Delete(key) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step14/15. DeleteBatch, Check the result of KvStoreObserver.OnChange + * @tc.expected: step14/15. Return OK. Do not print log. + */ + EXPECT_TRUE(g_kvDelegatePtr->DeleteBatch(keys) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step14/15. Clear all data, Check the result of KvStoreObserver.OnChange + * @tc.expected: step14/15. Return OK. Do not print log. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(g_observer->GetCallCount() == 1); + + /** + * @tc.steps:step14/15. Clear all data repeat, Check the result of KvStoreObserver.OnChange + * @tc.expected: step14/15. Return OK. Do not print log. + */ + EXPECT_TRUE(g_kvDelegatePtr->Clear() == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(g_observer->GetCallCount() == 1); +} + +static void CheckObserverCallback(const Entry &entryB) +{ + /** + * @tc.steps: step3. Start a transaction. Insert [keyA, valueD], [keyC, valueC] + * through the Put interface of the delegate, and delete the keyB data + * through the Delete interface of the delegate. Ending a transaction + * @tc.expected: step3. Obtain [keyC, valueC] + * from the GetEntriesInserted of the callback data, + * obtain [keyA, valueD] from the GetEntriesUpdated, + * and obtain [keyB, valueB] through the GetEntriesDeleted. + */ + EXPECT_EQ(g_kvDelegatePtr->StartTransaction(), OK); + Entry entryC = GetEntry("key_C", ""); + DistributedDBToolsUnitTest::GetRandomKeyValue(entryC.value, 10 * 1024); // 30K + Entry entryE = GetEntry("key_E", ""); + DistributedDBToolsUnitTest::GetRandomKeyValue(entryE.value, 200 * 1024); // 200K, over the slice threshold. + Entry entryF = GetEntry("key_F", ""); + DistributedDBToolsUnitTest::GetRandomKeyValue(entryF.value, 100); // 100 + EXPECT_EQ(g_kvDelegatePtr->Put(entryE.key, entryE.value), OK); + EXPECT_EQ(g_kvDelegatePtr->Put(entryF.key, entryF.value), OK); + EXPECT_EQ(g_kvDelegatePtr->Put(entryC.key, entryC.value), OK); + Entry entryD = GetEntry("key_A", ""); + DistributedDBToolsUnitTest::GetRandomKeyValue(entryD.value, 100 * 1024); // 100k + EXPECT_EQ(g_kvDelegatePtr->Put(entryD.key, entryD.value), OK); + EXPECT_EQ(g_kvDelegatePtr->Delete(entryB.key), OK); + EXPECT_EQ(g_kvDelegatePtr->Commit(), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME * 10)); + + /** + * @tc.steps: step4. Check whether the observer callback is triggered + * and check the data obtained from the survey. + */ + EXPECT_TRUE(g_observer->GetCallCount() == 1); + std::list inserted = g_observer->GetEntriesInserted(); + std::list updated = g_observer->GetEntriesUpdated(); + std::list deleted = g_observer->GetEntriesDeleted(); + LOGI("insert size[%zu], updated size[%zu], deleted size[%zu]", inserted.size(), updated.size(), deleted.size()); + + std::list expectedInserted; + std::list expectedUpdated; + std::list expectedDeleted; + expectedInserted.push_back(entryC); + expectedInserted.push_back(entryE); + expectedInserted.push_back(entryF); + expectedUpdated.push_back(entryD); + expectedDeleted.push_back(entryB); + EXPECT_TRUE(TestEntryList(inserted, expectedInserted)); + EXPECT_TRUE(TestEntryList(updated, expectedUpdated)); + EXPECT_TRUE(TestEntryList(deleted, expectedDeleted)); +} + +/** + * @tc.name: GetObserverData001 + * @tc.desc: Test whether the data change notification can obtain these changes + * when the database is added, deleted, or modified. + * @tc.type: FUNC + * @tc.require: AR000BVDFR AR000CQDVK + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, GetObserverData001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Insert the data of [keyA, valueA], [keyB, valueB] through the Put interface of the delegate. + */ + Entry entryA = GetEntry("key_A", "value_A"); + EXPECT_EQ(g_kvDelegatePtr->Put(entryA.key, entryA.value), OK); + Entry entryB = GetEntry("key_B", "value_B"); + EXPECT_EQ(g_kvDelegatePtr->Put(entryB.key, entryB.value), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps: step2. Register an observer through the RegisterObserver interface of the delegate. + * @tc.expected: step2. Returns a non-empty snapshot. + */ + EXPECT_EQ(g_kvDelegatePtr->RegisterObserver(g_observer), OK); + + CheckObserverCallback(entryB); + + EXPECT_EQ(g_kvDelegatePtr->UnRegisterObserver(g_observer), OK); +} + +/** + * @tc.name: GetSnapshotObserverData001 + * @tc.desc: Test whether a data notification is sent + * when the value of observer is not empty + * when a snapshot is obtained and the database data changes. + * @tc.type: FUNC + * @tc.require: AR000C06UT AR000CQDTG + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesRegisterSyncDBTest, GetSnapshotObserverData001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Insert the data of [keyA, valueA], [keyB, valueB] through the Put interface of the delegate. + */ + Entry entryA = GetEntry("key_A", "value_A"); + EXPECT_EQ(g_kvDelegatePtr->Put(entryA.key, entryA.value), OK); + Entry entryB = GetEntry("key_B", "value_B"); + EXPECT_EQ(g_kvDelegatePtr->Put(entryB.key, entryB.value), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps: step2. Obtain the snapshot object snapshotA + * through the GetKvStoreSnapshot interface of the delegate and transfer the non-null observer. + * @tc.expected: step2. Returns a non-empty snapshot. + */ + g_kvDelegatePtr->GetKvStoreSnapshot(g_observer, g_snapshotDelegateCallback); + EXPECT_EQ(g_kvDelegateStatus, OK); + + CheckObserverCallback(entryB); + + EXPECT_EQ(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr), OK); + g_snapshotDelegatePtr = nullptr; +} diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_relational_sync_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_relational_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dda185f9a7e35af85140ede62a7db65a37782d8e --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_relational_sync_test.cpp @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_common.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "relational_store_manager.h" +#include "virtual_communicator_aggregator.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + constexpr const char* DB_SUFFIX = ".db"; + constexpr const char* STORE_ID = "Relational_Store_ID"; + const std::string DEVICE_A = "DEVICE_A"; + std::string g_testDir; + std::string g_dbDir; + DistributedDB::RelationalStoreManager g_mgr(APP_ID, USER_ID); + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + + const std::string NORMAL_CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS sync_data(" \ + "key BLOB NOT NULL UNIQUE," \ + "value BLOB," \ + "timestamp INT NOT NULL," \ + "flag INT NOT NULL," \ + "device BLOB," \ + "ori_device BLOB," \ + "hash_key BLOB PRIMARY KEY NOT NULL," \ + "w_timestamp INT," \ + "UNIQUE(device, ori_device));" \ + "CREATE INDEX key_index ON sync_data (key, flag);"; + + const std::string EMPTY_COLUMN_TYPE_CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS student(" \ + "id INTEGER NOT NULL UNIQUE," \ + "name TEXT," \ + "field_1);"; +} + +class DistributedDBInterfacesRelationalSyncTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp() override; + void TearDown() override; +protected: + sqlite3 *db = nullptr; + RelationalStoreDelegate *delegate = nullptr; +}; + +void DistributedDBInterfacesRelationalSyncTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + LOGD("Test dir is %s", g_testDir.c_str()); + g_dbDir = g_testDir + "/"; + + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); +} + +void DistributedDBInterfacesRelationalSyncTest::TearDownTestCase(void) +{ + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); +} + +void DistributedDBInterfacesRelationalSyncTest::SetUp() +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + + db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", DEVICE_A); + + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + status = delegate->CreateDistributedTable("sync_data"); + EXPECT_EQ(status, OK); +} + +void DistributedDBInterfacesRelationalSyncTest::TearDown() +{ + g_mgr.CloseStore(delegate); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +/** + * @tc.name: RelationalSyncTest001 + * @tc.desc: Test with sync interface, table is not a distributed table + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest001, TestSize.Level1) +{ + std::vector devices = {DEVICE_A}; + Query query = Query::Select("sync_datb"); + int errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + + EXPECT_EQ(errCode, DISTRIBUTED_SCHEMA_NOT_FOUND); +} + +/** + * @tc.name: RelationalSyncTest002 + * @tc.desc: Test with sync interface, query is not support + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest002, TestSize.Level1) +{ + std::vector devices = {DEVICE_A}; + Query query = Query::Select("sync_data").Like("value", "abc"); + int errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + + EXPECT_EQ(errCode, NOT_SUPPORT); +} + +/** + * @tc.name: RelationalSyncTest003 + * @tc.desc: Test with sync interface, query is invalid format + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest003, TestSize.Level1) +{ + std::vector devices = {DEVICE_A}; + Query query = Query::Select("sync_data").And().Or().EqualTo("flag", 2); + int errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + + EXPECT_EQ(errCode, INVALID_QUERY_FORMAT); +} + +/** + * @tc.name: RelationalSyncTest004 + * @tc.desc: Test with sync interface, query use invalid field + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest004, TestSize.Level1) +{ + std::vector devices = {DEVICE_A}; + Query query = Query::Select("sync_data").EqualTo("fleg", 2); + int errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + + EXPECT_EQ(errCode, INVALID_QUERY_FIELD); +} + +/** + * @tc.name: RelationalSyncTest005 + * @tc.desc: Test with sync interface, query table has been modified + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest005, TestSize.Level1) +{ + std::string modifySql = "ALTER TABLE sync_data ADD COLUMN add_field INTEGER;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, modifySql), SQLITE_OK); + + std::vector devices = {DEVICE_A}; + Query query = Query::Select("sync_data"); + int errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + + EXPECT_EQ(errCode, DISTRIBUTED_SCHEMA_CHANGED); +} + +/** + * @tc.name: RelationalSyncTest006 + * @tc.desc: Test with sync interface, query is not set table name + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest006, TestSize.Level1) +{ + std::vector devices = {DEVICE_A}; + Query query = Query::Select(); + int errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + + EXPECT_EQ(errCode, NOT_SUPPORT); +} + +/** + * @tc.name: RelationalSyncTest007 + * @tc.desc: Test with sync interface, distributed table has empty column type + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest007, TestSize.Level1) +{ + EXPECT_EQ(RelationalTestUtils::ExecSql(db, EMPTY_COLUMN_TYPE_CREATE_TABLE_SQL), SQLITE_OK); + RelationalTestUtils::CreateDeviceTable(db, "student", DEVICE_A); + + DBStatus status = delegate->CreateDistributedTable("student"); + EXPECT_EQ(status, OK); + + std::vector devices = {DEVICE_A}; + Query query = Query::Select("student"); + int errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + + EXPECT_EQ(errCode, OK); +} + +/** + * @tc.name: RelationalSyncTest008 + * @tc.desc: Test sync with rebuilt table + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest008, TestSize.Level1) +{ + /** + * @tc.steps:step1. Drop sync_data + * @tc.expected: step1. ok + */ + std::string dropSql = "DROP TABLE IF EXISTS sync_data;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, dropSql), SQLITE_OK); + + /** + * @tc.steps:step2. sync with sync_data + * @tc.expected: step2. return INVALID_QUERY_FORMAT + */ + std::vector devices = {DEVICE_A}; + Query query = Query::Select("sync_data"); + int errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + EXPECT_EQ(errCode, DISTRIBUTED_SCHEMA_CHANGED); + + /** + * @tc.steps:step3. recreate sync_data + * @tc.expected: step3. ok + */ + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + DBStatus status = delegate->CreateDistributedTable("sync_data"); + EXPECT_EQ(status, OK); + + /** + * @tc.steps:step4. Check trigger + * @tc.expected: step4. trigger exists + */ + bool result = false; + std::string checkSql = "select * from sqlite_master where type = 'trigger' and tbl_name = 'sync_data';"; + EXPECT_EQ(RelationalTestUtils::CheckSqlResult(db, checkSql, result), E_OK); + EXPECT_EQ(result, true); + + /** + * @tc.steps:step5. sync with sync_data + * @tc.expected: step5. ok + */ + errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + + EXPECT_EQ(errCode, OK); +} + +/** + * @tc.name: RelationalSyncTest009 + * @tc.desc: Test sync with invalid query + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest009, TestSize.Level1) +{ + EXPECT_EQ(RelationalTestUtils::ExecSql(db, EMPTY_COLUMN_TYPE_CREATE_TABLE_SQL), SQLITE_OK); + RelationalTestUtils::CreateDeviceTable(db, "student", DEVICE_A); + + DBStatus status = delegate->CreateDistributedTable("student"); + EXPECT_EQ(status, OK); + + std::vector devices = {DEVICE_A}; + Query query = Query::Select("student").EqualTo("$id", 123); + status = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + EXPECT_EQ(status, INVALID_QUERY_FORMAT); + + query = Query::Select("student").EqualTo("A$id", 123); + status = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + EXPECT_EQ(status, INVALID_QUERY_FORMAT); + + query = Query::Select("student").EqualTo("$.id", 123); + status = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + + EXPECT_EQ(status, OK); +} + +/** + * @tc.name: RelationalSyncTest010 + * @tc.desc: Test sync with shcema changed + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalSyncTest, RelationalSyncTest010, TestSize.Level1) +{ + std::vector devices = {DEVICE_A}; + Query query = Query::Select("sync_data"); + int errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + EXPECT_EQ(errCode, OK); + + std::string modifySql = "DROP TABLE IF EXISTS sync_data;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, modifySql), SQLITE_OK); + + errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + EXPECT_EQ(errCode, DISTRIBUTED_SCHEMA_CHANGED); + + errCode = delegate->Sync(devices, SyncMode::SYNC_MODE_PUSH_ONLY, query, + [&devices](const std::map> &devicesMap) { + EXPECT_EQ(devicesMap.size(), devices.size()); + }, true); + EXPECT_EQ(errCode, DISTRIBUTED_SCHEMA_CHANGED); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_relational_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_relational_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..824f9be1d9fce6f5c31f348409089b23436ae899 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_relational_test.cpp @@ -0,0 +1,784 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "db_common.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "platform_specific.h" +#include "relational_store_manager.h" +#include "relational_store_sqlite_ext.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + constexpr const char* DB_SUFFIX = ".db"; + constexpr const char* STORE_ID = "Relational_Store_ID"; + std::string g_testDir; + std::string g_dbDir; + DistributedDB::RelationalStoreManager g_mgr(APP_ID, USER_ID); + + const std::string NORMAL_CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS sync_data(" \ + "key BLOB NOT NULL UNIQUE," \ + "value BLOB," \ + "timestamp INT NOT NULL," \ + "flag INT NOT NULL," \ + "device BLOB," \ + "ori_device BLOB," \ + "hash_key BLOB PRIMARY KEY NOT NULL," \ + "w_timestamp INT," \ + "UNIQUE(device, ori_device));" \ + "CREATE INDEX key_index ON sync_data (key, flag);"; + + const std::string CREATE_TABLE_SQL_NO_PRIMARY_KEY = "CREATE TABLE IF NOT EXISTS sync_data(" \ + "key BLOB NOT NULL UNIQUE," \ + "value BLOB," \ + "timestamp INT NOT NULL," \ + "flag INT NOT NULL," \ + "device BLOB," \ + "ori_device BLOB," \ + "hash_key BLOB NOT NULL," \ + "w_timestamp INT," \ + "UNIQUE(device, ori_device));" \ + "CREATE INDEX key_index ON sync_data (key, flag);"; + + const std::string UNSUPPORTED_FIELD_TABLE_SQL = "CREATE TABLE IF NOT EXISTS test('$.ID' INT, val BLOB);"; + + const std::string COMPOSITE_PRIMARY_KEY_TABLE_SQL = R"(CREATE TABLE workers ( + worker_id INTEGER, + last_name VARCHAR NOT NULL, + first_name VARCHAR, + join_date DATE, + PRIMARY KEY (last_name, first_name) + );)"; + + const std::string INSERT_SYNC_DATA_SQL = "INSERT OR REPLACE INTO sync_data (key, timestamp, flag, hash_key) " + "VALUES('KEY', 123456789, 1, 'HASH_KEY');"; +} + +class DistributedDBInterfacesRelationalTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesRelationalTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + LOGD("Test dir is %s", g_testDir.c_str()); + g_dbDir = g_testDir + "/"; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +void DistributedDBInterfacesRelationalTest::TearDownTestCase(void) +{ +} + +void DistributedDBInterfacesRelationalTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBInterfacesRelationalTest::TearDown(void) +{ + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +/** + * @tc.name: RelationalStoreTest001 + * @tc.desc: Test open store and create distributed db + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalStoreTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_A"); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + /** + * @tc.steps:step2. open relational store, create distributed table, close store + * @tc.expected: step2. Return OK. + */ + RelationalStoreDelegate *delegate = nullptr; + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + status = delegate->CreateDistributedTable("sync_data"); + EXPECT_EQ(status, OK); + + // test create same table again + status = delegate->CreateDistributedTable("sync_data"); + EXPECT_EQ(status, OK); + + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); + + /** + * @tc.steps:step3. drop sync_data table + * @tc.expected: step3. Return OK. + */ + db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "drop table sync_data;"), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + /** + * @tc.steps:step4. open again, check auxiliary should be delete + * @tc.expected: step4. Return OK. + */ + delegate = nullptr; + status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); +} + +/** + * @tc.name: RelationalStoreTest002 + * @tc.desc: Test open store with invalid path or store ID + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalStoreTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + /** + * @tc.steps:step2. Test open store with invalid path or store ID + * @tc.expected: step2. open store failed. + */ + RelationalStoreDelegate *delegate = nullptr; + + // test open store with path not exist + DBStatus status = g_mgr.OpenStore(g_dbDir + "tmp/" + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_NE(status, OK); + ASSERT_EQ(delegate, nullptr); + + // test open store with empty store_id + status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, {}, {}, delegate); + EXPECT_NE(status, OK); + ASSERT_EQ(delegate, nullptr); + + // test open store with path has invalid character + status = g_mgr.OpenStore(g_dbDir + "t&m$p/" + STORE_ID + DB_SUFFIX, {}, {}, delegate); + EXPECT_NE(status, OK); + ASSERT_EQ(delegate, nullptr); + + // test open store with store_id has invalid character + status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, "Relation@al_S$tore_ID", {}, delegate); + EXPECT_NE(status, OK); + ASSERT_EQ(delegate, nullptr); + + // test open store with store_id length over MAX_STORE_ID_LENGTH + status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, + std::string(DBConstant::MAX_STORE_ID_LENGTH + 1, 'a'), {}, delegate); + EXPECT_NE(status, OK); + ASSERT_EQ(delegate, nullptr); +} + +/** + * @tc.name: RelationalStoreTest003 + * @tc.desc: Test open store with journal_mode is not WAL + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalStoreTest003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file with string is not WAL + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=PERSIST;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + /** + * @tc.steps:step2. Test open store + * @tc.expected: step2. Open store failed. + */ + RelationalStoreDelegate *delegate = nullptr; + + // test open store with journal mode is not WAL + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_NE(status, OK); + ASSERT_EQ(delegate, nullptr); +} + +/** + * @tc.name: RelationalStoreTest004 + * @tc.desc: Test create distributed table with over limit + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalStoreTest004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file with multiple tables + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + const int tableCount = DBConstant::MAX_DISTRIBUTED_TABLE_COUNT + 10; // 10: additional size for test abnormal scene + for (int i=0; iCreateDistributedTable("TEST_" + std::to_string(i)), OK); + } else { + EXPECT_NE(delegate->CreateDistributedTable("TEST_" + std::to_string(i)), OK); + } + } + + /** + * @tc.steps:step3. Close store + * @tc.expected: step3. Return OK. + */ + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); +} + +/** + * @tc.name: RelationalStoreTest005 + * @tc.desc: Test create distributed table with invalid table name + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalStoreTest005, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + /** + * @tc.steps:step2. Open store + * @tc.expected: step2. return OK + */ + RelationalStoreDelegate *delegate = nullptr; + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + /** + * @tc.steps:step3. Create distributed table with invalid table name + * @tc.expected: step3. Create distributed table failed. + */ + EXPECT_NE(delegate->CreateDistributedTable(DBConstant::SYSTEM_TABLE_PREFIX + "_tmp"), OK); + + EXPECT_EQ(delegate->CreateDistributedTable("Handle-J@^."), INVALID_ARGS); + + /** + * @tc.steps:step4. Close store + * @tc.expected: step4. Return OK. + */ + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); +} + +/** + * @tc.name: RelationalStoreTest006 + * @tc.desc: Test create distributed table with non primary key schema + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalStoreTest006, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, CREATE_TABLE_SQL_NO_PRIMARY_KEY), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + /** + * @tc.steps:step2. Open store + * @tc.expected: step2. return OK + */ + RelationalStoreDelegate *delegate = nullptr; + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + /** + * @tc.steps:step3. Create distributed table with invalid table name + * @tc.expected: step3. Create distributed table failed. + */ + EXPECT_EQ(delegate->CreateDistributedTable("sync_data"), OK); + + /** + * @tc.steps:step4. Close store + * @tc.expected: step4. Return OK. + */ + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); + delegate = nullptr; + + status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); +} + +/** + * @tc.name: RelationalStoreTest007 + * @tc.desc: Test create distributed table with table has invalid field name + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalStoreTest007, TestSize.Level1) +{ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, UNSUPPORTED_FIELD_TABLE_SQL), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + RelationalStoreDelegate *delegate = nullptr; + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + EXPECT_EQ(delegate->CreateDistributedTable("test"), NOT_SUPPORT); + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); +} + +/** + * @tc.name: RelationalStoreTest008 + * @tc.desc: Test create distributed table with table has composite primary key + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalStoreTest008, TestSize.Level1) +{ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, COMPOSITE_PRIMARY_KEY_TABLE_SQL), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + RelationalStoreDelegate *delegate = nullptr; + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + EXPECT_EQ(delegate->CreateDistributedTable("workers"), NOT_SUPPORT); + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); +} + +namespace { +void TableModifyTest(const std::string &modifySql, DBStatus expect) +{ + /** + * @tc.steps:step1. Prepare db file + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_A"); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_B"); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_C"); + + /** + * @tc.steps:step2. Open store + * @tc.expected: step2. return OK + */ + RelationalStoreDelegate *delegate = nullptr; + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + /** + * @tc.steps:step3. Create distributed table + * @tc.expected: step3. Create distributed table OK. + */ + EXPECT_EQ(delegate->CreateDistributedTable("sync_data"), OK); + + /** + * @tc.steps:step4. Upgrade table with modifySql + * @tc.expected: step4. return OK + */ + EXPECT_EQ(RelationalTestUtils::ExecSql(db, modifySql), SQLITE_OK); + + /** + * @tc.steps:step5. Create distributed table again + * @tc.expected: step5. Create distributed table return expect. + */ + EXPECT_EQ(delegate->CreateDistributedTable("sync_data"), expect); + + /** + * @tc.steps:step6. Close store + * @tc.expected: step6 Return OK. + */ + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); +} +} + +/** + * @tc.name: RelationalTableModifyTest001 + * @tc.desc: Test modify distributed table with compatible upgrade + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalTableModifyTest001, TestSize.Level1) +{ + TableModifyTest("ALTER TABLE sync_data ADD COLUMN add_field INTEGER NOT NULL DEFAULT 123;", OK); +} + +/** + * @tc.name: RelationalTableModifyTest002 + * @tc.desc: Test modify distributed table with incompatible upgrade + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalTableModifyTest002, TestSize.Level1) +{ + TableModifyTest("ALTER TABLE sync_data ADD COLUMN add_field INTEGER NOT NULL;", SCHEMA_MISMATCH); +} + +/** + * @tc.name: RelationalTableModifyTest003 + * @tc.desc: Test modify distributed table with incompatible upgrade + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalTableModifyTest003, TestSize.Level1) +{ + TableModifyTest("ALTER TABLE sync_data DROP COLUMN w_timestamp;", SCHEMA_MISMATCH); +} + +/** + * @tc.name: RelationalTableModifyTest004 + * @tc.desc: Test upgrade distributed table with device table exists + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalTableModifyTest004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_A"); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_B"); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_C"); + + /** + * @tc.steps:step2. Open store + * @tc.expected: step2. return OK + */ + RelationalStoreDelegate *delegate = nullptr; + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + /** + * @tc.steps:step3. Create distributed table + * @tc.expected: step3. Create distributed table OK. + */ + EXPECT_EQ(delegate->CreateDistributedTable("sync_data"), OK); + + /** + * @tc.steps:step4. Upgrade table + * @tc.expected: step4. return OK + */ + std::string modifySql = "ALTER TABLE sync_data ADD COLUMN add_field INTEGER;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, modifySql), SQLITE_OK); + std::string indexSql = "CREATE INDEX add_index ON sync_data (add_field);"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, indexSql), SQLITE_OK); + std::string deleteIndexSql = "DROP INDEX IF EXISTS key_index"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, deleteIndexSql), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, INSERT_SYNC_DATA_SQL), SQLITE_OK); + + /** + * @tc.steps:step5. Create distributed table again + * @tc.expected: step5. Create distributed table return expect. + */ + EXPECT_EQ(delegate->CreateDistributedTable("sync_data"), OK); + + /** + * @tc.steps:step6. Close store + * @tc.expected: step6 Return OK. + */ + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); +} + +/** + * @tc.name: RelationalTableModifyTest005 + * @tc.desc: Test modify distributed table with compatible upgrade + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalTableModifyTest005, TestSize.Level1) +{ + TableModifyTest("ALTER TABLE sync_data ADD COLUMN add_field STRING NOT NULL DEFAULT 'asdf';", OK); +} + +/** + * @tc.name: RelationalRemoveDeviceDataTest001 + * @tc.desc: Test remove device data + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalRemoveDeviceDataTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_A"); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_B"); + RelationalTestUtils::CreateDeviceTable(db, "sync_data", "DEVICE_C"); + + /** + * @tc.steps:step2. Open store + * @tc.expected: step2. return OK + */ + RelationalStoreDelegate *delegate = nullptr; + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + /** + * @tc.steps:step3. Remove device data + * @tc.expected: step3. ok + */ + EXPECT_EQ(delegate->RemoveDeviceData("DEVICE_A"), OK); + EXPECT_EQ(delegate->RemoveDeviceData("DEVICE_B"), OK); + EXPECT_EQ(delegate->RemoveDeviceData("DEVICE_C", "sync_data"), OK); + + /** + * @tc.steps:step4. Remove device data with invalid args + * @tc.expected: step4. invalid + */ + EXPECT_EQ(delegate->RemoveDeviceData(""), INVALID_ARGS); + EXPECT_EQ(delegate->RemoveDeviceData("DEVICE_A", "Handle-J@^."), INVALID_ARGS); + + /** + * @tc.steps:step5. Close store + * @tc.expected: step5 Return OK. + */ + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); +} + +/** + * @tc.name: RelationalOpenStorePathCheckTest001 + * @tc.desc: Test open store with same label but different path. + * @tc.type: FUNC + * @tc.require: AR000GK58F + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalOpenStorePathCheckTest001, TestSize.Level1) +{ + std::string dir1 = g_dbDir + "dbDir1"; + EXPECT_EQ(OS::MakeDBDirectory(dir1), E_OK); + sqlite3 *db1 = RelationalTestUtils::CreateDataBase(dir1 + STORE_ID + DB_SUFFIX); + ASSERT_NE(db1, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db1, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db1, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db1), SQLITE_OK); + + std::string dir2 = g_dbDir + "dbDir2"; + EXPECT_EQ(OS::MakeDBDirectory(dir2), E_OK); + sqlite3 *db2 = RelationalTestUtils::CreateDataBase(dir2 + STORE_ID + DB_SUFFIX); + ASSERT_NE(db2, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db2, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db2, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db2), SQLITE_OK); + + DBStatus status = OK; + RelationalStoreDelegate *delegate1 = nullptr; + status = g_mgr.OpenStore(dir1 + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate1); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate1, nullptr); + + RelationalStoreDelegate *delegate2 = nullptr; + status = g_mgr.OpenStore(dir2 + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate2); + EXPECT_EQ(status, INVALID_ARGS); + ASSERT_EQ(delegate2, nullptr); + + status = g_mgr.CloseStore(delegate1); + EXPECT_EQ(status, OK); + + status = g_mgr.CloseStore(delegate2); + EXPECT_EQ(status, INVALID_ARGS); +} + +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalOpenStorePressureTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + DBStatus status = OK; + for (int i = 0; i < 1000; i++) { + RelationalStoreDelegate *delegate = nullptr; + status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + + status = g_mgr.CloseStore(delegate); + EXPECT_EQ(status, OK); + delegate = nullptr; + } +} + +HWTEST_F(DistributedDBInterfacesRelationalTest, RelationalOpenStorePressureTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare db file + * @tc.expected: step1. Return OK. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); + + std::queue delegateQueue; + std::mutex queueLock; + std::random_device rd; + default_random_engine e(rd()); + uniform_int_distribution u(0, 9); + + std::thread openStoreThread([&, this]() { + for (int i = 0; i < 1000; i++) { + LOGD("++++> open store delegate: %d", i); + RelationalStoreDelegate *delegate = nullptr; + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(delegate, nullptr); + { + std::lock_guard lock(queueLock); + delegateQueue.push(delegate); + } + LOGD("++++< open store delegate: %d", i); + } + }); + + int cnt = 0; + while (cnt < 1000) { + RelationalStoreDelegate *delegate = nullptr; + { + std::lock_guard lock(queueLock); + if (delegateQueue.empty()) { + std::this_thread::sleep_for(std::chrono::microseconds(100)); + continue; + } + delegate = delegateQueue.front(); + delegateQueue.pop(); + } + LOGD("++++> close store delegate: %d", cnt); + DBStatus status = g_mgr.CloseStore(delegate); + LOGD("++++< close store delegate: %d", cnt); + EXPECT_EQ(status, OK); + delegate = nullptr; + cnt++; + std::this_thread::sleep_for(std::chrono::microseconds(100 * u(e))); + } + openStoreThread.join(); +} diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_resultset_performance.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_resultset_performance.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ded0a669fe1f7e81b1791467849509df56a4af3 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_resultset_performance.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "distributeddb_tools_unit_test.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.h" +#include "log_print.h" +#include "store_types.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr("app0", "user0"); + string g_testDir; + KvStoreConfig g_config; + Key g_keyPrefix = {'A', 'B', 'C'}; + + const int BASE_NUMBER = 100000; + const int INSERT_NUMBER = 100; + const int ENTRY_VALUE_SIZE = 3000; + const int BATCH_ENTRY_NUMBER = 100; + + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + + void KvStoreNbDelegateCallback(DBStatus statusSrc, KvStoreNbDelegate* kvStoreSrc, + DBStatus* statusDst, KvStoreNbDelegate** kvStoreDst) + { + *statusDst = statusSrc; + *kvStoreDst = kvStoreSrc; + } + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, &g_kvDelegateStatus, &g_kvNbDelegatePtr); + + void InitResultSet() + { + Key testKey; + Value testValue; + for (int i = BASE_NUMBER; i < BASE_NUMBER + INSERT_NUMBER; i++) { + testKey.clear(); + testValue.clear(); + testKey = g_keyPrefix; + std::string strIndex = std::to_string(i); + testKey.insert(testKey.end(), strIndex.begin(), strIndex.end()); + + DistributedDBToolsUnitTest::GetRandomKeyValue(testValue, ENTRY_VALUE_SIZE); + if ((i % BATCH_ENTRY_NUMBER) == 0) { + g_kvNbDelegatePtr->StartTransaction(); + } + EXPECT_EQ(g_kvNbDelegatePtr->Put(testKey, testValue), OK); + if (((i + 1) % BATCH_ENTRY_NUMBER) == 0) { + g_kvNbDelegatePtr->Commit(); + } + } + + std::this_thread::sleep_for(std::chrono::seconds(2)); // sleep 2 s for the cache. + } +} +class DistributedDBInterfacesNBResultsetPerfTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesNBResultsetPerfTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesNBResultsetPerfTest::TearDownTestCase(void) +{ +} + +void DistributedDBInterfacesNBResultsetPerfTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvNbDelegatePtr = nullptr; + g_kvDelegatePtr = nullptr; +} + +void DistributedDBInterfacesNBResultsetPerfTest::TearDown(void) +{ + if (g_kvDelegatePtr != nullptr) { + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_kvNbDelegatePtr = nullptr; + } +} + +/** + * @tc.name: ResultSetPerfTest001 + * @tc.desc: Test the NbDelegate for result set function. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBInterfacesNBResultsetPerfTest, ResultSetPerfTest001, TestSize.Level4) +{ + /** + * @tc.steps: step1. initialize result set. + * @tc.expected: step1. Success. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore("resultset_perf_test", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + InitResultSet(); + + /** + * @tc.steps: step2. get entries using result set. + * @tc.expected: step2. Success. + */ + LOGI("######## Before get resultset"); + KvStoreResultSet *readResultSet = nullptr; + Key keyGet = g_keyPrefix; + keyGet.push_back('1'); + + int offset = 40; // offset 40 + LOGI("######## Query resultSet"); + Query query = Query::Select().PrefixKey(keyGet).Limit(50, offset); // limit 50 + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(query, readResultSet), OK); + ASSERT_TRUE(readResultSet != nullptr); + LOGI("######## After get resultset"); + int totalCount = readResultSet->GetCount(); + EXPECT_EQ(totalCount, 50); // limit 50 + LOGI("######## After get count:%d", totalCount); + + readResultSet->MoveToPosition(0); + LOGI("######## After move to next"); + EXPECT_EQ(g_kvNbDelegatePtr->CloseResultSet(readResultSet), OK); + EXPECT_TRUE(readResultSet == nullptr); + + std::this_thread::sleep_for(std::chrono::seconds(5)); // sleep 5 s + LOGI("######## Plain resultSet"); + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(keyGet, readResultSet), OK); + ASSERT_TRUE(readResultSet != nullptr); + LOGI("######## After get resultset"); + totalCount = readResultSet->GetCount(); + EXPECT_EQ(totalCount, INSERT_NUMBER); + LOGI("######## After get count:%d", totalCount); + + readResultSet->MoveToPosition(offset); + LOGI("######## After move to next"); + EXPECT_EQ(g_kvNbDelegatePtr->CloseResultSet(readResultSet), OK); + EXPECT_TRUE(readResultSet == nullptr); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("resultset_perf_test"), OK); + g_kvNbDelegatePtr = nullptr; +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_schema_database_upgrade_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_schema_database_upgrade_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c0dd063026e1c17546fe5d6fef2abe2f6c4ba9e4 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_schema_database_upgrade_test.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_JSON +#include +#include "distributeddb_tools_unit_test.h" +#include "kv_store_delegate_manager.h" +#include "schema_constant.h" +#include "schema_object.h" +#include "schema_utils.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + const std::string APP_ID = "SCHEMA"; + const std::string USER_ID = "UPGRADE"; + std::string g_testDir; + KvStoreDelegateManager g_manager(APP_ID, USER_ID); + + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvDelegatePtr = nullptr; + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + std::placeholders::_1, std::placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + std::string g_baseSchema; + DBStatus g_expectError = SCHEMA_VIOLATE_VALUE; + + std::string StringEraser(const std::string &oriString, const std::string &toErase) + { + std::string resStr = oriString; + auto iter = std::search(resStr.begin(), resStr.end(), toErase.begin(), toErase.end()); + resStr.erase(iter, iter + toErase.size()); + return resStr; + } + std::string StringReplacer(const std::string &oriString, const std::string &toErase, const std::string &toRepalce) + { + std::string resStr = oriString; + auto iter = std::search(resStr.begin(), resStr.end(), toErase.begin(), toErase.end()); + resStr.replace(iter, iter + toErase.size(), toRepalce); + return resStr; + } + + const std::string SCHEMA_INC_FIELD = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_DEFINE\":{" + "\"field_1\":\"LONG, NOT NULL, DEFAULT 100\"," + "\"field_2\":{" + "\"field_3\":\"STRING, DEFAULT 'OpenHarmony'\"," + "\"field_4\":\"INTEGER\"" + "}" + "}," + "\"SCHEMA_INDEXES\":[\"field_1\", [\"field_2.field_3\", \"field_2.field_4\"]]}"; + const std::string SCHEMA_BASE = StringReplacer(StringEraser(SCHEMA_INC_FIELD, ",\"field_4\":\"INTEGER\""), + "[\"field_2.field_3\", \"field_2.field_4\"]", "\"field_2.field_3\""); + const std::string SCHEMA_INC_FIELD_NOTNULL = StringReplacer(SCHEMA_INC_FIELD, "\"INTEGER\"", + "\"INTEGER, NOT NULL\""); + const std::string SCHEMA_INC_FIELD_DEFAULT = StringReplacer(SCHEMA_INC_FIELD, "\"INTEGER\"", + "\"INTEGER, DEFAULT 88\""); + const std::string SCHEMA_INC_FIELD_NOTNULL_DEFAULT = StringReplacer(SCHEMA_INC_FIELD, "\"INTEGER\"", + "\"INTEGER, NOT NULL, DEFAULT 88\""); + + const std::string VALUE_BASE_LACK = "{\"field_1\":LONG_VAL}"; + const std::string VALUE_BASE = "{\"field_1\":LONG_VAL,\"field_2\":{\"field_3\":STR_VAL}}"; + const std::string VALUE_FIELD_FULL = "{\"field_1\":LONG_VAL,\"field_2\":{\"field_3\":STR_VAL,\"field_4\":INT_VAL}}"; + + std::string SchemaSwitchMode(const std::string &oriSchemaStr) + { + std::string resStr = oriSchemaStr; + auto iterStrict = std::search(resStr.begin(), resStr.end(), SchemaConstant::KEYWORD_MODE_STRICT.begin(), + SchemaConstant::KEYWORD_MODE_STRICT.end()); + auto iterCompatible = std::search(resStr.begin(), resStr.end(), SchemaConstant::KEYWORD_MODE_COMPATIBLE.begin(), + SchemaConstant::KEYWORD_MODE_COMPATIBLE.end()); + if (iterStrict != resStr.end()) { + resStr.replace(iterStrict, iterStrict + SchemaConstant::KEYWORD_MODE_STRICT.size(), + SchemaConstant::KEYWORD_MODE_COMPATIBLE.begin(), SchemaConstant::KEYWORD_MODE_COMPATIBLE.end()); + return resStr; + } + if (iterCompatible != resStr.end()) { + resStr.replace(iterCompatible, iterCompatible + SchemaConstant::KEYWORD_MODE_COMPATIBLE.size(), + SchemaConstant::KEYWORD_MODE_STRICT.begin(), SchemaConstant::KEYWORD_MODE_STRICT.end()); + return resStr; + } + return oriSchemaStr; + } + bool SchemaChecker(const std::string schema) + { + SchemaObject schemaObj; + return (schemaObj.ParseFromSchemaString(schema) == E_OK); + } + std::vector ToVec(const std::string &inStr) + { + std::vector outVec(inStr.begin(), inStr.end()); + return outVec; + } + std::string ToStr(const std::vector &inVec) + { + std::string outStr(inVec.begin(), inVec.end()); + return outStr; + } + + const std::map> VALUE_MAP { + {"LACK", ToVec(StringReplacer(VALUE_BASE_LACK, "LONG_VAL", "1"))}, + {"BASE", ToVec(StringReplacer(StringReplacer(VALUE_BASE, "LONG_VAL", "2"), "STR_VAL", "\"OS\""))}, + {"FULL", ToVec(StringReplacer(StringReplacer(StringReplacer(VALUE_FIELD_FULL, "LONG_VAL", "3"), "STR_VAL", + "\"TEST\""), "INT_VAL", "33"))}, + {"BASE_WRONG_TYPE", ToVec(StringReplacer(StringReplacer(VALUE_BASE, "LONG_VAL", "2"), "STR_VAL", "10086"))}, + {"FULL_NULL", ToVec(StringReplacer(StringReplacer(StringReplacer(VALUE_FIELD_FULL, "LONG_VAL", "3"), + "STR_VAL", "\"TEST\""), "INT_VAL", "null"))}, + {"FULL_WRONG_TYPE", ToVec(StringReplacer(StringReplacer(StringReplacer(VALUE_FIELD_FULL, "LONG_VAL", "3"), + "STR_VAL", "\"TEST\""), "INT_VAL", "\"UT\""))}, + }; + + // Key begin from "KEY_1" + void InsertPresetEntry(KvStoreNbDelegate &delegate, const std::vector &selection) + { + int count = 0; + for (const auto &eachSel : selection) { + ASSERT_NE(VALUE_MAP.count(eachSel), 0ul); + DBStatus ret = delegate.Put(ToVec(std::string("KEY_") + std::to_string(++count)), VALUE_MAP.at(eachSel)); + ASSERT_EQ(ret, OK); + } + } +} + +class DistributedDBInterfacesSchemaDatabaseUpgradeTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesSchemaDatabaseUpgradeTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + KvStoreConfig config{g_testDir}; + g_manager.SetKvStoreConfig(config); + ASSERT_EQ(SchemaChecker(SCHEMA_BASE), true); + ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD), true); + ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD_NOTNULL), true); + ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD_DEFAULT), true); + ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD_NOTNULL_DEFAULT), true); +} + +void DistributedDBInterfacesSchemaDatabaseUpgradeTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("[TestSchemaUpgrade] Remove test directory error."); + } +} + +void DistributedDBInterfacesSchemaDatabaseUpgradeTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvDelegatePtr = nullptr; +} + +void DistributedDBInterfacesSchemaDatabaseUpgradeTest::TearDown(void) +{ + if (g_kvDelegatePtr != nullptr) { + ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + } +} + +/** + * @tc.name: UpgradeFromKv001 + * @tc.desc: Schema database upgrade from kv database, exist value match compatible schema(mismatch strict schema) + * @tc.type: FUNC + * @tc.require: AR000F3OPD + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromKv001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Prepare kv database with value match compatible schema(mismatch strict schema) then close + * @tc.expected: step1. E_OK + */ + std::string storeId = "UpgradeFromKv001"; + KvStoreNbDelegate::Option option; + g_manager.GetKvStore(storeId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_EQ(g_kvDelegateStatus, OK); + + InsertPresetEntry(*g_kvDelegatePtr, std::vector{"LACK", "BASE", "LACK", "BASE", "FULL"}); + DBStatus ret = g_kvDelegatePtr->Delete(ToVec("KEY_4")); + ASSERT_EQ(ret, OK); + ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + + /** + * @tc.steps: step2. Upgrade to schema(strict) database + * @tc.expected: step2. SCHEMA_VIOLATE_VALUE + */ + option.schema = SchemaSwitchMode(SCHEMA_BASE); + g_manager.GetKvStore(storeId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + ASSERT_EQ(g_kvDelegateStatus, SCHEMA_VIOLATE_VALUE); + + /** + * @tc.steps: step3. Upgrade to schema(compatible) database + * @tc.expected: step3. E_OK + */ + option.schema = SCHEMA_BASE; + g_manager.GetKvStore(storeId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_EQ(g_kvDelegateStatus, OK); + + /** + * @tc.steps: step4. Query field_2.field_3 equal OpenHarmony + * @tc.expected: step4. E_OK, KEY_1 + */ + Query query = Query::Select().EqualTo("field_2.field_3", "OpenHarmony"); + std::vector entries; + EXPECT_EQ(g_kvDelegatePtr->GetEntries(query, entries), OK); + ASSERT_EQ(entries.size(), 2ul); + EXPECT_EQ(ToStr(entries[0].key), std::string("KEY_1")); + EXPECT_EQ(ToStr(entries[1].key), std::string("KEY_3")); + std::string valStr = ToStr(entries[0].value); + std::string defaultVal = "OpenHarmony"; + auto iter = std::search(valStr.begin(), valStr.end(), defaultVal.begin(), defaultVal.end()); + EXPECT_TRUE(iter != valStr.end()); +} + +/** + * @tc.name: UpgradeFromKv002 + * @tc.desc: Schema database upgrade from kv database, exist value mismatch compatible schema + * @tc.type: FUNC + * @tc.require: AR000F3OPD + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromKv002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Prepare kv database with value mismatch compatible schema then close + * @tc.expected: step1. E_OK + */ + std::string storeId = "UpgradeFromKv002"; + KvStoreNbDelegate::Option option; + g_manager.GetKvStore(storeId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_EQ(g_kvDelegateStatus, OK); + + InsertPresetEntry(*g_kvDelegatePtr, std::vector{"BASE_WRONG_TYPE"}); + ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + + /** + * @tc.steps: step2. Upgrade to schema(compatible) database + * @tc.expected: step2. SCHEMA_VIOLATE_VALUE + */ + option.schema = SCHEMA_BASE; + g_manager.GetKvStore(storeId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + ASSERT_EQ(g_kvDelegateStatus, SCHEMA_VIOLATE_VALUE); +} + +namespace { +void TestUpgradeFromSchema(const std::string &storeId, const std::vector &selection, + const std::string &newSchema, bool expectMatch, uint32_t expectCount) +{ + LOGI("[TestUpgradeFromSchema] StoreId=%s", storeId.c_str()); + /** + * @tc.steps: step1. Prepare kv database with value then close + * @tc.expected: step1. E_OK + */ + KvStoreNbDelegate::Option option; + option.schema = g_baseSchema; + g_manager.GetKvStore(storeId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_EQ(g_kvDelegateStatus, OK); + + InsertPresetEntry(*g_kvDelegatePtr, selection); + ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + + /** + * @tc.steps: step2. Upgrade to schema database + * @tc.expected: step2. OK or SCHEMA_VIOLATE_VALUE + */ + option.schema = newSchema; + g_manager.GetKvStore(storeId, option, g_kvDelegateCallback); + if (expectMatch) { + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + ASSERT_EQ(g_kvDelegateStatus, OK); + } else { + ASSERT_TRUE(g_kvDelegatePtr == nullptr); + ASSERT_EQ(g_kvDelegateStatus, g_expectError); + } + + /** + * @tc.steps: step3. Query field_2.field_3 + * @tc.expected: step3. E_OK + */ + if (expectMatch) { + Query query = Query::Select().EqualTo("field_2.field_4", 88); // 88 is the default value in the testcase. + std::vector entries; + if (expectCount == 0) { + EXPECT_EQ(g_kvDelegatePtr->GetEntries(query, entries), NOT_FOUND); + } else { + ASSERT_EQ(g_kvDelegatePtr->GetEntries(query, entries), OK); + EXPECT_EQ(entries.size(), expectCount); + } + ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + } +} +} + +/** + * @tc.name: UpgradeFromSchema001 + * @tc.desc: Schema database upgrade from kv database, exist value match new schema + * @tc.type: FUNC + * @tc.require: AR000F3OPD + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromSchema001, TestSize.Level1) +{ + g_baseSchema = SCHEMA_BASE; + g_expectError = SCHEMA_VIOLATE_VALUE; + TestUpgradeFromSchema("UpgradeFromSchema001_1", std::vector{"LACK", "BASE", "FULL", "FULL_NULL"}, + SCHEMA_INC_FIELD, true, 0); + TestUpgradeFromSchema("UpgradeFromSchema001_2", std::vector{"LACK", "BASE", "FULL", "FULL_NULL"}, + SCHEMA_INC_FIELD_DEFAULT, true, 2); + TestUpgradeFromSchema("UpgradeFromSchema001_3", std::vector{"LACK", "BASE", "FULL"}, + SCHEMA_INC_FIELD_NOTNULL_DEFAULT, true, 2); +} + +/** + * @tc.name: UpgradeFromSchema002 + * @tc.desc: Schema database upgrade from kv database, exist value mismatch new schema + * @tc.type: FUNC + * @tc.require: AR000F3OPD + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromSchema002, TestSize.Level1) +{ + g_baseSchema = SCHEMA_BASE; + g_expectError = SCHEMA_VIOLATE_VALUE; + TestUpgradeFromSchema("UpgradeFromSchema002_1", std::vector{"LACK", "BASE", "FULL", "FULL_WRONG_TYPE"}, + SCHEMA_INC_FIELD, false, 0); + TestUpgradeFromSchema("UpgradeFromSchema002_2", std::vector{"LACK", "BASE", "FULL", "FULL_WRONG_TYPE"}, + SCHEMA_INC_FIELD_DEFAULT, false, 0); + TestUpgradeFromSchema("UpgradeFromSchema002_3", std::vector{"LACK", "BASE", "FULL", "FULL_NULL"}, + SCHEMA_INC_FIELD_NOTNULL_DEFAULT, false, 0); +} + +/** + * @tc.name: UpgradeFromSchema003 + * @tc.desc: Schema database upgrade from kv database, new schema incompatible with old schema + * @tc.type: FUNC + * @tc.require: AR000F3OPD + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromSchema003, TestSize.Level1) +{ + // Compatible schema can increase field, but must not be null without default. + g_baseSchema = SCHEMA_BASE; + g_expectError = SCHEMA_MISMATCH; + TestUpgradeFromSchema("UpgradeFromSchema003_1", std::vector{"LACK", "BASE", "FULL", "FULL_NULL"}, + SCHEMA_INC_FIELD_NOTNULL, false, 0); + // Strict schema can not incrase field + g_baseSchema = SchemaSwitchMode(SCHEMA_BASE); + TestUpgradeFromSchema("UpgradeFromSchema003_2", std::vector{"LACK", "BASE"}, + SchemaSwitchMode(SCHEMA_INC_FIELD), false, 0); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_single_version_result_set_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_single_version_result_set_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4bd33648244fcbcb7590d42ff8342dd0a0ae609 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_single_version_result_set_test.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_common.h" +#include "db_constant.h" +#include "db_types.h" +#include "distributeddb_data_generate_unit_test.h" +#include "ikvdb_raw_cursor.h" +#include "kv_store_nb_delegate_impl.h" +#include "kvdb_manager.h" +#include "platform_specific.h" +#include "result_entries_window.h" +#include "sqlite_single_ver_forward_cursor.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + string g_identifier; + IKvDBRawCursor *g_rawCursor = nullptr; + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + DBStatus g_kvDelegateStatus = INVALID_ARGS; + SQLiteSingleVerNaturalStore *g_store = nullptr; + DistributedDB::SQLiteSingleVerNaturalStoreConnection *g_connection = nullptr; + const string STORE_ID = STORE_ID_SYNC; + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + const int TIME_LAG = 100; + const int INITIAL_POSITION = 0; + const int SECOND_POSITION = 1; + const int TOTAL_COUNT = 3; + const Key KEY_PREFIX = {'K'}; + const Key LOCAL_KEY_1 = {'K', '1'}; + const Key LOCAL_KEY_2 = {'K', '2'}; + const Key LOCAL_KEY_3 = {'K', '3'}; + const Key LOCAL_KEY_4 = {'K', '4'}; +} + +class DistributedDBInterfacesSingleVersionResultSetTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesSingleVersionResultSetTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + std::string origIdentifier = USER_ID + "-" + APP_ID + "-" + STORE_ID; + std::string identifier = DBCommon::TransferHashString(origIdentifier); + g_identifier = DBCommon::TransferStringToHex(identifier); + string dir = g_testDir + g_identifier + "/" + DBConstant::SINGLE_SUB_DIR; + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } +} + +void DistributedDBInterfacesSingleVersionResultSetTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + STORE_ID + "/" + DBConstant::SINGLE_SUB_DIR) != 0) { + LOGE("rm test db files error!"); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); +} + +void DistributedDBInterfacesSingleVersionResultSetTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + KvStoreNbDelegate::Option delegateOption = {true}; + g_mgr.GetKvStore(STORE_ID, delegateOption, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + + KvDBProperties property; + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, STORE_ID); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, g_identifier); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + + g_store = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(g_store, nullptr); + ASSERT_EQ(g_store->Open(property), E_OK); + + int errCode = E_OK; + g_connection = static_cast(g_store->GetDBConnection(errCode)); + ASSERT_NE(g_connection, nullptr); + g_store->DecObjRef(g_store); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps:step1. Put 3 data items. + * @tc.expected: step1. + */ + IOption option; + option.dataType = IOption::SYNC_DATA; + g_connection->Clear(option); + ASSERT_EQ(g_connection->Put(option, LOCAL_KEY_1, VALUE_1), OK); + ASSERT_EQ(g_connection->Put(option, LOCAL_KEY_2, VALUE_2), OK); + ASSERT_EQ(g_connection->Put(option, LOCAL_KEY_3, VALUE_3), OK); + + EXPECT_EQ(errCode, E_OK); + g_rawCursor = new (std::nothrow) SQLiteSingleVerForwardCursor(g_store, KEY_PREFIX); + ASSERT_NE(g_rawCursor, nullptr); +} + +void DistributedDBInterfacesSingleVersionResultSetTest::TearDown(void) +{ + if (g_connection != nullptr) { + g_connection->Close(); + g_connection = nullptr; + } + + g_store = nullptr; + + if (g_kvNbDelegatePtr != nullptr) { + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore(STORE_ID) == OK); + } + + if (g_rawCursor != nullptr) { + delete g_rawCursor; + g_rawCursor = nullptr; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); +} + +/** + * @tc.name: SingleVersionResultSetTest001 + * @tc.desc: CursorWindow Class: Return error when the window size too large. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be INT_MAX, which is larger than the upper limit. + * @tc.expected: step1. Expect return -E_INVALID_ARGS. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int64_t windoweSize = 0x100000000L; // 4G + EXPECT_EQ(resultWindow.Init(g_rawCursor, windoweSize, scale), -E_INVALID_ARGS); +} + +/** + * @tc.name: SingleVersionResultSetTest002 + * @tc.desc: CursorWindow Class: Return error when the window size is negative. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be -1. + * @tc.expected: step1. Expect return -E_INVALID_ARGS. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = -1; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), -E_INVALID_ARGS); +} + +/** + * @tc.name: SingleVersionResultSetTest003 + * @tc.desc: CursorWindow Class: Return error when the window size is zero. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 0. + * @tc.expected: step1. Expect return -E_INVALID_ARGS. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = 0; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), -E_INVALID_ARGS); +} + +/** + * @tc.name: SingleVersionResultSetTest004 + * @tc.desc: CursorWindow Class: Return OK when the window size is positive. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, which is smaller than the upper limit. + * @tc.expected: step1. Expect return OK. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), E_OK); +} + +/** + * @tc.name: SingleVersionResultSetTest005 + * @tc.desc: CursorWindow Class: Return -E_INVALID_ARGS when the window scale is negative. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest005, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be negative (-1). + * @tc.expected: step1. Expect return -E_INVALID_ARGS. + */ + ResultEntriesWindow resultWindow; + double scale = -1; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), -E_INVALID_ARGS); +} + +/** + * @tc.name: SingleVersionResultSetTest006 + * @tc.desc: CursorWindow Class: Return -E_INVALID_ARGS when the window scale is larger than 1. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest006, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 2. + * @tc.expected: step1. Expect return -E_INVALID_ARGS. + */ + ResultEntriesWindow resultWindow; + double scale = 2; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), -E_INVALID_ARGS); +} + +/** + * @tc.name: SingleVersionResultSetTest007 + * @tc.desc: CursorWindow Class: Return -E_INVALID_ARGS when the window scale 0. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest007, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 0. + * @tc.expected: step1. Expect return -E_INVALID_ARGS. + */ + ResultEntriesWindow resultWindow; + double scale = 0; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), -E_INVALID_ARGS); +} + +/** + * @tc.name: SingleVersionResultSetTest008 + * @tc.desc: CursorWindow Class: Return OK when the window scale is between 0 and 1. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest008, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 0.5. + * @tc.expected: step1. Expect return OK. + */ + ResultEntriesWindow resultWindow; + double scale = 0.5; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), E_OK); +} + +/** + * @tc.name: SingleVersionResultSetTest009 + * @tc.desc: CursorWindow Class: Return -E_INVALID_ARGS when the g_rawCursor is nulSSSlptr. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest009, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 1 and resultWindow be null pointer. + * @tc.expected: step1. Expect return -E_INVALID_ARGS. + */ + IKvDBRawCursor *rawCursor = nullptr; + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(rawCursor, windowSize, scale), -E_INVALID_ARGS); +} + +/** + * @tc.name: SingleVersionResultSetTest010 + * @tc.desc: CursorWindow Class: Check if get total count is feasible. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest010, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 1 and resultWindow be null pointer. + * @tc.expected: step1. Expect return OK. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), E_OK); + + /** + * @tc.steps:step2. Get the total count. + * @tc.expected: step2. Expect return 3. + */ + EXPECT_EQ(resultWindow.GetTotalCount(), TOTAL_COUNT); +} + +/** + * @tc.name: SingleVersionResultSetTest011 + * @tc.desc: CursorWindow Class: Check if get total count is feasible and the inserted items after + * creating ResultEntriesWindow have not been counted. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest011, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 1 and resultWindow be null pointer. + * @tc.expected: step1. Expect return OK. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), E_OK); + + /** + * @tc.steps:step2. Get the total count. + * @tc.expected: step2. Expect return 3. + */ + EXPECT_EQ(resultWindow.GetTotalCount(), TOTAL_COUNT); + + /** + * @tc.steps:step3. Put one more item + * @tc.expected: step3. + */ + IOption option; + option.dataType = IOption::SYNC_DATA; + ASSERT_EQ(g_connection->Put(option, LOCAL_KEY_4, VALUE_4), OK); + + /** + * @tc.steps:step4. Get the total count. + * @tc.expected: step4. Expect return 3. + */ + EXPECT_EQ(resultWindow.GetTotalCount(), TOTAL_COUNT); +} + +/** + * @tc.name: SingleVersionResultSetTest012 + * @tc.desc: CursorWindow Class: Check if current position after initialization is at 0. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest012, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 1 and resultWindow be null pointer. + * @tc.expected: step1. Expect return OK. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), E_OK); + + /** + * @tc.steps:step2. Get initial position. + * @tc.expected: step2. Expect return INITIAL_POSITION (which is 0). + */ + EXPECT_EQ(resultWindow.GetCurrentPosition(), INITIAL_POSITION); + + /** + * @tc.steps:step3. Get entry . + * @tc.expected: step3. Expect return E_OK. + */ + Entry entry; + EXPECT_EQ(resultWindow.GetEntry(entry), E_OK); + EXPECT_EQ(entry.key, LOCAL_KEY_1); + EXPECT_EQ(entry.value, VALUE_1); +} + +/** + * @tc.name: SingleVersionResultSetTest013 + * @tc.desc: CursorWindow Class: Check if current position after move is at the right place+. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest013, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 1 and resultWindow be null pointer. + * @tc.expected: step1. Expect return OK. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), E_OK); + + /** + * @tc.steps:step2. move to second position. + * @tc.expected: step2. Expect return SECOND_POSITION (which is 2). + */ + EXPECT_EQ(resultWindow.MoveToPosition(SECOND_POSITION), true); + EXPECT_EQ(resultWindow.GetCurrentPosition(), SECOND_POSITION); + + /** + * @tc.steps:step3. Get entry . + * @tc.expected: step3. Expect return OK and entry corresponds to the right item. + */ + Entry entry; + EXPECT_EQ(resultWindow.GetEntry(entry), E_OK); + EXPECT_EQ(entry.key, LOCAL_KEY_2); + EXPECT_EQ(entry.value, VALUE_2); +} + +/** + * @tc.name: SingleVersionResultSetTest014 + * @tc.desc: CursorWindow Class: Move to negative position and the position bounces back to zero. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest014, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 1 and resultWindow be null pointer. + * @tc.expected: step1. Expect return OK. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), E_OK); + + /** + * @tc.steps:step2. move to second position. + * @tc.expected: step2. Expect return false and initial position. + */ + int negativePosition = -2; + EXPECT_EQ(resultWindow.MoveToPosition(negativePosition), false); + EXPECT_EQ(resultWindow.GetCurrentPosition(), INITIAL_POSITION); + + /** + * @tc.steps:step3. Get entry . + * @tc.expected: step3. Expect return E_OK. + */ + Entry entry; + EXPECT_EQ(resultWindow.GetEntry(entry), E_OK); + EXPECT_EQ(entry.key, LOCAL_KEY_1); + EXPECT_EQ(entry.value, VALUE_1); +} + +/** + * @tc.name: SingleVersionResultSetTest015 + * @tc.desc: CursorWindow Class: Move to position larger than N and the position bounces back to original position. + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBInterfacesSingleVersionResultSetTest, SingleVersionResultSetTest015, TestSize.Level1) +{ + /** + * @tc.steps:step1. Let the WindowSize be 100, and window scale to be 1 and resultWindow be null pointer. + * @tc.expected: step1. Expect return OK. + */ + ResultEntriesWindow resultWindow; + double scale = 1; + int windowSize = 100; + EXPECT_EQ(resultWindow.Init(g_rawCursor, windowSize, scale), E_OK); + + /** + * @tc.steps:step2. move to second position. + * @tc.expected: step2. Expect return false and move to total count. + */ + int largePosition = TOTAL_COUNT + 1; + EXPECT_EQ(resultWindow.MoveToPosition(largePosition), false); + EXPECT_EQ(resultWindow.GetCurrentPosition(), INITIAL_POSITION); + + /** + * @tc.steps:step3. Get entry . + * @tc.expected: step3. Expect return VALUE_1. + */ + Entry entry; + EXPECT_EQ(resultWindow.GetEntry(entry), E_OK); + EXPECT_EQ(entry.key, LOCAL_KEY_1); + EXPECT_EQ(entry.value, VALUE_1); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_space_management_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_space_management_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a837aded51ff49a4e32edf9b67c44a849841d2d --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_space_management_test.cpp @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "db_constant.h" +#include "db_common.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + string g_testDir; + KvStoreConfig g_config; + + // define the g_kvNbDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + KvStoreNbDelegate::Option g_nbOption; + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + std::string g_storeId; + std::string g_identifier; + vector g_singleVerFileNames; + vector g_commitLogFileNames; + vector g_metaStorageFileNames; + vector g_multiVerDataFileNames; + vector g_ValueStorageFileNames; + + void GetRealFileUrl() + { + std::string origIdentifier = USER_ID + "-" + APP_ID + "-" + g_storeId; + std::string hashIdentifier = DBCommon::TransferHashString(origIdentifier); + g_identifier = DBCommon::TransferStringToHex(hashIdentifier); + + g_singleVerFileNames = { + g_testDir + "/" + g_identifier + "/single_ver/main/gen_natural_store.db", + g_testDir + "/" + g_identifier + "/single_ver/main/gen_natural_store.db-shm", + g_testDir + "/" + g_identifier + "/single_ver/main/gen_natural_store.db-wal"}; + g_commitLogFileNames = { + g_testDir + "/" + g_identifier + "/multi_ver/commit_logs.db", + g_testDir + "/" + g_identifier + "/multi_ver/commit_logs.db-shm", + g_testDir + "/" + g_identifier + "/multi_ver/commit_logs.db-wal"}; + g_metaStorageFileNames = { + g_testDir + "/" + g_identifier + "/multi_ver/meta_storage.db", + g_testDir + "/" + g_identifier + "/multi_ver/meta_storage.db-shm", + g_testDir + "/" + g_identifier + "/multi_ver/meta_storage.db-wal"}; + g_multiVerDataFileNames = { + g_testDir + "/" + g_identifier + "/multi_ver/multi_ver_data.db", + g_testDir + "/" + g_identifier + "/multi_ver/multi_ver_data.db-shm", + g_testDir + "/" + g_identifier + "/multi_ver/multi_ver_data.db-wal"}; + g_ValueStorageFileNames = { + g_testDir + "/" + g_identifier + "/multi_ver/value_storage.db", + g_testDir + "/" + g_identifier + "/multi_ver/value_storage.db-shm", + g_testDir + "/" + g_identifier + "/multi_ver/value_storage.db-wal"}; + } + + vector GetMultiVerFilelist() + { + vector multiFileNames; + for (const auto &iter : g_commitLogFileNames) { + multiFileNames.push_back(iter); + } + for (const auto &iter : g_metaStorageFileNames) { + multiFileNames.push_back(iter); + } + for (const auto &iter : g_multiVerDataFileNames) { + multiFileNames.push_back(iter); + } + for (const auto &iter : g_ValueStorageFileNames) { + multiFileNames.push_back(iter); + } + return multiFileNames; + } +} + +class DistributedDBInterfacesSpaceManagementTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesSpaceManagementTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesSpaceManagementTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesSpaceManagementTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvNbDelegatePtr = nullptr; + g_kvDelegatePtr = nullptr; +} + +void DistributedDBInterfacesSpaceManagementTest::TearDown(void) {} + +// use another way calculate small file size(2G) +static uint64_t CheckRealFileSize(const vector &fileNames) +{ + uint64_t size = 0; + for (const auto &file : fileNames) { + FILE *fileHandle = nullptr; + fileHandle = fopen(file.c_str(), "rb"); + if (fileHandle == nullptr) { + LOGE("Open file[%s] fail[%d]", file.c_str(), errno); + continue; + } + (void)fseek(fileHandle, 0, SEEK_END); + long fileSize = ftell(fileHandle); + LOGD("CheckRealFileSize:FileName[%s],size[%ld]", file.c_str(), fileSize); + size += static_cast(fileSize); // file is less than 16M. + (void)fclose(fileHandle); + } + return size; +} + +/** + * @tc.name: GetKvStoreDiskSize001 + * @tc.desc: ROM space occupied by applications in the distributed database can be calculated. + * @tc.type: FUNC + * @tc.require: AR000CQDTD + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesSpaceManagementTest, GetKvStoreDiskSize001, TestSize.Level1) +{ + g_storeId = "distributed_GetKvStoreDiskSize_001"; + GetRealFileUrl(); + + g_mgr.GetKvStore(g_storeId, g_nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step1/2. Get Db size by GetKvStoreDiskSize. + * @tc.expected: step1/2. Return right size and ok. + */ + uint64_t localDbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(g_storeId, localDbSize), OK); + EXPECT_EQ(CheckRealFileSize(g_singleVerFileNames), localDbSize); + + /** + * @tc.steps: step3. Reopen Db. + */ + g_mgr.GetKvStore(g_storeId, g_nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step4. Put some Key Value to change Db size. + */ + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key, DBConstant::MAX_KEY_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(value, DBConstant::MAX_VALUE_SIZE); + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), OK); + + /** + * @tc.steps: step5/6. Get Db size by GetKvStoreDiskSize. + * @tc.expected: step5/6. Return right size and ok. + */ + localDbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(g_storeId, localDbSize), OK); + EXPECT_EQ(CheckRealFileSize(g_singleVerFileNames), localDbSize); + + /** + * @tc.steps: step7. Close and Delete Db. + * @tc.expected: step7. Successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(g_storeId), OK); +} + +/** + * @tc.name: GetKvStoreDiskSize002 + * @tc.desc: Obtain the size of the opened database. + * @tc.type: FUNC + * @tc.require: AR000CQDTD + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesSpaceManagementTest, GetKvStoreDiskSize002, TestSize.Level2) +{ + g_storeId = "distributed_GetKvStoreDiskSize_002"; + GetRealFileUrl(); + + KvStoreDelegate::Option option; + g_mgr.GetKvStore(g_storeId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + g_mgr.GetKvStore(g_storeId, g_nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step1/2. Get Db size by GetKvStoreDiskSize. + * @tc.expected: step1/2. Return right size and ok. + */ + uint64_t singleAndMultiDbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(g_storeId, singleAndMultiDbSize), OK); + uint64_t dbSizeForCheck = CheckRealFileSize(g_singleVerFileNames) + CheckRealFileSize(GetMultiVerFilelist()); + EXPECT_EQ(dbSizeForCheck, singleAndMultiDbSize); + + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key, DBConstant::MAX_KEY_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(value, DBConstant::MAX_VALUE_SIZE); + + EXPECT_EQ(g_kvNbDelegatePtr->Put(key, value), OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step3/4. Reopen Db and Put some Key Value to change Db size. + */ + g_mgr.GetKvStore(g_storeId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_kvDelegatePtr->Put(key, value), OK); + + /** + * @tc.steps: step5/6. Get Db size by GetKvStoreDiskSize. + * @tc.expected: step5/6. Return right size and ok. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // for vacuum + singleAndMultiDbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(g_storeId, singleAndMultiDbSize), OK); + ASSERT_TRUE(dbSizeForCheck != singleAndMultiDbSize); + dbSizeForCheck = CheckRealFileSize(g_singleVerFileNames) + CheckRealFileSize(GetMultiVerFilelist()); + EXPECT_EQ(dbSizeForCheck, singleAndMultiDbSize); + LOGE("single:%" PRIu64 ",mul:%" PRIu64, CheckRealFileSize(g_singleVerFileNames), + CheckRealFileSize(GetMultiVerFilelist())); + + /** + * @tc.steps: step7. Close and Delete Db. + * @tc.expected: step7. Successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(g_storeId), OK); +} + +// The file will be deleted after the test, it only for test, no security impact on permissions +static void CreateFile(const std::string &fileUrl, uint64_t fileSize) +{ + ofstream mcfile; + mcfile.open(fileUrl); + if (!mcfile.is_open()) { + return; + } + std::string fileContext; + fileContext.resize(fileSize, 'X'); + mcfile << fileContext; + mcfile.close(); + return; +} + +static void DeleteFile(const std::string &fileUrl) +{ + std::ifstream walFile(fileUrl); + if (walFile) { + int result = remove(fileUrl.c_str()); + if (result < 0) { + LOGE("failed to delete the file[%s]:%d", fileUrl.c_str(), errno); + } + } + return; +} + +/** + * @tc.name: GetKvStoreDiskSize003 + * @tc.desc: Verification exception parameters + * @tc.type: FUNC + * @tc.require: AR000CQDTD + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesSpaceManagementTest, GetKvStoreDiskSize003, TestSize.Level1) +{ + g_storeId = "distributed_GetKvStoreDiskSize_003"; + GetRealFileUrl(); + KvStoreNbDelegate::Option nbOption; + g_mgr.GetKvStore(g_storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + KvStoreDelegate::Option option; + g_mgr.GetKvStore(g_storeId, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step1. Use an anomalous length of storeId by GetKvStoreDiskSize to get size. + * @tc.expected: step1. Return 0 size and INVALID_ARGS. + */ + uint64_t dbSize = 0; + std::string exceptStoreId; + exceptStoreId.clear(); + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(exceptStoreId, dbSize), INVALID_ARGS); + EXPECT_EQ(dbSize, 0ull); + + exceptStoreId.resize(129, 'X'); + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(exceptStoreId, dbSize), INVALID_ARGS); + EXPECT_EQ(dbSize, 0ull); + + /** + * @tc.steps: step2. Use a valid but not exist storeId to GetKvStoreDiskSize. + * @tc.expected: step2. Return 0 size and NOT_FOUND. + */ + exceptStoreId.resize(128, 'X'); + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(exceptStoreId, dbSize), NOT_FOUND); + EXPECT_EQ(dbSize, 0ull); + + /** + * @tc.steps: step3/4. Use right storeId to GetKvStoreDiskSize. + * @tc.expected: step3/4. Return right size and OK. + */ + uint64_t singleAndMultiDbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(g_storeId, singleAndMultiDbSize), OK); + uint64_t dbSizeForCheck = CheckRealFileSize(g_singleVerFileNames) + CheckRealFileSize(GetMultiVerFilelist()); + EXPECT_EQ(dbSizeForCheck, singleAndMultiDbSize); + + /** + * @tc.steps: step5. Create irrelevant files. + */ + CreateFile(g_testDir + "/" + g_storeId + "/" + DBConstant::MULTI_SUB_DIR + "/test.txt", 1024 * 1024); + + /** + * @tc.steps: step6/7/8. Get Db size by GetKvStoreDiskSize. + * @tc.expected: step6/7/8. Return right size and ok. + */ + singleAndMultiDbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(g_storeId, singleAndMultiDbSize), OK); + EXPECT_EQ(dbSizeForCheck, singleAndMultiDbSize); + + DeleteFile(g_testDir + "/" + g_storeId + "/" + DBConstant::MULTI_SUB_DIR + "/test.txt"); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(g_storeId), OK); +} + +/** + * @tc.name: GetKvStoreDiskSize004 + * @tc.desc: Calculate memory database size + * @tc.type: FUNC + * @tc.require: AR000CQDTD + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesSpaceManagementTest, GetKvStoreDiskSize004, TestSize.Level1) +{ + g_storeId = "distributed_GetKvStoreDiskSize_004"; + GetRealFileUrl(); + + KvStoreNbDelegate::Option nbOption; + g_mgr.GetKvStore(g_storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + uint64_t singleVerRealSize = CheckRealFileSize(g_singleVerFileNames); + + /** + * @tc.steps: step1/2. Get Db size by GetKvStoreDiskSize. + * @tc.expected: step1/2. Return right size and ok. + */ + uint64_t singleVerDbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(g_storeId, singleVerDbSize), OK); + EXPECT_EQ(singleVerDbSize, singleVerRealSize); + + /** + * @tc.steps: step3. Use the same storeId create memoryDb. + */ + nbOption = {true, true}; + g_mgr.GetKvStore(g_storeId, nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps: step4/5. Get Db size by GetKvStoreDiskSize. + * @tc.expected: step4/5. Return 0 size and ok. + */ + singleVerDbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(g_storeId, singleVerDbSize), OK); + EXPECT_EQ(singleVerDbSize, 0ull); + + /** + * @tc.steps: step6. Close memoryDb. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + /** + * @tc.steps: step7. Get Db size by GetKvStoreDiskSize. + * @tc.expected: step7. Return right size and ok. + */ + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(g_storeId, singleVerDbSize), OK); + EXPECT_EQ(singleVerDbSize, singleVerRealSize); + + EXPECT_EQ(g_mgr.DeleteKvStore(g_storeId), OK); +} + +/** + * @tc.name: DeleteDbByStoreId001 + * @tc.desc: Delete database by storeId. + * @tc.type: FUNC + * @tc.require: AR000CQDTD + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesSpaceManagementTest, DeleteDbByStoreId001, TestSize.Level1) +{ + std::string storeId1 = "distributed_DeleteDbByStoreId001"; + KvStoreNbDelegate::Option nbOption; + g_mgr.GetKvStore(storeId1, nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + KvStoreDelegate::Option option; + g_mgr.GetKvStore(storeId1, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + option.localOnly = true; + g_mgr.GetKvStore(storeId1, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + std::string storeId2 = "distributed_DeleteDbByStoreId002"; + + g_mgr.GetKvStore(storeId2, nbOption, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + option.localOnly = false; + g_mgr.GetKvStore(storeId2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + option.localOnly = true; + g_mgr.GetKvStore(storeId2, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + + uint64_t store1DbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(storeId1, store1DbSize), OK); + EXPECT_NE(store1DbSize, 0ull); + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(storeId2, store1DbSize), OK); + EXPECT_NE(store1DbSize, 0ull); + + /** + * @tc.steps: step1. Delete database by storeId 1. + */ + EXPECT_EQ(g_mgr.DeleteKvStore(storeId1), OK); + + /** + * @tc.steps: step2. Use storeId 1 to get Db size by GetKvStoreDiskSize. + * @tc.expected: step2. Return 0 size and ok. + */ + store1DbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(storeId1, store1DbSize), NOT_FOUND); + EXPECT_EQ(store1DbSize, 0ull); + + /** + * @tc.steps: step3. Use storeId 2 to get Db size by GetKvStoreDiskSize. + * @tc.expected: step3. Return right size and ok. + */ + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(storeId2, store1DbSize), OK); + EXPECT_NE(store1DbSize, 0ull); +} + +/** + * @tc.name: DeleteDbByStoreId002 + * @tc.desc: Delete database by not exist storeId. + * @tc.type: FUNC + * @tc.require: AR000CQDTD + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesSpaceManagementTest, DeleteDbByStoreId002, TestSize.Level1) +{ + std::string storeId1 = "distributed_DeleteDbByStoreId001"; + + uint64_t store1DbSize = 0; + EXPECT_EQ(g_mgr.GetKvStoreDiskSize(storeId1, store1DbSize), NOT_FOUND); + EXPECT_EQ(store1DbSize, 0ull); + + /** + * @tc.steps: step1. Delete database by not exist storeId 1. + * @tc.expected: step3. Return NOT_FOUND. + */ + EXPECT_EQ(g_mgr.DeleteKvStore(storeId1), NOT_FOUND); +} diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_optimization_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_optimization_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f8e6078b7cfa09750fcca3da4580eda28fbd4f4 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_optimization_test.cpp @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + KvStoreConfig g_config; + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + + const int OBSERVER_SLEEP_TIME = 100; + const int BATCH_PRESET_SIZE_TEST = 10; + const int DIVIDE_BATCH_PRESET_SIZE = 5; + + const Key KEY1{'k', 'e', 'y', '1'}; + const Key KEY2{'k', 'e', 'y', '2'}; + const Value VALUE1{'v', 'a', 'l', 'u', 'e', '1'}; + const Value VALUE2{'v', 'a', 'l', 'u', 'e', '2'}; + + // the type of g_kvNbDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); +} + +class DistributedDBInterfacesTransactionOptimizationTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesTransactionOptimizationTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesTransactionOptimizationTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesTransactionOptimizationTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_kvDelegateStatus = INVALID_ARGS; + g_kvNbDelegatePtr = nullptr; +} + +void DistributedDBInterfacesTransactionOptimizationTest::TearDown(void) +{ + if (g_kvNbDelegatePtr != nullptr) { + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_kvNbDelegatePtr = nullptr; + } +} + +/** + * @tc.name: BatchOperationsOfSyncAndLocal001 + * @tc.desc: Verify the batch put and query functions of the sync and local data in the same transaction. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesTransactionOptimizationTest, SyncAndLocalBatchOperations001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + std::string storeId("SyncAndLocalBatchOperations001"); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Starting a transaction. + * @tc.expected: step2. The transaction is started successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + vector entries; + vector keys; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entries, keys); + EXPECT_TRUE(entries.size() == BATCH_PRESET_SIZE_TEST); + + vector localEntrys; + vector localKeys; + DistributedDBUnitTest::GenerateRecords(DIVIDE_BATCH_PRESET_SIZE, localEntrys, localKeys); + EXPECT_TRUE(localEntrys.size() == DIVIDE_BATCH_PRESET_SIZE); + + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entries), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(localEntrys), OK); + + Key keyPrefix; + std::vector getSyncEntries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(keyPrefix, getSyncEntries), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(entries, getSyncEntries, true)); + + std::vector getLocalEntries; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries(keyPrefix, getLocalEntries), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(localEntrys, getLocalEntries, true)); + + /** + * @tc.steps:step4. Commit a transaction. + * @tc.expected: step4. Transaction submitted successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + + /** + * @tc.steps:step5. GetEntries after the transaction is submitted. + * @tc.expected: step5. GetEntries return OK and the geted data is correct. + */ + getSyncEntries.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(keyPrefix, getSyncEntries), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(entries, getSyncEntries, true)); + + getLocalEntries.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries(keyPrefix, getLocalEntries), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsEntriesEqual(localEntrys, getLocalEntries, true)); + + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SyncAndLocalSingleOperations001 + * @tc.desc: Verify the single put and query functions of the sync and local data in the same transaction. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesTransactionOptimizationTest, SyncAndLocalSingleOperations001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + std::string storeId("SyncAndLocalSingleOperations001"); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Starting a transaction. + * @tc.expected: step2. The transaction is started successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + /** + * @tc.steps:step3. Put and Get single data. + * @tc.expected: step3. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY1, VALUE1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY2, VALUE2), OK); + + Value getSyncValue; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY1, getSyncValue), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsValueEqual(VALUE1, getSyncValue)); + + Value getLocalValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY2, getLocalValue), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsValueEqual(VALUE2, getLocalValue)); + + /** + * @tc.steps:step4. Commit a transaction. + * @tc.expected: step4. Transaction submitted successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + + /** + * @tc.steps:step5. Get after the transaction is submitted. + * @tc.expected: step5. Get return OK and the geted data is correct. + */ + getSyncValue.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY1, getSyncValue), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsValueEqual(VALUE1, getSyncValue)); + + getLocalValue.clear(); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY2, getLocalValue), OK); + EXPECT_TRUE(DistributedDBToolsUnitTest::IsValueEqual(VALUE2, getLocalValue)); + + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: DeleteInTransaction001 + * @tc.desc: Verify that the sync and local functions can be deleted in the same transaction. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesTransactionOptimizationTest, DeleteInTransaction001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + std::string storeId("DeleteInTransaction001"); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Starting a transaction. + * @tc.expected: step2. The transaction is started successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + /** + * @tc.steps:step3. Put and Get single data. + * @tc.expected: step3. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY1, VALUE1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY2, VALUE2), OK); + + /** + * @tc.steps:step4 Delete before the transaction is submitted. + * @tc.expected: step4. Delete return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Delete(KEY1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocal(KEY2), OK); + + /** + * @tc.steps:step5 Commit a transaction. + * @tc.expected: step5 Transaction submitted successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + + /** + * @tc.steps:step6 Get after the transaction is submitted. + * @tc.expected: step6 Get return NOT_FOUND and the geted data is correct. + */ + Value getSyncValue; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY1, getSyncValue), NOT_FOUND); + Value getLocalValue; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY2, getLocalValue), NOT_FOUND); + + /** + * @tc.steps:step7 Close the kv store. + * @tc.expected: step7 Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: DeleteBatchInTransaction001 + * @tc.desc: Local data does not check readOnly. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesTransactionOptimizationTest, DeleteBatchInTransaction001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + std::string storeId("DeleteBatchInTransaction001"); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Starting a Transaction. + * @tc.expected: step2. The transaction is started successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + vector entries; + vector keys; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, entries, keys); + EXPECT_TRUE(entries.size() == BATCH_PRESET_SIZE_TEST); + + vector localEntrys; + vector localKeys; + DistributedDBUnitTest::GenerateRecords(DIVIDE_BATCH_PRESET_SIZE, localEntrys, localKeys); + EXPECT_TRUE(localEntrys.size() == DIVIDE_BATCH_PRESET_SIZE); + + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(entries), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(localEntrys), OK); + + /** + * @tc.steps:step4 DeleteBatch before the transaction is submitted. + * @tc.expected: step4. Delete return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(keys), OK); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(localKeys), OK); + + /** + * @tc.steps:step5 Commit a transaction. + * @tc.expected: step5 Transaction submitted successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + + /** + * @tc.steps:step6 GetEntries after the transaction is submitted. + * @tc.expected: step6 GetEntries return NOT_FOUND and the geted data is correct. + */ + Key keyPrefix; + std::vector getSyncEntries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(keyPrefix, getSyncEntries), NOT_FOUND); + std::vector getLocalEntries; + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries(keyPrefix, getLocalEntries), NOT_FOUND); + + /** + * @tc.steps:step7 Close the kv store. + * @tc.expected: step7 Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SyncAndLocalObserver001 + * @tc.desc: Verify the observer functions of the sync and local data in the same transaction. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesTransactionOptimizationTest, SyncAndLocalObserver001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + std::string storeId("SyncAndLocalObserver001"); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *syncObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(syncObserver != nullptr); + KvStoreObserverUnitTest *localObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(localObserver != nullptr); + + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, syncObserver), OK); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, localObserver), OK); + + /** + * @tc.steps:step3. Starting a Transaction. + * @tc.expected: step3. The transaction is started successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + /** + * @tc.steps:step4. Put batch data. + * @tc.expected: step4. Returns OK. + */ + vector syncKeys; + vector syncEntries; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, syncEntries, syncKeys); + EXPECT_TRUE(syncEntries.size() == BATCH_PRESET_SIZE_TEST); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(syncEntries), OK); + + vector localKeys; + vector localEntries; + DistributedDBUnitTest::GenerateRecords(DIVIDE_BATCH_PRESET_SIZE, localEntries, localKeys); + EXPECT_TRUE(localEntries.size() == DIVIDE_BATCH_PRESET_SIZE); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(localEntries), OK); + + /** + * @tc.steps:step5. Commit a transaction. + * @tc.expected: step5. Transaction submitted successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + + /** + * @tc.steps:step6. Check changed data. + * @tc.expected: step6. The inserted data is the same as the written data. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(syncEntries, syncObserver->GetEntriesInserted())); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(localEntries, localObserver->GetEntriesInserted())); + + /** + * @tc.steps:step7. UnRegister the observer. + * @tc.expected: step7. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(syncObserver), OK); + delete syncObserver; + syncObserver = nullptr; + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(localObserver), OK); + delete localObserver; + localObserver = nullptr; + + /** + * @tc.steps:step8. Close the kv store. + * @tc.expected: step8. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: OnlyDeleteInTransaction001 + * @tc.desc: Verify the observer functions of delete operation in the transaction. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesTransactionOptimizationTest, OnlyDeleteInTransaction001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + std::string storeId("OnlyDeleteInTransaction001"); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + /** + * @tc.steps:step2. Put batch data. + * @tc.expected: step2. Returns OK. + */ + vector syncKeys; + vector syncEntries; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, syncEntries, syncKeys); + EXPECT_TRUE(syncEntries.size() == BATCH_PRESET_SIZE_TEST); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(syncEntries), OK); + + vector localKeys; + vector localEntries; + DistributedDBUnitTest::GenerateRecords(DIVIDE_BATCH_PRESET_SIZE, localEntries, localKeys); + EXPECT_TRUE(localEntries.size() == DIVIDE_BATCH_PRESET_SIZE); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(localEntries), OK); + + KvStoreObserverUnitTest *syncObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(syncObserver != nullptr); + KvStoreObserverUnitTest *localObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(localObserver != nullptr); + + /** + * @tc.steps:step3. Register the non-null observer for the special key. + * @tc.expected: step3. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, syncObserver), OK); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, localObserver), OK); + + /** + * @tc.steps:step4. Starting a Transaction. + * @tc.expected: step4. The transaction is started successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + /** + * @tc.steps:step5 DeleteBatch before the transaction is submitted. + * @tc.expected: step5. Delete return OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->DeleteBatch(syncKeys), OK); + EXPECT_EQ(g_kvNbDelegatePtr->DeleteLocalBatch(localKeys), OK); + + /** + * @tc.steps:step6. Commit a transaction. + * @tc.expected: step6. Transaction submitted successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + + /** + * @tc.steps:step7. Check changed data. + * @tc.expected: step7. The inserted data is the same as the written data. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(syncEntries, syncObserver->GetEntriesDeleted())); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(localEntries, localObserver->GetEntriesDeleted())); + + /** + * @tc.steps:step8. UnRegister the observer. + * @tc.expected: step8. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(syncObserver), OK); + delete syncObserver; + syncObserver = nullptr; + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(localObserver), OK); + delete localObserver; + localObserver = nullptr; + + /** + * @tc.steps:step9. Close the kv store. + * @tc.expected: step9. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: SyncAndLocalObserver002 + * @tc.desc: Verify the observer functions of the sync and local data in the same transaction. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesTransactionOptimizationTest, SyncAndLocalObserver002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + std::string storeId("SyncAndLocalObserver002"); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *syncObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(syncObserver != nullptr); + KvStoreObserverUnitTest *localObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(localObserver != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, syncObserver), OK); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, localObserver), OK); + + /** + * @tc.steps:step3. Starting a Transaction. + * @tc.expected: step3. The transaction is started successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + /** + * @tc.steps:step4. Put data. + * @tc.expected: step4. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY1, VALUE1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY2, VALUE2), OK); + + /** + * @tc.steps:step5. Commit a transaction. + * @tc.expected: step5. Transaction submitted successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Commit(), OK); + + std::vector syncEntries; + Entry syncEntry{KEY1, VALUE1}; + syncEntries.emplace_back(syncEntry); + std::vector localEntries; + Entry localEntry{KEY2, VALUE2}; + localEntries.emplace_back(localEntry); + + /** + * @tc.steps:step6. Check changed data. + * @tc.expected: step6. The inserted data is the same as the written data. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(syncEntries, syncObserver->GetEntriesInserted())); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(localEntries, localObserver->GetEntriesInserted())); + + /** + * @tc.steps:step7. UnRegister the observer. + * @tc.expected: step7. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(syncObserver), OK); + delete syncObserver; + syncObserver = nullptr; + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(localObserver), OK); + delete localObserver; + localObserver = nullptr; + + /** + * @tc.steps:step8. Close the kv store. + * @tc.expected: step8. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: PutRollback001 + * @tc.desc: Verify that a transaction can be rolled back after data is put. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesTransactionOptimizationTest, PutRollback001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + std::string storeId("PutRollback001"); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *syncObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(syncObserver != nullptr); + KvStoreObserverUnitTest *localObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(localObserver != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, syncObserver), OK); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, localObserver), OK); + + /** + * @tc.steps:step3. Starting a Transaction. + * @tc.expected: step3. The transaction is started successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + /** + * @tc.steps:step3. Put data. + * @tc.expected: step3. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY1, VALUE1), OK); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocal(KEY2, VALUE2), OK); + + /** + * @tc.steps:step3. Transaction rollback. + * @tc.expected: step3. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + + /** + * @tc.steps:step4. After the rollback, query the database and observe the changed data. + * @tc.expected: step4. Get return NOT_FOUND. The changed data is empty. + */ + Value value; + EXPECT_EQ(g_kvNbDelegatePtr->Get(KEY1, value), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocal(KEY2, value), NOT_FOUND); + + std::vector empty; + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(empty, syncObserver->GetEntriesInserted())); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(empty, localObserver->GetEntriesInserted())); + + /** + * @tc.steps:step5. UnRegister the observer. + * @tc.expected: step5. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(syncObserver), OK); + delete syncObserver; + syncObserver = nullptr; + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(localObserver), OK); + delete localObserver; + localObserver = nullptr; + + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + g_kvNbDelegatePtr = nullptr; +} + +/** + * @tc.name: PutBatchRollback001 + * @tc.desc: Verify that a transaction can be rolled back after data is put. + * @tc.type: FUNC + * @tc.require: AR000EPAS8 + * @tc.author: changguicai + */ +HWTEST_F(DistributedDBInterfacesTransactionOptimizationTest, PutBatchRollback001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + std::string storeId("OptimizeObserver008"); + KvStoreNbDelegate::Option option = {true, false, false}; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + KvStoreObserverUnitTest *syncObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(syncObserver != nullptr); + KvStoreObserverUnitTest *localObserver = new (std::nothrow) KvStoreObserverUnitTest; + ASSERT_TRUE(localObserver != nullptr); + /** + * @tc.steps:step2. Register the non-null observer for the special key. + * @tc.expected: step2. Register results OK. + */ + Key key; + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_NATIVE, syncObserver), OK); + EXPECT_EQ(g_kvNbDelegatePtr->RegisterObserver(key, OBSERVER_CHANGES_LOCAL_ONLY, localObserver), OK); + + /** + * @tc.steps:step3. Starting a Transaction. + * @tc.expected: step3. The transaction is started successfully. + */ + EXPECT_EQ(g_kvNbDelegatePtr->StartTransaction(), OK); + + /** + * @tc.steps:step3. Put batch data. + * @tc.expected: step3. Returns OK. + */ + std::vector syncKeys; + std::vector syncEntries; + DistributedDBUnitTest::GenerateRecords(BATCH_PRESET_SIZE_TEST, syncEntries, syncKeys); + EXPECT_TRUE(syncEntries.size() == BATCH_PRESET_SIZE_TEST); + EXPECT_EQ(g_kvNbDelegatePtr->PutBatch(syncEntries), OK); + + std::vector localKeys; + std::vector localEntries; + DistributedDBUnitTest::GenerateRecords(DIVIDE_BATCH_PRESET_SIZE, localEntries, localKeys); + EXPECT_TRUE(localEntries.size() == DIVIDE_BATCH_PRESET_SIZE); + EXPECT_EQ(g_kvNbDelegatePtr->PutLocalBatch(localEntries), OK); + + /** + * @tc.steps:step3. Transaction rollback. + * @tc.expected: step3. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->Rollback(), OK); + + /** + * @tc.steps:step4. After the rollback, query the database and observe the changed data. + * @tc.expected: step4. Get return NOT_FOUND. The changed data is empty. + */ + Key keyPrefix; + std::vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(keyPrefix, entries), NOT_FOUND); + EXPECT_EQ(g_kvNbDelegatePtr->GetLocalEntries(keyPrefix, entries), NOT_FOUND); + + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entries, syncObserver->GetEntriesInserted())); + EXPECT_TRUE(DistributedDBToolsUnitTest::CheckObserverResult(entries, localObserver->GetEntriesInserted())); + + /** + * @tc.steps:step5. UnRegister the observer. + * @tc.expected: step5. Returns OK. + */ + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(syncObserver), OK); + delete syncObserver; + syncObserver = nullptr; + EXPECT_EQ(g_kvNbDelegatePtr->UnRegisterObserver(localObserver), OK); + delete localObserver; + localObserver = nullptr; + + /** + * @tc.steps:step6. Close the kv store. + * @tc.expected: step6. Results OK and delete successfully. + */ + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore(storeId), OK); + g_kvNbDelegatePtr = nullptr; +} + diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_syncdb_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_syncdb_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b8779f8e504e28307a7aac7b121a125ff1988a4 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_syncdb_test.cpp @@ -0,0 +1,643 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_interfaces_transaction_testcase.h" +#include "distributeddb_tools_unit_test.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const bool LOCAL_ONLY = false; + const string STORE_ID = STORE_ID_SYNC; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + // define the g_snapshotDelegateCallback, used to get some information when open a kv snapshot. + DBStatus g_snapshotDelegateStatus = INVALID_ARGS; + KvStoreSnapshotDelegate *g_snapshotDelegatePtr = nullptr; + // the type of g_snapshotDelegateCallback is function + auto g_snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_snapshotDelegateStatus), std::ref(g_snapshotDelegatePtr)); +} + +class DistributedDBInterfacesTransactionSyncDBTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesTransactionSyncDBTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesTransactionSyncDBTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesTransactionSyncDBTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /* + * Here, we create STORE_ID before test, + * and it will be closed in TearDown(). + */ + KvStoreDelegate::Option option = {true, LOCAL_ONLY}; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); +} + +void DistributedDBInterfacesTransactionSyncDBTest::TearDown(void) +{ + if (g_kvDelegatePtr != nullptr && g_snapshotDelegatePtr != nullptr) { + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; + } + + if (g_kvDelegatePtr != nullptr) { + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID), OK); + } +} + +/** + * @tc.name: StartTransaction001 + * @tc.desc: Test that can't call StartTransaction interface repeatedly. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, StartTransaction001, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface the 1st time. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call StartTransaction interface the 2nd time. + * @tc.expected: step2. call failed and return ERROR. + */ + DistributedDBInterfacesTransactionTestCase::StartTransaction001(g_kvDelegatePtr); +} + +/** + * @tc.name: StartTransaction002 + * @tc.desc: Test that call StartTransaction and commit interface normally. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, StartTransaction002, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call commit interface. + * @tc.expected: step2. call succeed. + */ + DistributedDBInterfacesTransactionTestCase::StartTransaction002(g_kvDelegatePtr); +} + +/** + * @tc.name: StartTransaction003 + * @tc.desc: Test that call StartTransaction and rolback interface normally. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, StartTransaction003, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call rollback interface. + * @tc.expected: step2. call succeed. + */ + DistributedDBInterfacesTransactionTestCase::StartTransaction003(g_kvDelegatePtr); +} + +/** + * @tc.name: StartTransaction004 + * @tc.desc: Test that call StartTransaction and rolback interface normally. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, StartTransaction004, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. put (k1, v1) to data base. + * @tc.expected: step2. put succeed. + */ + /** + * @tc.steps:step3. close data base. + * @tc.expected: step3. close succeed. + */ + /** + * @tc.steps:step4. use GetKvStore interface to open db. + * @tc.expected: step4. open succeed. + */ + /** + * @tc.steps:step5. use snapshot interface to check the value of k1. + * @tc.expected: step5. can't get the record of k1. + */ + DistributedDBInterfacesTransactionTestCase::StartTransaction004(g_kvDelegatePtr, STORE_ID, LOCAL_ONLY, + g_mgr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit001 + * @tc.desc: Test that can't commit Transaction before it start. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, Commit001, TestSize.Level1) +{ + /** + * @tc.steps:step1. commit Transaction without start it. + * @tc.expected: step1. commit failed and returned ERROR. + */ + DistributedDBInterfacesTransactionTestCase::Commit001(g_kvDelegatePtr); +} + +/** + * @tc.name: Commit002 + * @tc.desc: Test that can't commit Transaction repeatedly even if it start normally. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, Commit002, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call commit interface the 1st time. + * @tc.expected: step2. call succeed. + */ + /** + * @tc.steps:step3. call commit interface the 2nd time. + * @tc.expected: step3. call failed and returned ERROR. + */ + DistributedDBInterfacesTransactionTestCase::Commit002(g_kvDelegatePtr); +} + +/** + * @tc.name: Commit003 + * @tc.desc: Test that can commit Transaction after put record. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, Commit003, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. put (k1, v1) to db. + * @tc.expected: step2. put succeed. + */ + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. call succeed. + */ + /** + * @tc.steps:step4. use snapshot interface to check if (k1, v1) is put succeed. + * @tc.expected: step4. can find (k1, v1) from db. + */ + DistributedDBInterfacesTransactionTestCase::Commit003(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit004 + * @tc.desc: Test that can commit Transaction after update record. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, Commit004, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. update value = v2 where key = k1. + * @tc.expected: step2. update succeed. + */ + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. commit succeed. + */ + /** + * @tc.steps:step4. use snapshot interface to check if (k1, v2) is update succeed. + * @tc.expected: step4. the value is v2 where key = k1 in the db. + */ + DistributedDBInterfacesTransactionTestCase::Commit004(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit005 + * @tc.desc: Test that can commit Transaction after delete record. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, Commit005, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. delete record from db where key = k1. + * @tc.expected: step2. delete succeed. + */ + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. commit succeed. + */ + /** + * @tc.steps:step4. use snapshot interface to check if (k1, v1) is delete succeed. + * @tc.expected: step4. can't find (k1, v1) in the db. + */ + DistributedDBInterfacesTransactionTestCase::Commit005(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit006 + * @tc.desc: Test that can commit Transaction after clear all the records. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, Commit006, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. clear all the records from db. + * @tc.expected: step2. clear succeed. + */ + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. commit succeed. + */ + /** + * @tc.steps:step4. use snapshot interface to check if there are any data in db. + * @tc.expected: step4. can't find any data in db. + */ + DistributedDBInterfacesTransactionTestCase::Commit006(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit007 + * @tc.desc: Test that can commit Transaction after delete and update db. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, Commit007, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. delete record from db where key = k1. + * @tc.expected: step2. delete succeed. + */ + /** + * @tc.steps:step3. put (k2, v1) to db. + * @tc.expected: step3. put succeed. + */ + /** + * @tc.steps:step4. call commit interface. + * @tc.expected: step4. commit succeed. + */ + /** + * @tc.steps:step5. use snapshot interface to check the data in db. + * @tc.expected: step5. can't find (k1, v1) but can find (k2, v1) in db. + */ + DistributedDBInterfacesTransactionTestCase::Commit007(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit008 + * @tc.desc: Test that can commit Transaction after clear and new add records. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, Commit008, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. clear all the records from db. + * @tc.expected: step2. clear succeed. + */ + /** + * @tc.steps:step3. put (k3, v3) to db. + * @tc.expected: step3. put succeed. + */ + /** + * @tc.steps:step4. call commit interface. + * @tc.expected: step4. commit succeed. + */ + /** + * @tc.steps:step5. use snapshot interface to check the data in db. + * @tc.expected: step5. can only find (k3, v3) in db. + */ + DistributedDBInterfacesTransactionTestCase::Commit008(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack001 + * @tc.desc: Test if new commit records and logs generated + * when a transaction rollback-ed + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, RollBack001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test g_kvDelegatePtr->Rollback + * @tc.expected: step1. Return ERROR. + */ + DistributedDBInterfacesTransactionTestCase::RollBack001(g_kvDelegatePtr); +} + +/** +* @tc.name: RollBack002 +* @tc.desc: rollback a transaction two times +* @tc.type: FUNC +* @tc.require: AR000BVRNM AR000CQDTQ +* @tc.author: huangnaigu +*/ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, RollBack002, TestSize.Level1) +{ + /** + * @tc.steps:step1. start a transaction + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. rollback the transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. rollback the transaction the second time + * @tc.expected: step3. Return ERROR. + */ + DistributedDBInterfacesTransactionTestCase::RollBack002(g_kvDelegatePtr); +} + +/** + * @tc.name: RollBack003 + * @tc.desc: insert a data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, RollBack003, TestSize.Level1) +{ + /** + * @tc.steps:step1. start a transaction + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. Put (k1,v1) + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. rollback a transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. check if (k1,v1) exists + * @tc.expected: step4. Return NOT_FOUND. + */ + DistributedDBInterfacesTransactionTestCase::RollBack003(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack004 + * @tc.desc: update a data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, RollBack004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Put (k1,v1) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Update (k1,v1) to (k1,v2) in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. rollback the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. check the value of k1 is v1 + * @tc.expected: step5. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack004(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack005 + * @tc.desc: delete an exist data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, RollBack005, TestSize.Level1) +{ + /** + * @tc.steps:step1. Put (k1,v1) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Delete (k1,v1) in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. rollback the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. check the value of k1 is v1 + * @tc.expected: step5. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack005(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack006 + * @tc.desc: clear db and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, RollBack006, TestSize.Level1) +{ + /** + * @tc.steps:step1. PutBatch records: (k1,v1), (k2,v2) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Clear all records in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. rollback the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. check if there are 2 records in the db + * @tc.expected: step5. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack006(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack007 + * @tc.desc: delete a exist data and update a data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, RollBack007, TestSize.Level1) +{ + /** + * @tc.steps:step1. PutBatch records: (k1,v1), (k2,v2) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Delete (k1,v1) in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. Update (k2,v2) to (k2,v1) in the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. rollback the transaction + * @tc.expected: step5. Return OK. + */ + /** + * @tc.steps:step6. check if (k1,v1),(k2,v2) exist and no more records in the db + * @tc.expected: step6. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack007(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack008 + * @tc.desc: clear db and insert a data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionSyncDBTest, RollBack008, TestSize.Level1) +{ + /** + * @tc.steps:step1. PutBatch records: (k1,v1), (k2,v2) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Clear all records in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. Put (012, ABC) in the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. rollback the transaction + * @tc.expected: step5. Return OK. + */ + /** + * @tc.steps:step6. check if (k1,v1),(k2,v2) exist and no more records in the db + * @tc.expected: step6. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack008(g_kvDelegatePtr, g_snapshotDelegatePtr); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3dc576de19cf709903f52d5307370f87953906d7 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_test.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_interfaces_transaction_testcase.h" +#include "distributeddb_tools_unit_test.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const bool LOCAL_ONLY = true; + const string STORE_ID = STORE_ID_LOCAL; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + // define the g_snapshotDelegateCallback, used to get some information when open a kv snapshot. + DBStatus g_snapshotDelegateStatus = INVALID_ARGS; + KvStoreSnapshotDelegate *g_snapshotDelegatePtr = nullptr; + // the type of g_snapshotDelegateCallback is function + auto g_snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_snapshotDelegateStatus), std::ref(g_snapshotDelegatePtr)); +} + +class DistributedDBInterfacesTransactionTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesTransactionTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); +} + +void DistributedDBInterfacesTransactionTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBInterfacesTransactionTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /* + * Here, we create STORE_ID before test, + * and it will be closed in TearDown(). + */ + KvStoreDelegate::Option option = {true, LOCAL_ONLY}; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); +} + +void DistributedDBInterfacesTransactionTest::TearDown(void) +{ + if (g_kvDelegatePtr != nullptr && g_snapshotDelegatePtr != nullptr) { + EXPECT_TRUE(g_kvDelegatePtr->ReleaseKvStoreSnapshot(g_snapshotDelegatePtr) == OK); + g_snapshotDelegatePtr = nullptr; + } + + if (g_kvDelegatePtr != nullptr) { + EXPECT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + EXPECT_EQ(g_mgr.DeleteKvStore(STORE_ID), OK); + } +} + +/** + * @tc.name: StartTransaction001 + * @tc.desc: Test that can't call StartTransaction interface repeatedly. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, StartTransaction001, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface the 1st time. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call StartTransaction interface the 2nd time. + * @tc.expected: step2. call failed and return ERROR. + */ + DistributedDBInterfacesTransactionTestCase::StartTransaction001(g_kvDelegatePtr); +} + +/** + * @tc.name: StartTransaction002 + * @tc.desc: Test that call StartTransaction and commit interface normally. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, StartTransaction002, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call commit interface. + * @tc.expected: step2. call succeed. + */ + DistributedDBInterfacesTransactionTestCase::StartTransaction002(g_kvDelegatePtr); +} + +/** + * @tc.name: StartTransaction003 + * @tc.desc: Test that call StartTransaction and rolback interface normally. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, StartTransaction003, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call rollback interface. + * @tc.expected: step2. call succeed. + */ + DistributedDBInterfacesTransactionTestCase::StartTransaction003(g_kvDelegatePtr); +} + +/** + * @tc.name: StartTransaction004 + * @tc.desc: Test that call StartTransaction and rolback interface normally. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, StartTransaction004, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. put (k1, v1) to data base. + * @tc.expected: step2. put succeed. + */ + /** + * @tc.steps:step3. close data base. + * @tc.expected: step3. close succeed. + */ + /** + * @tc.steps:step4. use GetKvStore interface to open db. + * @tc.expected: step4. open succeed. + */ + /** + * @tc.steps:step5. use snapshot interface to check the value of k1. + * @tc.expected: step5. can't get the record of k1. + */ + DistributedDBInterfacesTransactionTestCase::StartTransaction004(g_kvDelegatePtr, STORE_ID, LOCAL_ONLY, + g_mgr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: StartTransaction005 + * @tc.desc: Test that can't call StartTransaction interface repeatedly for different kv store. + * @tc.type: FUNC + * @tc.require: AR000BVRNK AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, StartTransaction005, TestSize.Level3) +{ + /** + * @tc.steps:step1. call StartTransaction interface the 1st time. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call StartTransaction interface the 2nd time using another . + * @tc.expected: step2. call failed. + */ + /** + * @tc.steps:step4. call commit interface the 1st time. + * @tc.expected: step4. call failed. + */ + /** + * @tc.steps:step5. call commit interface the 2nd time. + * @tc.expected: step5. call failed. + */ + DistributedDBInterfacesTransactionTestCase::StartTransaction005(g_kvDelegatePtr, STORE_ID, LOCAL_ONLY, g_mgr); +} + +/** + * @tc.name: Commit001 + * @tc.desc: Test that can't commit Transaction before it start. + * @tc.type: FUNC + * @tc.require: AR000CQDTO AR000CQDTP + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Commit001, TestSize.Level1) +{ + /** + * @tc.steps:step1. commit Transaction without start it. + * @tc.expected: step1. commit failed and returned ERROR. + */ + DistributedDBInterfacesTransactionTestCase::Commit001(g_kvDelegatePtr); +} + +/** + * @tc.name: Commit002 + * @tc.desc: Test that can't commit Transaction repeatedly even if it start normally. + * @tc.type: FUNC + * @tc.require: AR000CQDTO AR000CQDTP + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Commit002, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call commit interface the 1st time. + * @tc.expected: step2. call succeed. + */ + /** + * @tc.steps:step3. call commit interface the 2nd time. + * @tc.expected: step3. call failed and returned ERROR. + */ + DistributedDBInterfacesTransactionTestCase::Commit002(g_kvDelegatePtr); +} + +/** + * @tc.name: Commit003 + * @tc.desc: Test that can commit Transaction after put record. + * @tc.type: FUNC + * @tc.require: AR000CQDTO AR000CQDTP + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Commit003, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. put (k1, v1) to db. + * @tc.expected: step2. put succeed. + */ + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. call succeed. + */ + /** + * @tc.steps:step4. use snapshot interface to check if (k1, v1) is put succeed. + * @tc.expected: step4. can find (k1, v1) from db. + */ + DistributedDBInterfacesTransactionTestCase::Commit003(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit004 + * @tc.desc: Test that can commit Transaction after update record. + * @tc.type: FUNC + * @tc.require: AR000CQDTO AR000CQDTP + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Commit004, TestSize.Level1) +{ + /** + * @tc.steps:step1. put one data. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. call StartTransaction interface. + * @tc.expected: step2. call succeed. + */ + /** + * @tc.steps:step3. update the data to another value. + * @tc.expected: step3. call succeed. + */ + /** + * @tc.steps:step4. call commit interface. + * @tc.expected: step4. call succeed. + */ + /** + * @tc.steps:step5. use snapshot interface to check the updated data. + * @tc.expected: step5. the value is updated. + */ + DistributedDBInterfacesTransactionTestCase::Commit004(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit005 + * @tc.desc: Test that can commit Transaction after delete record. + * @tc.type: FUNC + * @tc.require: AR000CQDTO AR000CQDTP + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Commit005, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. delete record from db where key = k1. + * @tc.expected: step2. delete succeed. + */ + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. commit succeed. + */ + /** + * @tc.steps:step4. use snapshot interface to check if (k1, v1) is delete succeed. + * @tc.expected: step4. can't find (k1, v1) in the db. + */ + DistributedDBInterfacesTransactionTestCase::Commit005(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit006 + * @tc.desc: Test that can commit Transaction after clear all the records. + * @tc.type: FUNC + * @tc.require: AR000CQDTO + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Commit006, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. clear all the records from db. + * @tc.expected: step2. clear succeed. + */ + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. commit succeed. + */ + /** + * @tc.steps:step4. use snapshot interface to check if there are any data in db. + * @tc.expected: step4. can't find any data in db. + */ + DistributedDBInterfacesTransactionTestCase::Commit006(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit007 + * @tc.desc: Test that can commit Transaction after delete and update db. + * @tc.type: FUNC + * @tc.require: AR000CQDTO AR000CQDTP + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Commit007, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. delete record from db where key = k1. + * @tc.expected: step2. delete succeed. + */ + /** + * @tc.steps:step3. put (k2, v1) to db. + * @tc.expected: step3. put succeed. + */ + /** + * @tc.steps:step4. call commit interface. + * @tc.expected: step4. commit succeed. + */ + /** + * @tc.steps:step5. use snapshot interface to check the data in db. + * @tc.expected: step5. can't find (k1, v1) but can find (k2, v1) in db. + */ + DistributedDBInterfacesTransactionTestCase::Commit007(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: Commit008 + * @tc.desc: Test that can commit Transaction after clear and new add records. + * @tc.type: FUNC + * @tc.require: AR000CQDTO AR000CQDTP + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Commit008, TestSize.Level1) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + /** + * @tc.steps:step2. clear all the records from db. + * @tc.expected: step2. clear succeed. + */ + /** + * @tc.steps:step3. put (k3, v3) to db. + * @tc.expected: step3. put succeed. + */ + /** + * @tc.steps:step4. call commit interface. + * @tc.expected: step4. commit succeed. + */ + /** + * @tc.steps:step5. use snapshot interface to check the data in db. + * @tc.expected: step5. can only find (k3, v3) in db. + */ + DistributedDBInterfacesTransactionTestCase::Commit008(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack001 + * @tc.desc: Test if new commit records and logs generated + * when a transaction rollback-ed + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Rollback001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test g_kvDelegatePtr->Rollback + * @tc.expected: step1. Return ERROR. + */ + DistributedDBInterfacesTransactionTestCase::RollBack001(g_kvDelegatePtr); +} + +/** +* @tc.name: RollBack002 +* @tc.desc: rollback a transaction two times +* @tc.type: FUNC +* @tc.require: AR000BVRNM AR000CQDTQ +* @tc.author: huangnaigu +*/ +HWTEST_F(DistributedDBInterfacesTransactionTest, Rollback002, TestSize.Level1) +{ + /** + * @tc.steps:step1. start a transaction + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. rollback the transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. rollback the transaction the second time + * @tc.expected: step3. Return ERROR. + */ + DistributedDBInterfacesTransactionTestCase::RollBack002(g_kvDelegatePtr); +} + +/** + * @tc.name: RollBack003 + * @tc.desc: insert a data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Rollback003, TestSize.Level1) +{ + /** + * @tc.steps:step1. start a transaction + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. Put (k1,v1) + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. rollback a transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. check if (k1,v1) exists + * @tc.expected: step4. Return NOT_FOUND. + */ + DistributedDBInterfacesTransactionTestCase::RollBack003(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack004 + * @tc.desc: update a data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Rollback004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Put (k1,v1) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Update (k1,v1) to (k1,v2) in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. rollback the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. check the value of k1 is v1 + * @tc.expected: step5. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack004(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack005 + * @tc.desc: delete a exist data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Rollback005, TestSize.Level1) +{ + /** + * @tc.steps:step1. Put (k1,v1) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Delete (k1,v1) in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. rollback the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. check the value of k1 is v1 + * @tc.expected: step5. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack005(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack006 + * @tc.desc: clear db and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Rollback006, TestSize.Level1) +{ + /** + * @tc.steps:step1. PutBatch records: (k1,v1), (k2,v2) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Clear all records in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. rollback the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. check if there are 2 records in the db + * @tc.expected: step5. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack006(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack007 + * @tc.desc: delete a exist data and update a data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Rollback007, TestSize.Level1) +{ + /** + * @tc.steps:step1. PutBatch records: (k1,v1), (k2,v2) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Delete (k1,v1) in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. Update (k2,v2) to (k2,v1) in the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. rollback the transaction + * @tc.expected: step5. Return OK. + */ + /** + * @tc.steps:step6. check if (k1,v1),(k2,v2) exist and no more records in the db + * @tc.expected: step6. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack007(g_kvDelegatePtr, g_snapshotDelegatePtr); +} + +/** + * @tc.name: RollBack008 + * @tc.desc: clear db and insert a data and rollback + * @tc.type: FUNC + * @tc.require: AR000BVRNM AR000CQDTQ + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBInterfacesTransactionTest, Rollback008, TestSize.Level1) +{ + /** + * @tc.steps:step1. PutBatch records: (k1,v1), (k2,v2) + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Clear all records in the transaction + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. Put (012, ABC) in the transaction + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps:step5. rollback the transaction + * @tc.expected: step5. Return OK. + */ + /** + * @tc.steps:step6. check if (k1,v1),(k2,v2) exist and no more records in the db + * @tc.expected: step6. verification is OK . + */ + DistributedDBInterfacesTransactionTestCase::RollBack008(g_kvDelegatePtr, g_snapshotDelegatePtr); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.cpp b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff6786cf720fb151d979b5c0ae0e0a2034d8370b --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.cpp @@ -0,0 +1,720 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "distributeddb_interfaces_transaction_testcase.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +void DistributedDBInterfacesTransactionTestCase::StartTransaction001(KvStoreDelegate *&kvDelegatePtr) +{ + /** + * @tc.steps:step1. call StartTransaction interface the 1st time. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. call StartTransaction interface the 2nd time. + * @tc.expected: step2. call failed and return ERROR. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == DB_ERROR); + EXPECT_EQ(kvDelegatePtr->Commit(), OK); +} + +void DistributedDBInterfacesTransactionTestCase::StartTransaction002(KvStoreDelegate *&kvDelegatePtr) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. call commit interface. + * @tc.expected: step2. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == OK); +} + +void DistributedDBInterfacesTransactionTestCase::StartTransaction003(KvStoreDelegate *&kvDelegatePtr) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. call rollback interface. + * @tc.expected: step2. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == OK); +} + +static void GetSnapshotUnitTest(KvStoreDelegate *&kvDelegatePtr, KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus snapshotDelegateStatus = INVALID_ARGS; + auto snapshotDelegateCallback = bind(&DistributedDBToolsUnitTest::SnapshotDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(snapshotDelegateStatus), std::ref(snapshotDelegatePtr)); + + kvDelegatePtr->GetKvStoreSnapshot(nullptr, snapshotDelegateCallback); + EXPECT_TRUE(snapshotDelegateStatus == OK); + ASSERT_TRUE(snapshotDelegatePtr != nullptr); +} + +void DistributedDBInterfacesTransactionTestCase::StartTransaction004(KvStoreDelegate *&kvDelegatePtr, + const string &storeId, bool localOnly, KvStoreDelegateManager &mgr, KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus kvDelegateStatus = INVALID_ARGS; + auto kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(kvDelegateStatus), std::ref(kvDelegatePtr)); + + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. put (k1, v1) to data base. + * @tc.expected: step2. put succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_1, VALUE_1) == OK); + /** + * @tc.steps:step3. close data base. + * @tc.expected: step3. close succeed. + */ + EXPECT_EQ(mgr.CloseKvStore(kvDelegatePtr), OK); + kvDelegatePtr = nullptr; + + /** + * @tc.steps:step4. use GetKvStore interface to open db. + * @tc.expected: step4. open succeed. + */ + KvStoreDelegate::Option option = {true, localOnly}; + mgr.GetKvStore(storeId, option, kvDelegateCallback); + EXPECT_EQ(kvDelegateStatus, OK); + ASSERT_TRUE(kvDelegatePtr != nullptr); + + /** + * @tc.steps:step5. use snapshot interface to check the value of k1. + * @tc.expected: step5. can't get the record of k1. + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == NOT_FOUND); + EXPECT_TRUE(value.size() == 0); +} + +void DistributedDBInterfacesTransactionTestCase::StartTransaction005(KvStoreDelegate *&kvDelegatePtr, + const string &storeId, bool localOnly, KvStoreDelegateManager &mgr) +{ + DBStatus kvDelegateStatus = INVALID_ARGS; + auto kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(kvDelegateStatus), std::ref(kvDelegatePtr)); + + /** + * @tc.steps:step1. call StartTransaction interface the 1st time. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + KvStoreDelegate *temp = kvDelegatePtr; + temp->Put(KEY_1, VALUE_1); + + KvStoreDelegate::Option option = {true, localOnly}; + mgr.GetKvStore(storeId, option, kvDelegateCallback); + EXPECT_TRUE(kvDelegateStatus == OK); + ASSERT_TRUE(kvDelegatePtr != nullptr); + /** + * @tc.steps:step2. call StartTransaction interface the 2nd time using another . + * @tc.expected: step2. call failed. + */ + EXPECT_NE(kvDelegatePtr->StartTransaction(), OK); + + kvDelegatePtr->Put(KEY_2, VALUE_2); + /** + * @tc.steps:step4. call commit interface the 1st time. + * @tc.expected: step4. call failed. + */ + EXPECT_EQ(temp->Commit(), OK); + EXPECT_EQ(mgr.CloseKvStore(temp), OK); + temp = nullptr; + /** + * @tc.steps:step5. call commit interface the 2nd time. + * @tc.expected: step5. call failed. + */ + EXPECT_NE(kvDelegatePtr->Commit(), OK); +} + +void DistributedDBInterfacesTransactionTestCase::Commit001(KvStoreDelegate *&kvDelegatePtr) +{ + /** + * @tc.steps:step1. commit Transaction without start it. + * @tc.expected: step1. commit failed and returned ERROR. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == DB_ERROR); +} + +void DistributedDBInterfacesTransactionTestCase::Commit002(KvStoreDelegate *&kvDelegatePtr) +{ + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. call commit interface the 1st time. + * @tc.expected: step2. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == OK); + /** + * @tc.steps:step3. call commit interface the 2nd time. + * @tc.expected: step3. call failed and returned ERROR. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == DB_ERROR); +} + +void DistributedDBInterfacesTransactionTestCase::Commit003(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. put (k1, v1) to db. + * @tc.expected: step2. put succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_1, VALUE_1) == OK); + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == OK); + + /** + * @tc.steps:step4. use snapshot interface to check if (k1, v1) is put succeed. + * @tc.expected: step4. can find (k1, v1) from db. + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == OK); + ASSERT_TRUE(value.size() > 0); + EXPECT_TRUE(value.front() == VALUE_1.front()); +} + +void DistributedDBInterfacesTransactionTestCase::Commit004(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + /** + * @tc.steps:step1. put one data. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_1, VALUE_1) == OK); + /** + * @tc.steps:step2. call StartTransaction interface. + * @tc.expected: step2. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step3. update the data to another value. + * @tc.expected: step3. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_1, VALUE_2) == OK); + /** + * @tc.steps:step4. call commit interface. + * @tc.expected: step4. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == OK); + + /** + * @tc.steps:step5. use snapshot interface to check the updated data. + * @tc.expected: step5. the value is updated. + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == OK); + ASSERT_TRUE(value.size() > 0); + EXPECT_TRUE(value.front() == VALUE_2.front()); +} + +void DistributedDBInterfacesTransactionTestCase::Commit005(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + + EXPECT_TRUE(kvDelegatePtr->Put(KEY_1, VALUE_1) == OK); + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. delete record from db where key = k1. + * @tc.expected: step2. delete succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Delete(KEY_1) == OK); + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. commit succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == OK); + + /** + * @tc.steps:step4. use snapshot interface to check if (k1, v1) is delete succeed. + * @tc.expected: step4. can't find (k1, v1) in the db. + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == NOT_FOUND); +} + +void DistributedDBInterfacesTransactionTestCase::Commit006(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus entryVectorStatus = INVALID_ARGS; + unsigned long matchSize = 0; + std::vector entriesVector; + auto entryVectorCallback = bind(&DistributedDBToolsUnitTest::EntryVectorCallback, placeholders::_1, + placeholders::_2, std::ref(entryVectorStatus), std::ref(matchSize), std::ref(entriesVector)); + + EXPECT_TRUE(kvDelegatePtr->PutBatch(ENTRY_VECTOR) == OK); + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. clear all the records from db. + * @tc.expected: step2. clear succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Clear() == OK); + /** + * @tc.steps:step3. call commit interface. + * @tc.expected: step3. commit succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == OK); + + /** + * @tc.steps:step4. use snapshot interface to check if there are any data in db. + * @tc.expected: step4. can't find any data in db. + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->GetEntries(NULL_KEY_1, entryVectorCallback); + EXPECT_TRUE(entryVectorStatus == NOT_FOUND); +} + +void DistributedDBInterfacesTransactionTestCase::Commit007(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + + EXPECT_TRUE(kvDelegatePtr->PutBatch(ENTRY_VECTOR) == OK); + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. delete record from db where key = k1. + * @tc.expected: step2. delete succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Delete(KEY_1) == OK); + /** + * @tc.steps:step3. put (k2, v1) to db. + * @tc.expected: step3. put succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_2, VALUE_1) == OK); + /** + * @tc.steps:step4. call commit interface. + * @tc.expected: step4. commit succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == OK); + + /** + * @tc.steps:step5. use snapshot interface to check the data in db. + * @tc.expected: step5. can't find (k1, v1) but can find (k2, v1) in db. + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == NOT_FOUND); + snapshotDelegatePtr->Get(KEY_2, valueCallback); + EXPECT_TRUE(valueStatus == OK); + ASSERT_TRUE(value.size() > 0); + EXPECT_TRUE(value.front() == VALUE_1.front()); +} + +void DistributedDBInterfacesTransactionTestCase::Commit008(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus entryVectorStatus = INVALID_ARGS; + unsigned long matchSizeCallback = 0; + std::vector entriesVector; + auto entryVectorCallback = bind(&DistributedDBToolsUnitTest::EntryVectorCallback, placeholders::_1, + placeholders::_2, std::ref(entryVectorStatus), std::ref(matchSizeCallback), std::ref(entriesVector)); + + EXPECT_TRUE(kvDelegatePtr->PutBatch(ENTRY_VECTOR) == OK); + /** + * @tc.steps:step1. call StartTransaction interface. + * @tc.expected: step1. call succeed. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. clear all the records from db. + * @tc.expected: step2. clear succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Clear() == OK); + /** + * @tc.steps:step3. put (k3, v3) to db. + * @tc.expected: step3. put succeed. + */ + Entry entry; + GenerateEntry(1, 3, entry); + EXPECT_TRUE(kvDelegatePtr->Put(entry.key, entry.value) == OK); + /** + * @tc.steps:step4. call commit interface. + * @tc.expected: step4. commit succeed. + */ + EXPECT_TRUE(kvDelegatePtr->Commit() == OK); + + /** + * @tc.steps:step5. use snapshot interface to check the data in db. + * @tc.expected: step5. can only find (k3, v3) in db. + */ + unsigned long matchSize = 1; + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->GetEntries(NULL_KEY_1, entryVectorCallback); + EXPECT_TRUE(entryVectorStatus == OK); + ASSERT_TRUE(matchSizeCallback == matchSize); +} + +void DistributedDBInterfacesTransactionTestCase::RollBack001(KvStoreDelegate *&kvDelegatePtr) +{ + /** + * @tc.steps:step1. Test g_kvDelegatePtr->Rollback + * @tc.expected: step1. Return ERROR. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == DB_ERROR); +} + +void DistributedDBInterfacesTransactionTestCase::RollBack002(KvStoreDelegate *&kvDelegatePtr) +{ + /** + * @tc.steps:step1. start a transaction + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. rollback the transaction + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == OK); + /** + * @tc.steps:step3. rollback the transaction the second time + * @tc.expected: step3. Return ERROR. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == DB_ERROR); +} + +void DistributedDBInterfacesTransactionTestCase::RollBack003(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + /** + * @tc.steps:step1. start a transaction + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step2. Put (k1,v1) + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_1, VALUE_1) == OK); + /** + * @tc.steps:step3. rollback a transaction + * @tc.expected: step3. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == OK); + + /** + * @tc.steps:step4. check if (k1,v1) exists + * @tc.expected: step4. Return NOT_FOUND. + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == NOT_FOUND); +} + +void DistributedDBInterfacesTransactionTestCase::RollBack004(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + /** + * @tc.steps:step1. Put (k1,v1) + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_1, VALUE_1) == OK); + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step3. Update (k1,v1) to (k1,v2) in the transaction + * @tc.expected: step3. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_1, VALUE_2) == OK); + /** + * @tc.steps:step4. rollback the transaction + * @tc.expected: step4. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == OK); + + /** + * @tc.steps:step5. check the value of k1 is v1 + * @tc.expected: step5. verification is OK . + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == OK); + ASSERT_TRUE(value.size() > 0); + EXPECT_TRUE(value.front() == VALUE_1.front()); +} + +void DistributedDBInterfacesTransactionTestCase::RollBack005(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + /** + * @tc.steps:step1. Put (k1,v1) + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_1, VALUE_1) == OK); + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step3. Delete (k1,v1) in the transaction + * @tc.expected: step3. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Delete(KEY_1) == OK); + /** + * @tc.steps:step4. rollback the transaction + * @tc.expected: step4. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == OK); + + /** + * @tc.steps:step5. check the value of k1 is v1 + * @tc.expected: step5. verification is OK . + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == OK); + ASSERT_TRUE(value.size() > 0); + EXPECT_TRUE(value.front() == VALUE_1.front()); +} + +void DistributedDBInterfacesTransactionTestCase::RollBack006(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus entryVectorStatus = INVALID_ARGS; + unsigned long matchSizeCallback = 0; + std::vector entriesVector; + auto entryVectorCallback = bind(&DistributedDBToolsUnitTest::EntryVectorCallback, placeholders::_1, + placeholders::_2, std::ref(entryVectorStatus), std::ref(matchSizeCallback), std::ref(entriesVector)); + /** + * @tc.steps:step1. PutBatch records: (k1,v1), (k2,v2) + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->PutBatch(ENTRY_VECTOR) == OK); + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step3. Clear all records in the transaction + * @tc.expected: step3. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Clear() == OK); + /** + * @tc.steps:step4. rollback the transaction + * @tc.expected: step4. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == OK); + + /** + * @tc.steps:step5. check if there are 2 records in the db + * @tc.expected: step5. verification is OK . + */ + unsigned long matchSize = 2; + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->GetEntries(NULL_KEY_1, entryVectorCallback); + EXPECT_TRUE(entryVectorStatus == OK); + ASSERT_TRUE(matchSizeCallback == matchSize); +} + +void DistributedDBInterfacesTransactionTestCase::RollBack007(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + + DBStatus entryVectorStatus = INVALID_ARGS; + unsigned long matchSizeCallback = 0; + std::vector entriesVector; + auto entryVectorCallback = bind(&DistributedDBToolsUnitTest::EntryVectorCallback, placeholders::_1, + placeholders::_2, std::ref(entryVectorStatus), std::ref(matchSizeCallback), std::ref(entriesVector)); + + /** + * @tc.steps:step1. PutBatch records: (k1,v1), (k2,v2) + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->PutBatch(ENTRY_VECTOR) == OK); + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step3. Delete (k1,v1) in the transaction + * @tc.expected: step3. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Delete(KEY_1) == OK); + /** + * @tc.steps:step4. Update (k2,v2) to (k2,v1) in the transaction + * @tc.expected: step4. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Put(KEY_2, VALUE_1) == OK); + /** + * @tc.steps:step5. rollback the transaction + * @tc.expected: step5. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == OK); + + /** + * @tc.steps:step6. check if (k1,v1),(k2,v2) exist and no more records in the db + * @tc.expected: step6. verification is OK . + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == OK); + ASSERT_TRUE(value.size() > 0); + EXPECT_TRUE(value.front() == VALUE_1.front()); + snapshotDelegatePtr->Get(KEY_2, valueCallback); + EXPECT_TRUE(valueStatus == OK); + ASSERT_TRUE(value.size() > 0); + EXPECT_TRUE(value.front() == VALUE_2.front()); + + unsigned long matchSize = 2; + snapshotDelegatePtr->GetEntries(NULL_KEY_1, entryVectorCallback); + EXPECT_TRUE(entryVectorStatus == OK); + ASSERT_TRUE(matchSizeCallback == matchSize); +} + +void DistributedDBInterfacesTransactionTestCase::RollBack008(KvStoreDelegate *&kvDelegatePtr, + KvStoreSnapshotDelegate *&snapshotDelegatePtr) +{ + DBStatus valueStatus = INVALID_ARGS; + Value value; + auto valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(valueStatus), std::ref(value)); + DBStatus entryVectorStatus = INVALID_ARGS; + unsigned long matchSizeCallback = 0; + std::vector entriesVector; + auto entryVectorCallback = bind(&DistributedDBToolsUnitTest::EntryVectorCallback, placeholders::_1, + placeholders::_2, std::ref(entryVectorStatus), std::ref(matchSizeCallback), std::ref(entriesVector)); + + /** + * @tc.steps:step1. PutBatch records: (k1,v1), (k2,v2) + * @tc.expected: step1. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->PutBatch(ENTRY_VECTOR) == OK); + /** + * @tc.steps:step2. start a transaction + * @tc.expected: step2. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->StartTransaction() == OK); + /** + * @tc.steps:step3. Clear all records in the transaction + * @tc.expected: step3. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Clear() == OK); + /** + * @tc.steps:step4. Put (012, ABC) in the transaction + * @tc.expected: step4. Return OK. + */ + Entry entry; + GenerateEntry(1, 3, entry); + EXPECT_TRUE(kvDelegatePtr->Put(entry.key, entry.value) == OK); + /** + * @tc.steps:step5. rollback the transaction + * @tc.expected: step5. Return OK. + */ + EXPECT_TRUE(kvDelegatePtr->Rollback() == OK); + + /** + * @tc.steps:step6. check if (k1,v1),(k2,v2) exist and no more records in the db + * @tc.expected: step6. verification is OK . + */ + GetSnapshotUnitTest(kvDelegatePtr, snapshotDelegatePtr); + snapshotDelegatePtr->Get(KEY_1, valueCallback); + EXPECT_TRUE(valueStatus == OK); + ASSERT_TRUE(value.size() > 0); + EXPECT_TRUE(value.front() == VALUE_1.front()); + snapshotDelegatePtr->Get(KEY_2, valueCallback); + EXPECT_TRUE(valueStatus == OK); + ASSERT_TRUE(value.size() > 0); + EXPECT_TRUE(value.front() == VALUE_2.front()); + + unsigned long matchSize = 2; + snapshotDelegatePtr->GetEntries(NULL_KEY_1, entryVectorCallback); + EXPECT_TRUE(entryVectorStatus == OK); + ASSERT_TRUE(matchSizeCallback == matchSize); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.h b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.h new file mode 100644 index 0000000000000000000000000000000000000000..73181f1a4f91a602b1415c65bfce95c1ee019008 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDB_INTERFACES_TRANSACTION_TESTCASE_H +#define DISTRIBUTEDDB_INTERFACES_TRANSACTION_TESTCASE_H + +#include +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" + +class DistributedDBInterfacesTransactionTestCase final { +public: + DistributedDBInterfacesTransactionTestCase() {}; + ~DistributedDBInterfacesTransactionTestCase() {}; + + static void StartTransaction001(DistributedDB::KvStoreDelegate *&kvDelegatePtr); + + static void StartTransaction002(DistributedDB::KvStoreDelegate *&kvDelegatePtr); + + static void StartTransaction003(DistributedDB::KvStoreDelegate *&kvDelegatePtr); + + static void StartTransaction004(DistributedDB::KvStoreDelegate *&kvDelegatePtr, const std::string &storeId, + bool localOnly, DistributedDB::KvStoreDelegateManager &mgr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void StartTransaction005(DistributedDB::KvStoreDelegate *&kvDelegatePtr, const std::string &storeId, + bool localOnly, DistributedDB::KvStoreDelegateManager &mgr); + + static void Commit001(DistributedDB::KvStoreDelegate *&kvDelegatePtr); + + static void Commit002(DistributedDB::KvStoreDelegate *&kvDelegatePtr); + + static void Commit003(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void Commit004(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void Commit005(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void Commit006(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void Commit007(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void Commit008(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void RollBack001(DistributedDB::KvStoreDelegate *&kvDelegatePtr); + + static void RollBack002(DistributedDB::KvStoreDelegate *&kvDelegatePtr); + + static void RollBack003(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void RollBack004(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void RollBack005(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void RollBack006(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void RollBack007(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); + + static void RollBack008(DistributedDB::KvStoreDelegate *&kvDelegatePtr, + DistributedDB::KvStoreSnapshotDelegate *&snapshotDelegatePtr); +}; +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.cpp b/mock/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..821801545619d4db4f201a938fdea3bd0360fa57 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "process_system_api_adapter_impl.h" + +#include +#include + +#include "distributeddb_tools_unit_test.h" +#include "distributeddb_data_generate_unit_test.h" +#include "log_print.h" +#include "platform_specific.h" + +using namespace DistributedDBUnitTest; + +namespace DistributedDB { +namespace { + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + std::placeholders::_1, std::placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); +} + +ProcessSystemApiAdapterImpl::ProcessSystemApiAdapterImpl() + : callback_(nullptr), + isLocked_(false), + createDb_(false) +{ +} + +ProcessSystemApiAdapterImpl::~ProcessSystemApiAdapterImpl() +{ + callback_ = nullptr; +} + +DBStatus ProcessSystemApiAdapterImpl::RegOnAccessControlledEvent(const OnAccessControlledEvent &callback) +{ + callback_ = callback; + return OK; +} + +bool ProcessSystemApiAdapterImpl::IsAccessControlled() const +{ + return isLocked_; +} + +DBStatus ProcessSystemApiAdapterImpl::SetSecurityOption(const std::string &filePath, const SecurityOption &option) +{ + bool isExisted = OS::CheckPathExistence(filePath); + if (!isExisted) { + LOGE("SetSecurityOption to unexistence dir![%s]", filePath.c_str()); + return NOT_FOUND; + } + + std::string dirName; + struct dirent *direntPtr = nullptr; + DIR *dirPtr = opendir(filePath.c_str()); + if (dirPtr == nullptr) { + LOGD("set path secOpt![%s] [%d] [%d]", filePath.c_str(), option.securityFlag, option.securityLabel); + pathSecOptDic_[filePath] = option; + return OK; + } + + while (true) { + direntPtr = readdir(dirPtr); + // condition to exit the loop + if (direntPtr == nullptr) { + break; + } + // only remove all *.db files + std::string str(direntPtr->d_name); + if (str == "." || str == "..") { + continue; + } + dirName.clear(); + dirName.append(filePath).append("/").append(str); + if (direntPtr->d_type == DT_DIR) { + SetSecurityOption(dirName, option); + std::lock_guard lock(adapterlock_); + pathSecOptDic_[dirName] = option; + LOGD("set path secOpt![%s] [%d] [%d]", dirName.c_str(), option.securityFlag, option.securityLabel); + } else { + std::lock_guard lock(adapterlock_); + pathSecOptDic_[dirName] = option; + LOGD("set path secOpt![%s] [%d] [%d]", dirName.c_str(), option.securityFlag, option.securityLabel); + continue; + } + } + closedir(dirPtr); + pathSecOptDic_[filePath] = option; + return OK; +} + +DBStatus ProcessSystemApiAdapterImpl::GetSecurityOption(const std::string &filePath, SecurityOption &option) const +{ + std::map temp = pathSecOptDic_; // For const interface only for test + if (temp.find(filePath) == temp.end()) { + LOGE("[ProcessSystemApiAdapterImpl]::[GetSecurityOption] path [%s] not set secOpt!", filePath.c_str()); + option.securityLabel = NOT_SET; + option.securityFlag = 0; + return OK; + } + LOGD("[AdapterImpl] Get path secOpt![%s] [%d] [%d]", filePath.c_str(), option.securityFlag, option.securityLabel); + option = temp[filePath]; + return OK; +} + +bool ProcessSystemApiAdapterImpl::CheckDeviceSecurityAbility(const std::string &devId, + const SecurityOption &option) const +{ + LOGI("CheckDeviceSecurityAbility!!"); + if (createDb_) { // for close kvstore will close virtual communicator + KvStoreConfig config; + DistributedDBToolsUnitTest::TestDirInit(config.dataDir); + + g_mgr.SetKvStoreConfig(config); + + KvStoreNbDelegate::Option dbOption = {true, false, false}; + g_mgr.GetKvStore("CheckDeviceSecurityAbilityMeta", dbOption, g_kvNbDelegateCallback); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + } + return true; +} + +void ProcessSystemApiAdapterImpl::SetLockStatus(bool isLock) +{ + std::lock_guard lock(adapterlock_); + if (callback_) { + callback_(isLock); + } + isLocked_ = isLock; +} + +void ProcessSystemApiAdapterImpl::SetNeedCreateDb(bool isCreate) +{ + std::lock_guard lock(adapterlock_); + createDb_ = isCreate; +} + +void ProcessSystemApiAdapterImpl::ResetSecOptDic() +{ + pathSecOptDic_.clear(); +} + +void ProcessSystemApiAdapterImpl::ResetAdapter() +{ + ResetSecOptDic(); + SetLockStatus(false); + g_mgr.DeleteKvStore("CheckDeviceSecurityAbilityMeta"); +} +}; diff --git a/mock/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.h b/mock/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..474e71d518ed1bd6be20eaf57fc55c611c0c2c56 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PROCESS_SYSTEM_API_ADAPTER_H +#define PROCESS_SYSTEM_API_ADAPTER_H + +#include +#include +#include + +#include "iprocess_system_api_adapter.h" +#include "store_types.h" + +namespace DistributedDB { +class ProcessSystemApiAdapterImpl : public IProcessSystemApiAdapter { +public: + ProcessSystemApiAdapterImpl(); + ~ProcessSystemApiAdapterImpl() override; + DBStatus RegOnAccessControlledEvent(const OnAccessControlledEvent &callback) override; + bool IsAccessControlled() const override; + DBStatus SetSecurityOption(const std::string &filePath, const SecurityOption &option) override; + DBStatus GetSecurityOption(const std::string &filePath, SecurityOption &option) const override; + bool CheckDeviceSecurityAbility(const std::string &devId, const SecurityOption &option) const override; + void SetLockStatus(bool isLock); + void SetNeedCreateDb(bool isCreate); + void ResetSecOptDic(); + void ResetAdapter(); + +private: + mutable std::mutex adapterlock_; + OnAccessControlledEvent callback_; + std::map pathSecOptDic_; + bool isLocked_; + bool createDb_; +}; +} // namespace DistributedDB + +#endif diff --git a/mock/distributeddb/test/unittest/common/interfaces/runtime_context_process_system_api_adapter_impl_test.cpp b/mock/distributeddb/test/unittest/common/interfaces/runtime_context_process_system_api_adapter_impl_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..373f325d62d1d6dc38bcad4030592e0f9cf3c3d3 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/interfaces/runtime_context_process_system_api_adapter_impl_test.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_errno.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "iprocess_system_api_adapter.h" +#include "log_print.h" +#include "process_system_api_adapter_impl.h" +#include "runtime_context.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + const std::string DATA_FILE_PATH = "/data/test/"; + SecurityOption g_option = {0, 0}; + const std::string DEV_ID = "devId"; + std::shared_ptr g_adapter; + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + string g_testDir; + KvStoreConfig g_config; + + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); +} + +class RuntimeContextProcessSystemApiAdapterImplTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); +}; + +void RuntimeContextProcessSystemApiAdapterImplTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Get an adapter + */ + g_adapter = std::make_shared(); + EXPECT_TRUE(g_adapter != nullptr); + DistributedDBToolsUnitTest::TestDirInit(g_testDir); +} + +void RuntimeContextProcessSystemApiAdapterImplTest::TearDownTestCase(void) +{ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +void RuntimeContextProcessSystemApiAdapterImplTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_adapter->ResetAdapter(); +} + +/** + * @tc.name: SetSecurityOption001 + * @tc.desc: Set SecurityOption. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + */ +HWTEST_F(RuntimeContextProcessSystemApiAdapterImplTest, SetSecurityOption001, TestSize.Level1) +{ + /** + * @tc.steps: step1. call SetSecurityOption to set SecurityOption before set g_adapter + * @tc.expected: step1. function return E_NOT_SUPPORT + */ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); + int errCode = RuntimeContext::GetInstance()->SetSecurityOption(DATA_FILE_PATH, g_option); + EXPECT_TRUE(errCode == -E_NOT_SUPPORT); + + /** + * @tc.steps: step2. call SetSecurityOption to set SecurityOption after set g_adapter + * @tc.expected: step2. function return E_OK + */ + EXPECT_TRUE(g_adapter != nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); + errCode = RuntimeContext::GetInstance()->SetSecurityOption(DATA_FILE_PATH, g_option); + EXPECT_EQ(errCode, E_OK); +} + +/** + * @tc.name: GetSecurityOption001 + * @tc.desc: Get SecurityOption. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + */ +HWTEST_F(RuntimeContextProcessSystemApiAdapterImplTest, GetSecurityOption001, TestSize.Level1) +{ + /** + * @tc.steps: step1. call GetSecurityOption to get SecurityOption before set g_adapter + * @tc.expected: step1. function return E_NOT_SUPPORT + */ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); + int errCode = RuntimeContext::GetInstance()->GetSecurityOption(DATA_FILE_PATH, g_option); + EXPECT_TRUE(errCode == -E_NOT_SUPPORT); + + /** + * @tc.steps: step2. call GetSecurityOption to get SecurityOption after set g_adapter + * @tc.expected: step2. function return E_OK + */ + EXPECT_TRUE(g_adapter != nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); + errCode = RuntimeContext::GetInstance()->GetSecurityOption(DATA_FILE_PATH, g_option); + EXPECT_TRUE(errCode == E_OK); +} + +/** + * @tc.name: RegisterLockStatusLister001 + * @tc.desc: Register a listener. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + */ +HWTEST_F(RuntimeContextProcessSystemApiAdapterImplTest, RegisterLockStatusLister001, TestSize.Level1) +{ + int errCode = E_OK; + bool lockStatus = false; + auto onEventFunction1 = [&lockStatus](void *isLock) { + LOGI("lock status 1 changed %d", *(static_cast(isLock))); + lockStatus = *(static_cast(isLock)); + }; + + auto onEventFunction2 = [&lockStatus](void *isLock) { + LOGI("lock status 2 changed %d", *(static_cast(isLock))); + lockStatus = *(static_cast(isLock)); + }; + /** + * @tc.steps: step1. call RegisterLockStatusLister to register a listener before set adapter + * @tc.expected: step1. function return ok + */ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); + NotificationChain::Listener *listener = + RuntimeContext::GetInstance()->RegisterLockStatusLister(onEventFunction1, errCode); + EXPECT_NE(listener, nullptr); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. call RegisterLockStatusLister to register a listener after set g_adapter + * @tc.expected: step2. function return a not null listener + */ + EXPECT_TRUE(g_adapter != nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); + + auto listener1 = RuntimeContext::GetInstance()->RegisterLockStatusLister(onEventFunction1, errCode); + EXPECT_TRUE(errCode == E_OK); + EXPECT_NE(listener1, nullptr); + listener1->Drop(); + + /** + * @tc.steps: step3. call SetLockStatus to change lock status + * @tc.expected: step3. the listener's callback should be called + */ + g_adapter->SetLockStatus(false); + EXPECT_TRUE(!lockStatus); + + /** + * @tc.steps: step4. call RegisterLockStatusLister to register another listener after set g_adapter + * @tc.expected: step4. function return a not null listener + */ + listener->Drop(); + listener = RuntimeContext::GetInstance()->RegisterLockStatusLister(onEventFunction2, errCode); + EXPECT_NE(listener, nullptr); + listener->Drop(); +} + +/** + * @tc.name: IsAccessControlled001 + * @tc.desc: Get Access Lock Status. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + */ +HWTEST_F(RuntimeContextProcessSystemApiAdapterImplTest, IsAccessControlled001, TestSize.Level1) +{ + /** + * @tc.steps: step1. call IsAccessControlled to get Access lock status before set g_adapter + * @tc.expected: step1. function return true + */ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); + bool isLocked = RuntimeContext::GetInstance()->IsAccessControlled(); + EXPECT_FALSE(isLocked); + + /** + * @tc.steps: step2. IsAccessControlled to get Access lock status after set g_adapter + * @tc.expected: step2. function return false + */ + EXPECT_TRUE(g_adapter != nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); + isLocked = RuntimeContext::GetInstance()->IsAccessControlled(); + EXPECT_TRUE(!isLocked); +} + +/** + * @tc.name: CheckDeviceSecurityAbility001 + * @tc.desc: Check device security ability. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + */ +HWTEST_F(RuntimeContextProcessSystemApiAdapterImplTest, CheckDeviceSecurityAbility001, TestSize.Level1) +{ + /** + * @tc.steps: step1. call CheckDeviceSecurityAbility to check device security ability before set g_adapter + * @tc.expected: step1. function return true + */ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); + bool isSupported = RuntimeContext::GetInstance()->CheckDeviceSecurityAbility(DEV_ID, g_option); + EXPECT_TRUE(isSupported); + + /** + * @tc.steps: step2. IsAccessControlled to check device security ability after set g_adapter + * @tc.expected: step2. function return true + */ + EXPECT_TRUE(g_adapter != nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); + isSupported = RuntimeContext::GetInstance()->CheckDeviceSecurityAbility(DEV_ID, g_option); + EXPECT_TRUE(isSupported); +} + +namespace { +void FuncCheckDeviceSecurityAbility() +{ + RuntimeContext::GetInstance()->CheckDeviceSecurityAbility("", SecurityOption()); + return; +} +} + +/** + * @tc.name: CheckDeviceSecurityAbility002 + * @tc.desc: Check device security ability with getkvstore frequency. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + */ +HWTEST_F(RuntimeContextProcessSystemApiAdapterImplTest, CheckDeviceSecurityAbility002, TestSize.Level1) +{ + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); + g_adapter->SetNeedCreateDb(true); + + const std::string storeId = "CheckDeviceSecurityAbility002"; + std::thread t1(FuncCheckDeviceSecurityAbility); + std::thread t2([&]() { + for (int i = 0; i < 100; i++) { // open close 100 times + LOGI("open store!!"); + KvStoreNbDelegate::Option option1 = {true, false, false}; + g_mgr.GetKvStore(storeId, option1, g_kvNbDelegateCallback); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + } + }); + std::thread t3(FuncCheckDeviceSecurityAbility); + + t1.join(); + t2.join(); + t3.join(); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_data_transformer_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_data_transformer_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3c6b353d6bf7c60bdefbdd47476dff17091d12f --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_data_transformer_test.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "data_transformer.h" + +using namespace testing::ext; +using namespace DistributedDB; +namespace { +const int ONE_HUNDERED = 100; +const char DEFAULT_CHAR = 'D'; +const std::string DEFAULT_TEXT = "This is a text"; +const std::vector DEFAULT_BLOB(ONE_HUNDERED, DEFAULT_CHAR); +void SetNull(DataValue &dataValue) +{ + dataValue.ResetValue(); +} + +void SetInt64(DataValue &dataValue) +{ + dataValue = INT64_MAX; +} + +void SetDouble(DataValue &dataValue) +{ + dataValue = 1.0; +} + +void SetText(DataValue &dataValue) +{ + dataValue.SetText(DEFAULT_TEXT); +} + +void SetBlob(DataValue &dataValue) +{ + Blob blob; + blob.WriteBlob(DEFAULT_BLOB.data(), DEFAULT_BLOB.size()); + dataValue.SetBlob(blob); +} + +std::map g_typeMapFunction = { + {StorageType::STORAGE_TYPE_NULL, &SetNull}, + {StorageType::STORAGE_TYPE_INTEGER, &SetInt64}, + {StorageType::STORAGE_TYPE_REAL, &SetDouble}, + {StorageType::STORAGE_TYPE_TEXT, &SetText}, + {StorageType::STORAGE_TYPE_BLOB, &SetBlob} +}; + +void GenerateRowData(const std::vector &fieldInfoList, RowData &rowData) +{ + for (auto &item: fieldInfoList) { + DataValue dataValue; + StorageType type = StorageType::STORAGE_TYPE_NULL; + if (g_typeMapFunction.find(item.GetStorageType()) != g_typeMapFunction.end()) { + type = item.GetStorageType(); + } + g_typeMapFunction[type](dataValue); + rowData.push_back(std::move(dataValue)); + } +} + +void GenerateTableDataWithLog(const std::vector &fieldInfoList, TableDataWithLog &tableDataWithLog) +{ + tableDataWithLog.tableName = DEFAULT_TEXT; + for (int i = 0; i < ONE_HUNDERED; i++) { + RowDataWithLog rowDataWithLog; + GenerateRowData(fieldInfoList, rowDataWithLog.rowData); + LogInfo logInfo; + // choose first element as primary key + logInfo.dataKey = i; + tableDataWithLog.dataList.push_back(std::move(rowDataWithLog)); + } +} + +bool Equal(const LogInfo &origin, const LogInfo &target) +{ + if (origin.dataKey != target.dataKey) { + return false; + } + if (origin.device != target.device) { + return false; + } + if (origin.flag != target.flag) { + return false; + } + if (origin.hashKey != target.hashKey) { + return false; + } + if (origin.originDev != target.originDev) { + return false; + } + if (origin.timestamp != target.timestamp) { + return false; + } + if (origin.wTimestamp != target.wTimestamp) { + return false; + } + return true; +} + +bool Equal(const RowDataWithLog &origin, const OptRowDataWithLog &target) +{ + if (!Equal(origin.logInfo, target.logInfo)) { + return false; + } + if (origin.rowData.size() != target.optionalData.size()) { + return false; + } + for (uint32_t i = 0; i < origin.rowData.size(); i++) { + const auto &originData = origin.rowData[i]; + const auto &targetData = target.optionalData[i]; + if (originData != targetData) { + LOGD("VALUE NOT EQUAL!"); + return false; + } + } + return true; +} + +bool Equal(TableDataWithLog origin, OptTableDataWithLog target) +{ + if (origin.dataList.size() != target.dataList.size()) { + return false; + } + for (uint32_t i = 0; i < origin.dataList.size(); i++) { + RowDataWithLog originData = origin.dataList[i]; + OptRowDataWithLog targetData = target.dataList[i]; + if (!Equal(originData, targetData)) { + return false; + } + } + return true; +} +} + +class DistributedDBDataTransformerTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBDataTransformerTest::SetUpTestCase(void) +{ +} + +void DistributedDBDataTransformerTest::TearDownTestCase(void) +{ +} + +void DistributedDBDataTransformerTest::SetUp(void) +{ +} + +void DistributedDBDataTransformerTest::TearDown(void) +{ +} + +/** + * @tc.name: DataTransformerCheck001 + * @tc.desc: To test transformer work correctly when data contains nullptr, bool, string, double, int64, uint8_t*. + * @tc.type: Func + * @tc.require: + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBDataTransformerTest, DataTransformerCheck001, TestSize.Level1) +{ + /** + * @tc.steps: step1. generate the fieldInfoList which contains nullptr, bool, string, double, int64, uint8_t*. + */ + std::vector fieldInfoList; + int count = 0; + for (const auto &item : g_typeMapFunction) { + FieldInfo fieldInfo; + fieldInfo.SetStorageType(item.first); + fieldInfo.SetFieldName(std::to_string(count++)); + fieldInfoList.push_back(fieldInfo); + } + + /** + * @tc.steps: step2. generate an originData by fieldInfoLiist. + */ + TableDataWithLog originData; + GenerateTableDataWithLog(fieldInfoList, originData); + + /** + * @tc.steps: step3. transform originData to KV data and transform back to relationData. + * @tc.expected: get ok and value has no change + */ + std::vector dataItemList; + EXPECT_EQ(DataTransformer::TransformTableData(originData, fieldInfoList, dataItemList), E_OK); + + OptTableDataWithLog targetData; + EXPECT_EQ(DataTransformer::TransformDataItem(dataItemList, fieldInfoList, fieldInfoList, targetData), E_OK); + + EXPECT_TRUE(Equal(originData, targetData)); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_file_package_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_file_package_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a4d6cdeb097bc0e844cd6b0b059a37216c47709 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_file_package_test.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "package_file.h" +#include "platform_specific.h" +#include "securec.h" +#include "value_hash_calc.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testPath; + string g_sourcePath = "/source/"; + string g_packageResultPath = "/package_result/"; + string g_unpackResultPath = "/unpack_result/"; + FileInfo g_fileInfo; + const string PACKAGE_RESULT_FILE_NAME = "package_result.dat"; + const string NON_EXIST_PATH = "/nonexist/"; + const string FILE_NAME_1 = "file1.txt"; + const string FILE_NAME_2 = "file2.dat"; + const string FILE_CONTENT_1 = "Hello world."; + const int FILE_CONTENT_2_LEN = 4; + const char FILE_CONTENT_2[FILE_CONTENT_2_LEN] = {0x5B, 0x3A, 0x29, 0x3E}; + const int BUFFER_SIZE = 4096; + const int DEVICE_ID_LEN = 32; + const vector DIVICE_ID = {'a', 'e', 'i', 'o', 'u'}; + + void RemovePath(const string &path) + { + list files; + int errCode = OS::GetFileAttrFromPath(path, files); + ASSERT_EQ(errCode, E_OK); + string fileName; + for (auto file : files) { + fileName = path + "/" + file.fileName; + switch (file.fileType) { + case OS::FILE: + (void)remove(fileName.c_str()); + break; + case OS::PATH: + if (file.fileName != "." && file.fileName != "..") { + RemovePath(fileName); + } + break; + default: + break; + } + } + (void)OS::RemoveDBDirectory(path); + } + + bool CompareFileName(const OS::FileAttr &file1, const OS::FileAttr &file2) + { + return file1.fileName <= file2.fileName; + } + + void ComparePath(const string &path1, const string &path2) + { + list files1; + int errCode = OS::GetFileAttrFromPath(path1, files1); + ASSERT_EQ(errCode, E_OK); + files1.sort(CompareFileName); + list files2; + errCode = OS::GetFileAttrFromPath(path2, files2); + ASSERT_EQ(errCode, E_OK); + files2.sort(CompareFileName); + ASSERT_EQ(files1.size(), files2.size()); + auto fileIter1 = files1.begin(); + auto fileIter2 = files2.begin(); + vector buffer1(BUFFER_SIZE, 0); + vector buffer2(BUFFER_SIZE, 0); + string bufferStr1; + string bufferStr2; + for (; fileIter1 != files1.end() && fileIter2 != files2.end(); fileIter1++, fileIter2++) { + ASSERT_STREQ(fileIter1->fileName.c_str(), fileIter2->fileName.c_str()); + ASSERT_EQ(fileIter1->fileType, fileIter2->fileType); + ASSERT_EQ(fileIter1->fileLen, fileIter2->fileLen); + if (fileIter1->fileType != OS::FILE) { + continue; + } + ifstream file1(path1 + fileIter1->fileName, ios::out | ios::binary); + ASSERT_TRUE(file1.is_open()); + ifstream file2(path2 + fileIter2->fileName, ios::out | ios::binary); + ASSERT_TRUE(file2.is_open()); + buffer1.assign(BUFFER_SIZE, 0); + buffer2.assign(BUFFER_SIZE, 0); + for (file1.read(buffer1.data(), BUFFER_SIZE), file2.read(buffer2.data(), BUFFER_SIZE); + !(file1.eof() || file2.eof()); + file1.read(buffer1.data(), BUFFER_SIZE), file2.read(buffer2.data(), BUFFER_SIZE)) { + bufferStr1.assign(buffer1.begin(), buffer1.end()); + bufferStr2.assign(buffer2.begin(), buffer2.end()); + ASSERT_STREQ(bufferStr1.c_str(), bufferStr2.c_str()); + } + file1.close(); + file2.close(); + bufferStr1.assign(buffer1.begin(), buffer1.end()); + bufferStr2.assign(buffer2.begin(), buffer2.end()); + ASSERT_STREQ(bufferStr1.c_str(), bufferStr2.c_str()); + } + } +} + +class DistributedDBFilePackageTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBFilePackageTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testPath); + g_sourcePath = g_testPath + g_sourcePath; + g_packageResultPath = g_testPath + g_packageResultPath; + g_unpackResultPath = g_testPath + g_unpackResultPath; + (void)OS::MakeDBDirectory(g_sourcePath); + ofstream file1(g_sourcePath + FILE_NAME_1, ios::out | ios::binary | ios::trunc); + ASSERT_TRUE(file1.is_open()); + file1.write(FILE_CONTENT_1.c_str(), FILE_CONTENT_1.size()); + file1.close(); + ofstream file2(g_sourcePath + FILE_NAME_2, ios::out | ios::binary | ios::trunc); + ASSERT_TRUE(file2.is_open()); + file2.write(FILE_CONTENT_2, FILE_CONTENT_2_LEN); + file2.close(); + g_fileInfo.dbType = 1; + ValueHashCalc calc; + int errCode = calc.Initialize(); + ASSERT_EQ(errCode, E_OK); + errCode = calc.Update(DIVICE_ID); + ASSERT_EQ(errCode, E_OK); + vector deviceIDVec; + errCode = calc.GetResult(deviceIDVec); + ASSERT_EQ(errCode, E_OK); + g_fileInfo.deviceID.resize(DEVICE_ID_LEN); + g_fileInfo.deviceID.assign(deviceIDVec.begin(), deviceIDVec.end()); +} + +void DistributedDBFilePackageTest::TearDownTestCase(void) +{ + RemovePath(g_testPath); +} + +void DistributedDBFilePackageTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + (void)OS::MakeDBDirectory(g_packageResultPath); + (void)OS::MakeDBDirectory(g_unpackResultPath); +} + +void DistributedDBFilePackageTest::TearDown(void) +{ + RemovePath(g_packageResultPath); + RemovePath(g_unpackResultPath); +} + +/** + * @tc.name: PackageFileTest001 + * @tc.desc: Test file package and unpack functions. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBFilePackageTest, PackageFileTest001, TestSize.Level1) +{ + int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo); + ASSERT_EQ(errCode, E_OK); + FileInfo fileInfo; + errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_unpackResultPath, fileInfo); + ASSERT_EQ(errCode, E_OK); + ComparePath(g_sourcePath, g_unpackResultPath); + ASSERT_EQ(fileInfo.dbType, g_fileInfo.dbType); + ASSERT_EQ(fileInfo.deviceID == g_fileInfo.deviceID, true); + return; +} + +/** + * @tc.name: PackageFileTest002 + * @tc.desc: Test file package if source path is not exist. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBFilePackageTest, PackageFileTest002, TestSize.Level1) +{ + int errCode = PackageFile::PackageFiles(g_sourcePath + NON_EXIST_PATH, + g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo); + ASSERT_EQ(errCode, -E_INVALID_PATH); + return; +} + +/** + * @tc.name: PackageFileTest003 + * @tc.desc: Test file package if result path is not exist. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBFilePackageTest, PackageFileTest003, TestSize.Level1) +{ + int errCode = PackageFile::PackageFiles(g_sourcePath, + g_packageResultPath + NON_EXIST_PATH + PACKAGE_RESULT_FILE_NAME, g_fileInfo); + ASSERT_EQ(errCode, -E_INVALID_PATH); + return; +} + +/** + * @tc.name: PackageFileTest004 + * @tc.desc: Test file package if source path is empty. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBFilePackageTest, PackageFileTest004, TestSize.Level1) +{ + // Clear source files. + RemovePath(g_sourcePath); + (void)OS::MakeDBDirectory(g_sourcePath); + // Test function. + int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo); + ASSERT_EQ(errCode, -E_EMPTY_PATH); + // Create source files again. + ofstream file1(g_sourcePath + FILE_NAME_1, ios::out | ios::binary | ios::trunc); + ASSERT_TRUE(file1.is_open()); + file1.write(FILE_CONTENT_1.c_str(), FILE_CONTENT_1.size()); + file1.close(); + ofstream file2(g_sourcePath + FILE_NAME_2, ios::out | ios::binary | ios::trunc); + ASSERT_TRUE(file2.is_open()); + file2.write(FILE_CONTENT_2, 4); + file2.close(); + return; +} + +/** + * @tc.name: PackageFileTest005 + * @tc.desc: Test file unpack if source file is not exist. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBFilePackageTest, PackageFileTest005, TestSize.Level1) +{ + FileInfo fileInfo; + int errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_unpackResultPath, fileInfo); + ASSERT_EQ(errCode, -E_INVALID_PATH); + return; +} + +/** + * @tc.name: PackageFileTest006 + * @tc.desc: Test file unpack if result path is not exist. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBFilePackageTest, PackageFileTest006, TestSize.Level1) +{ + int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo); + ASSERT_EQ(errCode, E_OK); + FileInfo fileInfo; + errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, + g_unpackResultPath + NON_EXIST_PATH, fileInfo); + ASSERT_EQ(errCode, -E_INVALID_PATH); + return; +} + +/** + * @tc.name: PackageFileTest007 + * @tc.desc: Test file unpack if magic check failed. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBFilePackageTest, PackageFileTest007, TestSize.Level1) +{ + int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo); + ASSERT_EQ(errCode, E_OK); + // Change package file header. + const string REPLACE_FILE_HEADER = "test"; + fstream file(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, ios::in | ios::out | ios::binary); + ASSERT_TRUE(file.is_open()); + file.seekp(0, ios_base::beg); + file.write(REPLACE_FILE_HEADER.c_str(), REPLACE_FILE_HEADER.size()); + file.close(); + // Unpack file. + FileInfo fileInfo; + errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_unpackResultPath, fileInfo); + ASSERT_EQ(errCode, -E_INVALID_FILE); + return; +} + +/** + * @tc.name: PackageFileTest008 + * @tc.desc: Test file unpack if checksum check failed. + * @tc.type: FUNC + * @tc.require: AR000D4879 + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBFilePackageTest, PackageFileTest008, TestSize.Level1) +{ + int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo); + ASSERT_EQ(errCode, E_OK); + // Rewrite package file without file tail. + ifstream fileIn(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, ios::in | ios::binary); + ASSERT_TRUE(fileIn.is_open()); + fileIn.seekg(0, ios_base::end); + int fileLen = fileIn.tellg(); + fileIn.seekg(0, ios_base::beg); + const int CUT_TAIL = 3; + int bufferLen = fileLen > BUFFER_SIZE ? BUFFER_SIZE : fileLen - CUT_TAIL; + vector buffer(bufferLen, 0); + fileIn.read(buffer.data(), buffer.size()); + fileIn.close(); + ofstream fileOut(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, ios::out | ios::binary | ios::trunc); + ASSERT_TRUE(fileOut.is_open()); + fileOut.write(buffer.data(), buffer.size()); + fileOut.close(); + // Unpack file. + FileInfo fileInfo; + errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_unpackResultPath, fileInfo); + ASSERT_EQ(errCode, -E_INVALID_FILE); + return; +} + diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_multi_ver_vacuum_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_multi_ver_vacuum_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e0618a83f50acf1653ba4a4123837e494821c18 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_multi_ver_vacuum_test.cpp @@ -0,0 +1,753 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "multi_ver_vacuum.h" +#include "multi_ver_vacuum_executor_stub.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; + +namespace { + using Predication = std::function; + // repeatInterval in millisecond + bool RepeatCheckAsyncResult(const Predication &inPred, uint8_t repeatLimit, uint32_t repeatInterval) + { + uint8_t limit = repeatLimit; + while (limit != 0) { + if (inPred()) { + return true; + } + if (--limit == 0) { + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(repeatInterval)); + } + return false; + } + + const string DB_IDENTITY_A = "DATABASE_A"; + const string DB_IDENTITY_B = "DATABASE_B"; + const string DB_IDENTITY_C = "DATABASE_C"; + + bool CheckVacuumTaskStatus(const MultiVerVacuum &inVacuum, const string &inDbIdentifier, + VacuumTaskStatus expectStatus, uint8_t repeatLimit = 5, uint32_t repeatInterval = 100) // 5 times, 100 ms + { + return RepeatCheckAsyncResult([&inVacuum, &inDbIdentifier, expectStatus]()->bool { + VacuumTaskStatus outStatus = VacuumTaskStatus::RUN_WAIT; + int errCode = inVacuum.QueryStatus(inDbIdentifier, outStatus); + return errCode == E_OK && outStatus == expectStatus; + }, repeatLimit, repeatInterval); + } +} + +class DistributedDBMultiVerVacuumTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBMultiVerVacuumTest::SetUpTestCase(void) +{ + MultiVerVacuum::Enable(true); // Make sure functionality is enabled. +} + +void DistributedDBMultiVerVacuumTest::TearDownTestCase(void) +{ +} + +void DistributedDBMultiVerVacuumTest::SetUp() +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBMultiVerVacuumTest::TearDown() +{ +} + +/** + * @tc.name: SingleTaskNormalStatusSwitch001 + * @tc.desc: Test status switch for single task under normal operation + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerVacuumTest, SingleTaskNormalStatusSwitch001, TestSize.Level2) +{ + // Preset + MultiVerVacuum vacuum; + MultiVerVacuumExecutorStub databaseA(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + + /** + * @tc.steps: step1. launch dbTaskA for databaseA + * @tc.expected: step1. dbTaskA RUN_NING + */ + int errCode = vacuum.Launch(DB_IDENTITY_A, &databaseA); + EXPECT_EQ(errCode, E_OK); + bool stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepOne, true); + + /** + * @tc.steps: step2. pause dbTaskA + * @tc.expected: step2. dbTaskA PAUSE_DONE + */ + errCode = vacuum.Pause(DB_IDENTITY_A); + EXPECT_EQ(errCode, E_OK); + bool stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::PAUSE_DONE, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + + /** + * @tc.steps: step3. pause dbTaskA again + * @tc.expected: step3. dbTaskA PAUSE_DONE + */ + errCode = vacuum.Pause(DB_IDENTITY_A); + EXPECT_EQ(errCode, E_OK); + bool stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::PAUSE_DONE, 1); // only 1 time + EXPECT_EQ(stepThree, true); + + /** + * @tc.steps: step4. continue dbTaskA with autoRelaunch false + * @tc.expected: step4. dbTaskA PAUSE_DONE + */ + errCode = vacuum.Continue(DB_IDENTITY_A, false); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100 ms + bool stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::PAUSE_DONE); + EXPECT_EQ(stepFour, true); + + /** + * @tc.steps: step5. continue dbTaskA with autoRelaunch false again + * @tc.expected: step5. dbTaskA RUN_NING + */ + errCode = vacuum.Continue(DB_IDENTITY_A, false); + EXPECT_EQ(errCode, E_OK); + bool stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepFive, true); + + /** + * @tc.steps: step6. wait for some time + * @tc.expected: step6. dbTaskA FINISH + */ + bool stepSix = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepSix, true); +} + +/** + * @tc.name: SingleTaskNormalStatusSwitch002 + * @tc.desc: Test status switch for single task under normal operation + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerVacuumTest, SingleTaskNormalStatusSwitch002, TestSize.Level2) +{ + // Preset + MultiVerVacuum vacuum; + MultiVerVacuumExecutorStub databaseB(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + + /** + * @tc.steps: step1. launch dbTaskB for databaseB, then wait for some time + * @tc.expected: step1. dbTaskB FINISH + */ + int errCode = vacuum.Launch(DB_IDENTITY_B, &databaseB); + EXPECT_EQ(errCode, E_OK); + bool stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepOne, true); + + /** + * @tc.steps: step2. pause dbTaskB + * @tc.expected: step2. dbTaskB FINISH + */ + errCode = vacuum.Pause(DB_IDENTITY_B); + EXPECT_EQ(errCode, E_OK); + bool stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::FINISH, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + + /** + * @tc.steps: step3. continue dbTaskB with autoRelaunch false + * @tc.expected: step3. dbTaskB FINISH + */ + errCode = vacuum.Continue(DB_IDENTITY_B, false); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100 ms + bool stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::FINISH, 1); // only 1 time + EXPECT_EQ(stepThree, true); + + /** + * @tc.steps: step4. pause dbTaskB again + * @tc.expected: step4. dbTaskB FINISH + */ + errCode = vacuum.Pause(DB_IDENTITY_B); + EXPECT_EQ(errCode, E_OK); + bool stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::FINISH, 1); // only 1 time + EXPECT_EQ(stepFour, true); + + /** + * @tc.steps: step5. continue dbTaskB again with autoRelaunch true + * @tc.expected: step5. dbTaskB RUN_NING + */ + errCode = vacuum.Continue(DB_IDENTITY_B, true); + EXPECT_EQ(errCode, E_OK); + bool stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepFive, true); + + /** + * @tc.steps: step6. wait for some time + * @tc.expected: step6. dbTaskB FINISH + */ + bool stepSix = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepSix, true); +} + +/** + * @tc.name: SingleTaskNormalStatusSwitch003 + * @tc.desc: Test status switch for single task under normal operation + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerVacuumTest, SingleTaskNormalStatusSwitch003, TestSize.Level2) +{ + // Preset + MultiVerVacuum vacuum; + MultiVerVacuumExecutorStub databaseC(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + + /** + * @tc.steps: step1. launch dbTaskC for databaseC, then wait for some time + * @tc.expected: step1. dbTaskC FINISH + */ + int errCode = vacuum.Launch(DB_IDENTITY_C, &databaseC); + EXPECT_EQ(errCode, E_OK); + bool stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepOne, true); + + /** + * @tc.steps: step2. AutoRelaunch dbTaskC + * @tc.expected: step2. dbTaskC RUN_NING + */ + errCode = vacuum.AutoRelaunchOnce(DB_IDENTITY_C); + EXPECT_EQ(errCode, E_OK); + bool stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepTwo, true); + + /** + * @tc.steps: step3. Abort dbTaskC + * @tc.expected: step3. dbTaskC ABORT_DONE + */ + errCode = vacuum.Abort(DB_IDENTITY_C); + EXPECT_EQ(errCode, E_OK); + bool stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + EXPECT_EQ(stepThree, true); + + /** + * @tc.steps: step4. launch dbTaskC again + * @tc.expected: step4. dbTaskC RUN_NING + */ + errCode = vacuum.Launch(DB_IDENTITY_C, &databaseC); + EXPECT_EQ(errCode, E_OK); + bool stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepFour, true); + + /** + * @tc.steps: step5. wait for some time + * @tc.expected: step5. dbTaskC FINISH + */ + bool stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepFive, true); + + /** + * @tc.steps: step6. Abort dbTaskC again + * @tc.expected: step6. dbTaskC ABORT_DONE + */ + errCode = vacuum.Abort(DB_IDENTITY_C); + EXPECT_EQ(errCode, E_OK); + bool stepSix = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + EXPECT_EQ(stepSix, true); +} + +/** + * @tc.name: SingleTaskNormalStatusSwitch004 + * @tc.desc: Test status switch for single task under normal operation + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerVacuumTest, SingleTaskNormalStatusSwitch004, TestSize.Level2) +{ + // Preset + MultiVerVacuum vacuum; + MultiVerVacuumExecutorStub databaseA(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + + /** + * @tc.steps: step1. launch dbTaskA for databaseA, then wait for some time + * @tc.expected: step1. dbTaskA FINISH + */ + int errCode = vacuum.Launch(DB_IDENTITY_A, &databaseA); + EXPECT_EQ(errCode, E_OK); + bool stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepOne, true); + + /** + * @tc.steps: step2. pause dbTaskA + * @tc.expected: step2. dbTaskA FINISH + */ + errCode = vacuum.Pause(DB_IDENTITY_A); + EXPECT_EQ(errCode, E_OK); + bool stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::FINISH, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + + /** + * @tc.steps: step3. AutoRelaunch dbTaskA + * @tc.expected: step3. dbTaskA FINISH + */ + errCode = vacuum.AutoRelaunchOnce(DB_IDENTITY_A); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100 ms + bool stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::FINISH, 1); // only 1 time + EXPECT_EQ(stepThree, true); + + /** + * @tc.steps: step4. continue dbTaskA with autoRelaunch false + * @tc.expected: step4. dbTaskA RUN_NING + */ + errCode = vacuum.Continue(DB_IDENTITY_A, false); + EXPECT_EQ(errCode, E_OK); + bool stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepFour, true); + + /** + * @tc.steps: step5. wait for some time + * @tc.expected: step5. dbTaskA FINISH + */ + bool stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepFive, true); +} + +/** + * @tc.name: SingleTaskAbnormalStatusSwitch001 + * @tc.desc: Test status switch for single task under abnormal operation + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerVacuumTest, SingleTaskAbnormalStatusSwitch001, TestSize.Level2) +{ + // Preset + MultiVerVacuum vacuum; + MultiVerVacuumExecutorStub databaseB(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + + /** + * @tc.steps: step1. launch dbTaskB for databaseB + * @tc.expected: step1. dbTaskB RUN_NING + */ + int errCode = vacuum.Launch(DB_IDENTITY_B, &databaseB); + EXPECT_EQ(errCode, E_OK); + bool stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepOne, true); + + /** + * @tc.steps: step2. pause dbTaskB + * @tc.expected: step2. dbTaskB PAUSE_DONE + */ + errCode = vacuum.Pause(DB_IDENTITY_B); + EXPECT_EQ(errCode, E_OK); + bool stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::PAUSE_DONE, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + + /** + * @tc.steps: step3. abort dbTaskB + * @tc.expected: step3. dbTaskB ABORT_DONE + */ + errCode = vacuum.Abort(DB_IDENTITY_B); + EXPECT_EQ(errCode, E_OK); + bool stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + EXPECT_EQ(stepThree, true); + + /** + * @tc.steps: step4. launch dbTaskB again + * @tc.expected: step4. dbTaskB RUN_NING + */ + errCode = vacuum.Launch(DB_IDENTITY_B, &databaseB); + EXPECT_EQ(errCode, E_OK); + bool stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepFour, true); + + /** + * @tc.steps: step5. pause dbTaskB again + * @tc.expected: step5. dbTaskB PAUSE_DONE + */ + errCode = vacuum.Pause(DB_IDENTITY_B); + EXPECT_EQ(errCode, E_OK); + bool stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::PAUSE_DONE, 1); // only 1 time + EXPECT_EQ(stepFive, true); + + /** + * @tc.steps: step6. continue dbTaskA with autoRelaunch false + * @tc.expected: step6. dbTaskB RUN_NING + */ + errCode = vacuum.Continue(DB_IDENTITY_B, false); + EXPECT_EQ(errCode, E_OK); + bool stepSix = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepSix, true); + + /** + * @tc.steps: step7. wait for some time + * @tc.expected: step7. dbTaskB FINISH + */ + bool stepSeven = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepSeven, true); +} + +namespace { + bool ConcurrentPauseThenCheckResult(MultiVerVacuum &vacuum, const std::string &dbIdentifier) + { + int retForThreadA = E_OK; + int retForThreadB = E_OK; + int isQuitThreadA = false; + int isQuitThreadB = false; + std::thread threadA([&vacuum, &dbIdentifier, &retForThreadA, &isQuitThreadA]() { + LOGI("[ConcurrentPauseThenCheckResult] ThreadA Enter Do Pause."); + retForThreadA = vacuum.Pause(dbIdentifier); + isQuitThreadA = true; + LOGI("[ConcurrentPauseThenCheckResult] ThreadA Exit Do Pause."); + }); + std::thread threadB([&vacuum, &dbIdentifier, &retForThreadB, &isQuitThreadB]() { + LOGI("[ConcurrentPauseThenCheckResult] ThreadB Enter Do Pause."); + retForThreadB = vacuum.Pause(dbIdentifier); + isQuitThreadB = true; + LOGI("[ConcurrentPauseThenCheckResult] ThreadB Exit Do Pause."); + }); + threadA.detach(); + threadB.detach(); + bool result = RepeatCheckAsyncResult([&retForThreadA, &isQuitThreadA, &retForThreadB, &isQuitThreadB]()->bool { + LOGI("[ConcurrentPauseThenCheckResult] Check."); + return retForThreadA == E_OK && retForThreadB == E_OK && isQuitThreadA == true && isQuitThreadB == true; + }, 6, 500); // 6 time, 500 ms + if (!result) { + LOGE("[ConcurrentPauseThenCheckResult] RepeatCheckAsyncResult Fail."); + return false; + } + return CheckVacuumTaskStatus(vacuum, dbIdentifier, VacuumTaskStatus::PAUSE_DONE, 1); // only 1 time + } + + bool ConcurrentPauseAndAbortThenCheckResult(MultiVerVacuum &vacuum, const std::string &dbIdentifier) + { + int retForThreadA = E_OK; + int retForThreadB = E_OK; + int isQuitThreadA = false; + int isQuitThreadB = false; + std::thread threadA([&vacuum, &dbIdentifier, &retForThreadA, &isQuitThreadA]() { + LOGI("[ConcurrentPauseAndAbortThenCheckResult] ThreadA Enter Do Pause."); + retForThreadA = vacuum.Pause(dbIdentifier); + isQuitThreadA = true; + LOGI("[ConcurrentPauseAndAbortThenCheckResult] ThreadA Exit Do Pause."); + }); + std::thread threadB([&vacuum, &dbIdentifier, &retForThreadB, &isQuitThreadB]() { + LOGI("[ConcurrentPauseAndAbortThenCheckResult] ThreadB Enter Do Abort."); + retForThreadB = vacuum.Abort(dbIdentifier); + isQuitThreadB = true; + LOGI("[ConcurrentPauseAndAbortThenCheckResult] ThreadB Exit Do Abort."); + }); + threadA.detach(); + threadB.detach(); + bool result = RepeatCheckAsyncResult([&retForThreadA, &isQuitThreadA, &retForThreadB, &isQuitThreadB]()->bool { + LOGI("[ConcurrentPauseAndAbortThenCheckResult] Check."); // Pause May Fail if Abort First + return retForThreadB == E_OK && isQuitThreadA == true && isQuitThreadB == true; + }, 6, 500); // 6 time, 500 ms + if (!result) { + LOGE("[ConcurrentPauseAndAbortThenCheckResult] RepeatCheckAsyncResult Fail."); + return false; + } + return CheckVacuumTaskStatus(vacuum, dbIdentifier, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + } +} + +/** + * @tc.name: SingleTaskConcurrentStatusSwitch001 + * @tc.desc: Test status switch for single task under Concurrent operation + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerVacuumTest, SingleTaskConcurrentStatusSwitch001, TestSize.Level2) +{ + // Preset + MultiVerVacuum vacuum; + MultiVerVacuumExecutorStub databaseC(DbScale {1, 1, 1, 1}, 1000); // 1 For Scale, 1000 For TimeCost, 11s in Total + + /** + * @tc.steps: step1. launch dbTaskC for databaseC, databaseC is timecost + * @tc.expected: step1. dbTaskC FINISH + */ + int errCode = vacuum.Launch(DB_IDENTITY_C, &databaseC); + EXPECT_EQ(errCode, E_OK); + bool stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepOne, true); + + /** + * @tc.steps: step2. Concurrently pause dbTaskC in two thread + * @tc.expected: step2. thread can quit and dbTaskC PAUSE_DONE + */ + bool stepTwo = ConcurrentPauseThenCheckResult(vacuum, DB_IDENTITY_C); + EXPECT_EQ(stepTwo, true); + + /** + * @tc.steps: step3. continue dbTaskC with autoRelaunch false + * @tc.expected: step3. dbTaskC PAUSE_DONE + */ + errCode = vacuum.Continue(DB_IDENTITY_C, false); + EXPECT_EQ(errCode, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100 ms + bool stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::PAUSE_DONE, 1); // only 1 time + EXPECT_EQ(stepThree, true); + + /** + * @tc.steps: step4. continue dbTaskC with autoRelaunch false again + * @tc.expected: step4. dbTaskC RUN_NING + */ + errCode = vacuum.Continue(DB_IDENTITY_C, false); + EXPECT_EQ(errCode, E_OK); + bool stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepFour, true); + + /** + * @tc.steps: step5. Concurrently pause and abort dbTaskC in two thread + * @tc.expected: step5. thread can quit and dbTaskC ABORT_DONE + */ + bool stepFive = ConcurrentPauseAndAbortThenCheckResult(vacuum, DB_IDENTITY_C); + EXPECT_EQ(stepFive, true); +} + +/** + * @tc.name: SingleTaskWriteHandleOccupy001 + * @tc.desc: Test write handle occupy for single task under normal operation + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerVacuumTest, SingleTaskWriteHandleOccupy001, TestSize.Level2) +{ + // Preset + MultiVerVacuum vacuum; + MultiVerVacuumExecutorStub databaseA(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + + /** + * @tc.steps: step1. launch dbTaskA for databaseA + * @tc.expected: step1. dbTaskA RUN_NING + */ + int errCode = vacuum.Launch(DB_IDENTITY_A, &databaseA); + EXPECT_EQ(errCode, E_OK); + bool stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepOne, true); + stepOne = RepeatCheckAsyncResult([&databaseA]()->bool { + return databaseA.IsTransactionOccupied() == true; + }, 5, 100); // 5 times, 100 ms + EXPECT_EQ(stepOne, true); + + /** + * @tc.steps: step2. pause dbTaskA + * @tc.expected: step2. dbTaskA PAUSE_DONE + */ + errCode = vacuum.Pause(DB_IDENTITY_A); + EXPECT_EQ(errCode, E_OK); + bool stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::PAUSE_DONE, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + EXPECT_EQ(databaseA.IsTransactionOccupied(), false); + + /** + * @tc.steps: step3. Continue dbTaskA + * @tc.expected: step3. dbTaskA RUN_NING + */ + errCode = vacuum.Continue(DB_IDENTITY_A, false); + EXPECT_EQ(errCode, E_OK); + bool stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepThree, true); + stepThree = RepeatCheckAsyncResult([&databaseA]()->bool { + return databaseA.IsTransactionOccupied() == true; + }, 5, 100); // 5 times, 100 ms + EXPECT_EQ(stepThree, true); + + /** + * @tc.steps: step4. Abort dbTaskA + * @tc.expected: step4. dbTaskA ABORT_DONE + */ + errCode = vacuum.Abort(DB_IDENTITY_A); + EXPECT_EQ(errCode, E_OK); + bool stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + EXPECT_EQ(stepFour, true); + EXPECT_EQ(databaseA.IsTransactionOccupied(), false); +} + +/** + * @tc.name: MultipleTaskNormalStatusSwitch001 + * @tc.desc: Test status switch for multiple task under normal operation + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerVacuumTest, MultipleTaskNormalStatusSwitch001, TestSize.Level1) +{ + // Preset + MultiVerVacuum vacuum; + MultiVerVacuumExecutorStub databaseA(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + MultiVerVacuumExecutorStub databaseB(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + + /** + * @tc.steps: step1. launch dbTaskA for databaseA and dbTaskB for databaseB + * @tc.expected: step1. dbTaskA RUN_NING and dbTaskB RUN_WAIT + */ + int errCode = vacuum.Launch(DB_IDENTITY_A, &databaseA); + EXPECT_EQ(errCode, E_OK); + errCode = vacuum.Launch(DB_IDENTITY_B, &databaseB); + EXPECT_EQ(errCode, E_OK); + bool stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepOne, true); + stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_WAIT); + EXPECT_EQ(stepOne, true); + + /** + * @tc.steps: step2. pause dbTaskB + * @tc.expected: step2. dbTaskA RUN_NING and dbTaskB PAUSE_DONE + */ + errCode = vacuum.Pause(DB_IDENTITY_B); + EXPECT_EQ(errCode, E_OK); + bool stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_NING, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::PAUSE_DONE, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + /** + * @tc.steps: step3. continue dbTaskB with autoRelaunch false + * @tc.expected: step3. dbTaskA RUN_NING and dbTaskB RUN_WAIT + */ + errCode = vacuum.Continue(DB_IDENTITY_B, false); + EXPECT_EQ(errCode, E_OK); + bool stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_NING, 1); // only 1 time + EXPECT_EQ(stepThree, true); + stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_WAIT, 1); // only 1 time + EXPECT_EQ(stepThree, true); + + /** + * @tc.steps: step4. Abort dbTaskA + * @tc.expected: step4. dbTaskA ABORT_DONE and dbTaskB RUN_NING + */ + errCode = vacuum.Abort(DB_IDENTITY_A); + EXPECT_EQ(errCode, E_OK); + bool stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + EXPECT_EQ(stepFour, true); + stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepFour, true); + + /** + * @tc.steps: step5. Abort dbTaskB + * @tc.expected: step5. dbTaskA ABORT_DONE and dbTaskB ABORT_DONE + */ + errCode = vacuum.Abort(DB_IDENTITY_B); + EXPECT_EQ(errCode, E_OK); + bool stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + EXPECT_EQ(stepFive, true); + stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + EXPECT_EQ(stepFive, true); +} + +/** + * @tc.name: MultipleTaskNormalStatusSwitch002 + * @tc.desc: Test status switch for multiple task under normal operation + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerVacuumTest, MultipleTaskNormalStatusSwitch002, TestSize.Level2) +{ + // Preset + MultiVerVacuum vacuum; + MultiVerVacuumExecutorStub databaseA(DbScale {1, 1, 1, 1}, 30); // 1 For Scale, 30 For TimeCost, 330ms in Total + MultiVerVacuumExecutorStub databaseB(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + MultiVerVacuumExecutorStub databaseC(DbScale {1, 1, 2, 2}, 100); // 1, 2 For Scale, 100 For TimeCost, 1.7s in Total + + /** + * @tc.steps: step1. launch dbTaskA,B,C for databaseA,B,C and wait dbTaskA,B FINISH + * @tc.expected: step1. dbTaskA FINISH and dbTaskB FINISH and dbTaskC RUN_NING + */ + int errCode = vacuum.Launch(DB_IDENTITY_A, &databaseA); + EXPECT_EQ(errCode, E_OK); + errCode = vacuum.Launch(DB_IDENTITY_B, &databaseB); + EXPECT_EQ(errCode, E_OK); + errCode = vacuum.Launch(DB_IDENTITY_C, &databaseC); + EXPECT_EQ(errCode, E_OK); + bool stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepOne, true); + stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepOne, true); + stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepOne, true); + + /** + * @tc.steps: step2. abnormal operation, Launch dbTaskB again without abort dbTaskB + * @tc.expected: step2. dbTaskA FINISH and dbTaskB RUN_WAIT and dbTaskC RUN_NING + */ + errCode = vacuum.Launch(DB_IDENTITY_B, &databaseB); + EXPECT_EQ(errCode, E_OK); + bool stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::FINISH, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_WAIT, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + stepTwo = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::RUN_NING, 1); // only 1 time + EXPECT_EQ(stepTwo, true); + + /** + * @tc.steps: step3. AutoRelaunch dbTaskA + * @tc.expected: step3. dbTaskA RUN_WAIT and dbTaskB RUN_WAIT and dbTaskC RUN_NING + */ + errCode = vacuum.AutoRelaunchOnce(DB_IDENTITY_A); + EXPECT_EQ(errCode, E_OK); + bool stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_WAIT, 1); // only 1 time + EXPECT_EQ(stepThree, true); + stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_WAIT, 1); // only 1 time + EXPECT_EQ(stepThree, true); + stepThree = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::RUN_NING, 1); // only 1 time + EXPECT_EQ(stepThree, true); + + /** + * @tc.steps: step4. wait dbTaskC FINISH + * @tc.expected: step4. dbTaskA RUN_WAIT and dbTaskB RUN_NING and dbTaskC FINISH + */ + bool stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::FINISH, 3, 1000); // 3 time, 1000 ms + EXPECT_EQ(stepFour, true); + stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::RUN_WAIT, 1); // only 1 time + EXPECT_EQ(stepFour, true); + stepFour = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::RUN_NING); + EXPECT_EQ(stepFour, true); + + /** + * @tc.steps: step5. Abort dbTaskB and dbTaskB + * @tc.expected: step5. dbTaskA ABORT_DONE and dbTaskB ABORT_DONE and dbTaskC FINISH + */ + vacuum.Abort(DB_IDENTITY_A); + vacuum.Abort(DB_IDENTITY_B); + bool stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_A, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + EXPECT_EQ(stepFive, true); + stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::ABORT_DONE, 1); // only 1 time + EXPECT_EQ(stepFive, true); + stepFive = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_C, VacuumTaskStatus::FINISH, 1); // only 1 time + EXPECT_EQ(stepFive, true); +} diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_query_object_helper_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_query_object_helper_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eabfbabd6c7bcb074812fd065581a1bac1d17393 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_query_object_helper_test.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_errno.h" +#include "get_query_info.h" +#include "log_print.h" +#include "query_object.h" + +using namespace testing::ext; +using namespace DistributedDB; + +namespace { +const std::string VALID_SCHEMA_FULL_DEFINE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":{" + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, NOT NULL, DEFAULT '3.1415'\"," + "\"field_name7\":[]," + "\"field_name8\":{}" + "}" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2.field_name6\"]}"; +const std::string TEST_FIELD_NAME = "$.field_name2.field_name6"; + +static void GetQuerySql(const Query &query) +{ + QueryObject queryObj(query); + + SchemaObject schema; + schema.ParseFromSchemaString(VALID_SCHEMA_FULL_DEFINE); + queryObj.SetSchema(schema); + + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); + ASSERT_EQ(errCode, E_OK); + EXPECT_EQ(errCode, E_OK); + std::string sql; + helper.GetQuerySql(sql, false); + LOGD("[UNITTEST][sql] = [%s]", sql.c_str()); +} +} + +class DistributedDBQueryObjectHelperTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBQueryObjectHelperTest::SetUpTestCase(void) +{ +} + +void DistributedDBQueryObjectHelperTest::TearDownTestCase(void) +{ +} + +void DistributedDBQueryObjectHelperTest::SetUp(void) +{ +} + +void DistributedDBQueryObjectHelperTest::TearDown(void) +{ +} + +/** + * @tc.name: Query001 + * @tc.desc: Check the legal single query operation to see if the generated container is correct + * @tc.type: FUNC + * @tc.require: AR000DR9K6 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBQueryObjectHelperTest, Query001, TestSize.Level1) +{ + Query query1 = Query::Select().NotEqualTo(TEST_FIELD_NAME, 123); // random test data + GetQuerySql(query1); + + Query query2 = Query::Select().EqualTo(TEST_FIELD_NAME, true); + GetQuerySql(query2); + + Query query3 = Query::Select().GreaterThan(TEST_FIELD_NAME, 0); + GetQuerySql(query3); + + Query query4 = Query::Select().LessThan(TEST_FIELD_NAME, INT_MAX); + GetQuerySql(query4); + + Query query5 = Query::Select().GreaterThanOrEqualTo(TEST_FIELD_NAME, 1.56); // random test data + GetQuerySql(query5); + + Query query6 = Query::Select().LessThanOrEqualTo(TEST_FIELD_NAME, 100); // random test data + GetQuerySql(query6); + + std::string testValue = "employee.sun.yong"; + Query query7 = Query::Select().Like(TEST_FIELD_NAME, testValue); + GetQuerySql(query7); + + Query query8 = Query::Select().NotLike(TEST_FIELD_NAME, "testValue"); + GetQuerySql(query8); + + std::vector fieldValues{1, 1, 1}; + Query query9 = Query::Select().In(TEST_FIELD_NAME, fieldValues); + GetQuerySql(query9); + + Query query10 = Query::Select().NotIn(TEST_FIELD_NAME, fieldValues); + GetQuerySql(query10); + + Query query11 = Query::Select().OrderBy(TEST_FIELD_NAME, false); + GetQuerySql(query11); + + Query query12 = Query::Select().Limit(1, 2); + GetQuerySql(query12); + + Query query13 = Query::Select().IsNull(TEST_FIELD_NAME); + GetQuerySql(query13); +} + +/** + * @tc.name: Query002 + * @tc.desc: Check for illegal query conditions can not get helper transfer to sql + * @tc.type: FUNC + * @tc.require: AR000DR9K6 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBQueryObjectHelperTest, Query002, TestSize.Level1) +{ + float testValue = 1.1; + Query query = Query::Select().NotEqualTo(".test", testValue); + QueryObject queryObj(query); + SchemaObject schema; + schema.ParseFromSchemaString(VALID_SCHEMA_FULL_DEFINE); + queryObj.SetSchema(schema); + int errCode = E_OK; + SqliteQueryHelper helper = queryObj.GetQueryHelper(errCode); // invalid field name + EXPECT_NE(errCode, E_OK); + + Query query1 = Query::Select().GreaterThan(TEST_FIELD_NAME, true); // bool compare + QueryObject queryObj1(query1); + queryObj1.SetSchema(schema); + SqliteQueryHelper helper1 = queryObj1.GetQueryHelper(errCode); + EXPECT_NE(errCode, E_OK); + + Query query2 = Query::Select().LessThan("$.field_name2.field_name4", true); + QueryObject queryObj2(query2); + queryObj2.SetSchema(schema); + SqliteQueryHelper helper2 = queryObj2.GetQueryHelper(errCode); + EXPECT_NE(errCode, E_OK); +} + +/** + * @tc.name: Query003 + * @tc.desc: Check combination condition transfer to sql + * @tc.type: FUNC + * @tc.require: AR000DR9K6 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBQueryObjectHelperTest, Query003, TestSize.Level1) +{ + Query query = Query::Select().EqualTo(TEST_FIELD_NAME, true).And().GreaterThan(TEST_FIELD_NAME, 1); + GetQuerySql(query); + + Query query1 = Query::Select().GreaterThan(TEST_FIELD_NAME, 1).OrderBy(TEST_FIELD_NAME); + GetQuerySql(query1); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_relational_get_data_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_relational_get_data_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30bf73395d90a8c254d12098d3ac77d8550d3287 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_relational_get_data_test.cpp @@ -0,0 +1,1475 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include + +#include "data_transformer.h" +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "db_types.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "generic_single_ver_kv_entry.h" +#include "kvdb_properties.h" +#include "log_print.h" +#include "relational_schema_object.h" +#include "relational_store_delegate.h" +#include "relational_store_instance.h" +#include "relational_store_manager.h" +#include "relational_store_sqlite_ext.h" +#include "relational_sync_able_storage.h" +#include "sqlite_relational_store.h" +#include "sqlite_utils.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { +string g_testDir; +string g_storePath; +string g_storeID = "dftStoreID"; +const string g_tableName { "data" }; +DistributedDB::RelationalStoreManager g_mgr(APP_ID, USER_ID); +RelationalStoreDelegate *g_delegate = nullptr; +IRelationalStore *g_store = nullptr; + +void CreateDBAndTable() +{ + sqlite3 *db = nullptr; + int errCode = sqlite3_open(g_storePath.c_str(), &db); + if (errCode != SQLITE_OK) { + LOGE("open db failed:%d", errCode); + sqlite3_close(db); + return; + } + + const string sql = + "PRAGMA journal_mode=WAL;" + "CREATE TABLE " + g_tableName + "(key INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, value INTEGER);"; + char *zErrMsg = nullptr; + errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &zErrMsg); + if (errCode != SQLITE_OK) { + LOGE("sql error:%s", zErrMsg); + sqlite3_free(zErrMsg); + } + sqlite3_close(db); +} + +int AddOrUpdateRecord(int64_t key, int64_t value) +{ + sqlite3 *db = nullptr; + int errCode = sqlite3_open(g_storePath.c_str(), &db); + if (errCode == SQLITE_OK) { + const string sql = + "INSERT OR REPLACE INTO " + g_tableName + " VALUES(" + to_string(key) + "," + to_string(value) + ");"; + errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr); + } + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + sqlite3_close(db); + return errCode; +} + +int GetLogData(int key, uint64_t &flag, Timestamp ×tamp, const DeviceID &device = "") +{ + string tableName = g_tableName; + if (!device.empty()) { + } + const string sql = "SELECT timestamp, flag \ + FROM " + g_tableName + " as a, " + DBConstant::RELATIONAL_PREFIX + g_tableName + "_log as b \ + WHERE a.key=? AND a.rowid=b.data_key;"; + + sqlite3 *db = nullptr; + sqlite3_stmt *statement = nullptr; + int errCode = sqlite3_open(g_storePath.c_str(), &db); + if (errCode != SQLITE_OK) { + LOGE("open db failed:%d", errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + goto END; + } + errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + goto END; + } + errCode = SQLiteUtils::BindInt64ToStatement(statement, 1, key); // 1 means key's index + if (errCode != E_OK) { + goto END; + } + errCode = SQLiteUtils::StepWithRetry(statement, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + timestamp = static_cast(sqlite3_column_int64(statement, 0)); + flag = static_cast(sqlite3_column_int64(statement, 1)); + errCode = E_OK; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = -E_NOT_FOUND; + } + +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + sqlite3_close(db); + return errCode; +} + +void InitStoreProp(const std::string &storePath, const std::string &appId, const std::string &userId, + RelationalDBProperties &properties) +{ + properties.SetStringProp(RelationalDBProperties::DATA_DIR, storePath); + properties.SetStringProp(RelationalDBProperties::APP_ID, appId); + properties.SetStringProp(RelationalDBProperties::USER_ID, userId); + properties.SetStringProp(RelationalDBProperties::STORE_ID, g_storeID); + std::string identifier = userId + "-" + appId + "-" + g_storeID; + std::string hashIdentifier = DBCommon::TransferHashString(identifier); + properties.SetStringProp(RelationalDBProperties::IDENTIFIER_DATA, hashIdentifier); +} + +const RelationalSyncAbleStorage *GetRelationalStore() +{ + RelationalDBProperties properties; + InitStoreProp(g_storePath, APP_ID, USER_ID, properties); + int errCode = E_OK; + g_store = RelationalStoreInstance::GetDataBase(properties, errCode); + if (g_store == nullptr) { + LOGE("Get db failed:%d", errCode); + return nullptr; + } + return static_cast(g_store)->GetStorageEngine(); +} + +int GetCount(sqlite3 *db, const string &sql, size_t &count) +{ + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + return errCode; + } + errCode = SQLiteUtils::StepWithRetry(stmt, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + count = static_cast(sqlite3_column_int64(stmt, 0)); + errCode = E_OK; + } + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; +} + +void ExpectCount(sqlite3 *db, const string &sql, size_t expectCount) +{ + size_t count = 0; + ASSERT_EQ(GetCount(db, sql, count), E_OK); + EXPECT_EQ(count, expectCount); +} + +std::string GetOneText(sqlite3 *db, const string &sql) +{ + std::string result; + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + return result; + } + errCode = SQLiteUtils::StepWithRetry(stmt, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + SQLiteUtils::GetColumnTextValue(stmt, 0, result); + } + SQLiteUtils::ResetStatement(stmt, true, errCode); + return result; +} + +int PutBatchData(uint32_t totalCount, uint32_t valueSize) +{ + sqlite3 *db = nullptr; + sqlite3_stmt *stmt = nullptr; + const string sql = "INSERT INTO " + g_tableName + " VALUES(?,?);"; + int errCode = sqlite3_open(g_storePath.c_str(), &db); + if (errCode != SQLITE_OK) { + goto ERROR; + } + EXPECT_EQ(sqlite3_exec(db, "BEGIN IMMEDIATE TRANSACTION", nullptr, nullptr, nullptr), SQLITE_OK); + errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + goto ERROR; + } + for (uint32_t i = 0; i < totalCount; i++) { + errCode = SQLiteUtils::BindBlobToStatement(stmt, 2, Value(valueSize, 'a'), false); // 2 means value index + if (errCode != E_OK) { + break; + } + errCode = SQLiteUtils::StepWithRetry(stmt); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + break; + } + errCode = E_OK; + SQLiteUtils::ResetStatement(stmt, false, errCode); + } + +ERROR: + if (errCode == E_OK) { + EXPECT_EQ(sqlite3_exec(db, "COMMIT TRANSACTION", nullptr, nullptr, nullptr), SQLITE_OK); + } else { + EXPECT_EQ(sqlite3_exec(db, "ROLLBACK TRANSACTION", nullptr, nullptr, nullptr), SQLITE_OK); + } + SQLiteUtils::ResetStatement(stmt, true, errCode); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + sqlite3_close(db); + return errCode; +} + +void ExecSqlAndAssertOK(sqlite3 *db, const std::string &sql) +{ + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); +} + +void ExecSqlAndAssertOK(sqlite3 *db, const initializer_list &sqlList) +{ + for (const auto &sql : sqlList) { + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + } +} + +void ExpectMissQueryCnt(const std::vector &entries, size_t expectCount) +{ + size_t count = 0; + for (auto iter = entries.begin(); iter != entries.end(); ++iter) { + if (((*iter)->GetFlag() & DataItem::REMOTE_DEVICE_DATA_MISS_QUERY) == 0) { + count++; + } + auto nextOne = std::next(iter, 1); + if (nextOne != entries.end()) { + EXPECT_LT((*iter)->GetTimestamp(), (*nextOne)->GetTimestamp()); + } + } + EXPECT_EQ(count, expectCount); +}; +} + +class DistributedDBRelationalGetDataTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBRelationalGetDataTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_storePath = g_testDir + "/getDataTest.db"; + LOGI("The test db is:%s", g_testDir.c_str()); +} + +void DistributedDBRelationalGetDataTest::TearDownTestCase(void) +{} + +void DistributedDBRelationalGetDataTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + CreateDBAndTable(); +} + +void DistributedDBRelationalGetDataTest::TearDown(void) +{ + if (g_delegate != nullptr) { + EXPECT_EQ(g_mgr.CloseStore(g_delegate), DBStatus::OK); + g_delegate = nullptr; + } + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error."); + } + return; +} + +/** + * @tc.name: LogTbl1 + * @tc.desc: When put sync data to relational store, trigger generate log. + * @tc.type: FUNC + * @tc.require: AR000GK58G + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, LogTbl1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step1. Put data. + * @tc.expected: Succeed, return OK. + */ + int insertKey = 1; + int insertValue = 1; + EXPECT_EQ(AddOrUpdateRecord(insertKey, insertValue), E_OK); + + /** + * @tc.steps: step2. Check log record. + * @tc.expected: Record exists. + */ + uint64_t flag = 0; + Timestamp timestamp1 = 0; + EXPECT_EQ(GetLogData(insertKey, flag, timestamp1), E_OK); + EXPECT_EQ(flag, DataItem::LOCAL_FLAG); + EXPECT_NE(timestamp1, 0ULL); +} + +/** + * @tc.name: GetSyncData1 + * @tc.desc: GetSyncData interface + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, GetSyncData1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step1. Put 500 records. + * @tc.expected: Succeed, return OK. + */ + const size_t RECORD_COUNT = 500; + for (size_t i = 0; i < RECORD_COUNT; ++i) { + EXPECT_EQ(AddOrUpdateRecord(i, i), E_OK); + } + + /** + * @tc.steps: step2. Get all data. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(g_tableName)); + std::vector entries; + DataSizeSpecInfo sizeInfo {MTU_SIZE, 50}; + + int errCode = store->GetSyncData(query, SyncTimeRange {}, sizeInfo, token, entries); + auto count = entries.size(); + SingleVerKvEntry::Release(entries); + EXPECT_EQ(errCode, -E_UNFINISHED); + while (token != nullptr) { + errCode = store->GetSyncDataNext(entries, token, sizeInfo); + count += entries.size(); + SingleVerKvEntry::Release(entries); + EXPECT_TRUE(errCode == E_OK || errCode == -E_UNFINISHED); + } + EXPECT_EQ(count, RECORD_COUNT); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: GetSyncData2 + * @tc.desc: GetSyncData interface. For overlarge data(over 4M), ignore it. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, GetSyncData2, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step1. Put 10 records.(1M + 2M + 3M + 4M + 5M) * 2. + * @tc.expected: Succeed, return OK. + */ + for (int i = 1; i <= 5; ++i) { + EXPECT_EQ(PutBatchData(1, i * 1024 * 1024), E_OK); // 1024*1024 equals 1M. + } + for (int i = 1; i <= 5; ++i) { + EXPECT_EQ(PutBatchData(1, i * 1024 * 1024), E_OK); // 1024*1024 equals 1M. + } + + /** + * @tc.steps: step2. Get all data. + * @tc.expected: Succeed and the count is 6. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(g_tableName)); + std::vector entries; + + const size_t EXPECT_COUNT = 6; // expect 6 records. + DataSizeSpecInfo sizeInfo; + sizeInfo.blockSize = 100 * 1024 * 1024; // permit 100M. + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, sizeInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), EXPECT_COUNT); + SingleVerKvEntry::Release(entries); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: GetSyncData3 + * @tc.desc: GetSyncData interface. For deleted data. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, GetSyncData3, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step1. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + const string tableName = g_tableName + "Plus"; + std::string sql = "CREATE TABLE " + tableName + "(key INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, value INTEGER);"; + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + + /** + * @tc.steps: step2. Put 5 records with different type into "dataPlus" table. Put 5 records into "data" table. + * @tc.expected: Succeed, return OK. + */ + const size_t RECORD_COUNT = 5; // 5 records + ExecSqlAndAssertOK(db, {"INSERT INTO " + tableName + " VALUES(NULL, 1);", + "INSERT INTO " + tableName + " VALUES(NULL, 0.01);", + "INSERT INTO " + tableName + " VALUES(NULL, NULL);", + "INSERT INTO " + tableName + " VALUES(NULL, 'This is a text.');", + "INSERT INTO " + tableName + " VALUES(NULL, x'0123456789');"}); + + /** + * @tc.steps: step3. Get all data from "dataPlus" table. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(tableName)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), RECORD_COUNT); + + /** + * @tc.steps: step4. Put data into "data" table from deviceA and deviceB + * @tc.expected: Succeed, return OK. + */ + QueryObject gQuery(Query::Select(g_tableName)); + DeviceID deviceA = "deviceA"; + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(g_tableName), + DBCommon::GetDistributedTableName(deviceA, g_tableName))); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(gQuery, entries, deviceA), E_OK); + + DeviceID deviceB = "deviceB"; + auto rEntries = std::vector(entries.rbegin(), entries.rend()); + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(g_tableName), + DBCommon::GetDistributedTableName(deviceB, g_tableName))); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(gQuery, rEntries, deviceB), E_OK); + rEntries.clear(); + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps: step5. Delete 2 "dataPlus" data from deviceA. + * @tc.expected: Succeed. + */ + ExecSqlAndAssertOK(db, "DELETE FROM " + tableName + " WHERE rowid<=2;"); + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), RECORD_COUNT); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(gQuery, entries, deviceA), E_OK); + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps: step6. Check data. + * @tc.expected: 2 data in the from deviceA are deleted and all data from deviceB are not deleted. + */ + ExpectCount(db, "SELECT count(*) FROM " + DBConstant::RELATIONAL_PREFIX + g_tableName + + "_log WHERE flag&0x01=0x01;", 2U); // 2 deleted log + ExpectCount(db, "SELECT count(*) FROM " + DBConstant::RELATIONAL_PREFIX + g_tableName + "_" + + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceA)) + ";", 3U); // 3 records in A + ExpectCount(db, "SELECT count(*) FROM " + DBConstant::RELATIONAL_PREFIX + g_tableName + "_" + + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceB)) + ";", RECORD_COUNT); // 5 records in B + + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: GetQuerySyncData1 + * @tc.desc: GetSyncData interface. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, GetQuerySyncData1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step1. Put 100 records. + * @tc.expected: Succeed, return OK. + */ + const size_t RECORD_COUNT = 100; // 100 records. + for (size_t i = 0; i < RECORD_COUNT; ++i) { + EXPECT_EQ(AddOrUpdateRecord(i, i), E_OK); + } + + /** + * @tc.steps: step2. Get data limit 80, offset 30. + * @tc.expected: Get 70 records. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + const unsigned int LIMIT = 80; // limit as 80. + const unsigned int OFFSET = 30; // offset as 30. + const unsigned int EXPECT_COUNT = RECORD_COUNT - OFFSET; // expect 70 records. + QueryObject query(Query::Select(g_tableName).Limit(LIMIT, OFFSET)); + std::vector entries; + + int errCode = store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries); + EXPECT_EQ(entries.size(), EXPECT_COUNT); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(token, nullptr); + SingleVerKvEntry::Release(entries); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: GetQuerySyncData2 + * @tc.desc: GetSyncData interface. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, GetQuerySyncData2, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step1. Put 100 records. + * @tc.expected: Succeed, return OK. + */ + const size_t RECORD_COUNT = 100; // 100 records. + for (size_t i = 0; i < RECORD_COUNT; ++i) { + EXPECT_EQ(AddOrUpdateRecord(i, i), E_OK); + } + + /** + * @tc.steps: step2. Get record whose key is not equal to 10 and value is not equal to 20, order by key desc. + * @tc.expected: Succeed, Get 98 records. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + + Query query = Query::Select(g_tableName).NotEqualTo("key", 10).And().NotEqualTo("value", 20).OrderBy("key", false); + QueryObject queryObj(query); + queryObj.SetSchema(store->GetSchemaInfo()); + + std::vector entries; + EXPECT_EQ(store->GetSyncData(queryObj, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(token, nullptr); + size_t expectCount = 98; // expect 98 records. + EXPECT_EQ(entries.size(), expectCount); + for (auto iter = entries.begin(); iter != entries.end(); ++iter) { + auto nextOne = std::next(iter, 1); + if (nextOne != entries.end()) { + EXPECT_LT((*iter)->GetTimestamp(), (*nextOne)->GetTimestamp()); + } + } + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps: step3. Get record whose key is equal to 10 or value is equal to 20, order by key asc. + * @tc.expected: Succeed, Get 98 records. + */ + query = Query::Select(g_tableName).EqualTo("key", 10).Or().EqualTo("value", 20).OrderBy("key", true); + queryObj = QueryObject(query); + queryObj.SetSchema(store->GetSchemaInfo()); + + EXPECT_EQ(store->GetSyncData(queryObj, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(token, nullptr); + expectCount = 2; // expect 2 records. + EXPECT_EQ(entries.size(), expectCount); + for (auto iter = entries.begin(); iter != entries.end(); ++iter) { + auto nextOne = std::next(iter, 1); + if (nextOne != entries.end()) { + EXPECT_LT((*iter)->GetTimestamp(), (*nextOne)->GetTimestamp()); + } + } + SingleVerKvEntry::Release(entries); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: GetIncorrectTypeData1 + * @tc.desc: GetSyncData and PutSyncDataWithQuery interface. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, GetIncorrectTypeData1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step1. Create 2 index for table "data". + * @tc.expected: Succeed, return OK. + */ + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + + ExecSqlAndAssertOK(db, {"CREATE INDEX index1 ON " + g_tableName + "(value);", + "CREATE UNIQUE INDEX index2 ON " + g_tableName + "(value,key);"}); + + /** + * @tc.steps: step2. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + const string tableName = g_tableName + "Plus"; + string sql = "CREATE TABLE " + tableName + "(key INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, value INTEGER);"; + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + + /** + * @tc.steps: step3. Put 5 records with different type into "dataPlus" table. + * @tc.expected: Succeed, return OK. + */ + const size_t RECORD_COUNT = 5; // 5 sqls + ExecSqlAndAssertOK(db, {"INSERT INTO " + tableName + " VALUES(NULL, 1);", + "INSERT INTO " + tableName + " VALUES(NULL, 0.01);", + "INSERT INTO " + tableName + " VALUES(NULL, NULL);", + "INSERT INTO " + tableName + " VALUES(NULL, 'This is a text.');", + "INSERT INTO " + tableName + " VALUES(NULL, x'0123456789');"}); + + /** + * @tc.steps: step4. Get all data from "dataPlus" table. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(tableName)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), RECORD_COUNT); + + /** + * @tc.steps: step5. Put data into "data" table from deviceA. + * @tc.expected: Succeed, return OK. + */ + QueryObject queryPlus(Query::Select(g_tableName)); + const DeviceID deviceID = "deviceA"; + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(g_tableName), + DBCommon::GetDistributedTableName(deviceID, g_tableName))); + ASSERT_EQ(E_OK, SQLiteUtils::CloneIndexes(db, g_tableName, + DBCommon::GetDistributedTableName(deviceID, g_tableName))); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(queryPlus, entries, deviceID), E_OK); + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps: step6. Check data. + * @tc.expected: All data in the two tables are same. + */ + ExpectCount(db, "SELECT count(*) FROM " + tableName + " as a, " + DBConstant::RELATIONAL_PREFIX + g_tableName + + "_" + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceID)) + " as b " + "WHERE a.key=b.key AND (a.value=b.value OR (a.value is NULL AND b.value is NULL));", RECORD_COUNT); + + /** + * @tc.steps: step7. Check index. + * @tc.expected: 2 index for deviceA's data table exists. + */ + ExpectCount(db, + "SELECT count(*) FROM sqlite_master WHERE type='index' AND tbl_name='" + DBConstant::RELATIONAL_PREFIX + + g_tableName + "_" + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceID)) + "'", 2U); // 2 index + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: UpdateData1 + * @tc.desc: UpdateData succeed when the table has primary key. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, UpdateData1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step1. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + const string tableName = g_tableName + "Plus"; + std::string sql = "CREATE TABLE " + tableName + "(key INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, value INTEGER);"; + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + + /** + * @tc.steps: step2. Put 5 records with different type into "dataPlus" table. + * @tc.expected: Succeed, return OK. + */ + vector sqls = { + "INSERT INTO " + tableName + " VALUES(NULL, 1);", + "INSERT INTO " + tableName + " VALUES(NULL, 0.01);", + "INSERT INTO " + tableName + " VALUES(NULL, NULL);", + "INSERT INTO " + tableName + " VALUES(NULL, 'This is a text.');", + "INSERT INTO " + tableName + " VALUES(NULL, x'0123456789');", + }; + const size_t RECORD_COUNT = sqls.size(); + for (const auto &sql : sqls) { + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + } + + /** + * @tc.steps: step3. Get all data from "dataPlus" table. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(tableName)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), RECORD_COUNT); + + /** + * @tc.steps: step4. Put data into "data" table from deviceA for 10 times. + * @tc.expected: Succeed, return OK. + */ + query = QueryObject(Query::Select(g_tableName)); + const DeviceID deviceID = "deviceA"; + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(g_tableName), + DBCommon::GetDistributedTableName(deviceID, g_tableName))); + for (uint32_t i = 0; i < 10; ++i) { // 10 for test. + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(query, entries, deviceID), E_OK); + } + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps: step5. Check data. + * @tc.expected: There is 5 data in table. + */ + sql = "SELECT count(*) FROM " + DBConstant::RELATIONAL_PREFIX + g_tableName + "_" + + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceID)) + ";"; + size_t count = 0; + EXPECT_EQ(GetCount(db, sql, count), E_OK); + EXPECT_EQ(count, RECORD_COUNT); + + sql = "SELECT count(*) FROM " + DBConstant::RELATIONAL_PREFIX + g_tableName + "_log;"; + count = 0; + EXPECT_EQ(GetCount(db, sql, count), E_OK); + EXPECT_EQ(count, RECORD_COUNT); + + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: UpdateDataWithMulDevData1 + * @tc.desc: UpdateData succeed when there is multiple devices data exists. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, UpdateDataWithMulDevData1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + /** + * @tc.steps: step1. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + const string tableName = g_tableName + "Plus"; + std::string sql = "CREATE TABLE " + tableName + "(key INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, value INTEGER);"; + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + /** + * @tc.steps: step2. Put k1v1 into "dataPlus" table. + * @tc.expected: Succeed, return OK. + */ + sql = "INSERT INTO " + tableName + " VALUES(1, 1);"; // k1v1 + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + /** + * @tc.steps: step3. Get k1v1 from "dataPlus" table. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(tableName)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + /** + * @tc.steps: step4. Put k1v1 into "data" table from deviceA. + * @tc.expected: Succeed, return OK. + */ + query = QueryObject(Query::Select(g_tableName)); + const DeviceID deviceID = "deviceA"; + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(g_tableName), + DBCommon::GetDistributedTableName(deviceID, g_tableName))); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(query, entries, deviceID), E_OK); + SingleVerKvEntry::Release(entries); + /** + * @tc.steps: step4. Put k1v1 into "data" table. + * @tc.expected: Succeed, return OK. + */ + EXPECT_EQ(AddOrUpdateRecord(1, 1), E_OK); // k1v1 + /** + * @tc.steps: step5. Change k1v1 to k1v2 + * @tc.expected: Succeed, return OK. + */ + sql = "UPDATE " + g_tableName + " SET value=2 WHERE key=1;"; // k1v1 + EXPECT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); // change k1v1 to k1v2 + + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: MissQuery1 + * @tc.desc: Check REMOTE_DEVICE_DATA_MISS_QUERY flag succeed. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, MissQuery1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + /** + * @tc.steps: step1. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + const string tableName = g_tableName + "Plus"; + std::string sql = "CREATE TABLE " + tableName + "(key INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, value INTEGER);"; + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + + /** + * @tc.steps: step2. Put 5 records with different type into "dataPlus" table. + * @tc.expected: Succeed, return OK. + */ + ExecSqlAndAssertOK(db, {"INSERT INTO " + tableName + " VALUES(NULL, 1);", + "INSERT INTO " + tableName + " VALUES(NULL, 2);", "INSERT INTO " + tableName + " VALUES(NULL, 3);", + "INSERT INTO " + tableName + " VALUES(NULL, 4);", "INSERT INTO " + tableName + " VALUES(NULL, 5);"}); + + /** + * @tc.steps: step3. Get all data from "dataPlus" table. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + SyncTimeRange timeRange; + QueryObject query(Query::Select(tableName).EqualTo("value", 2).Or().EqualTo("value", 3).Or().EqualTo("value", 4)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, timeRange, DataSizeSpecInfo {}, token, entries), E_OK); + timeRange.lastQueryTime = (*(entries.rbegin()))->GetTimestamp(); + EXPECT_EQ(entries.size(), 3U); // 3 for test + + /** + * @tc.steps: step4. Put data into "data" table from deviceA for 10 times. + * @tc.expected: Succeed, return OK. + */ + query = QueryObject(Query::Select(g_tableName)); + const DeviceID deviceID = "deviceA"; + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(g_tableName), + DBCommon::GetDistributedTableName(deviceID, g_tableName))); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(query, entries, deviceID), E_OK); + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps: step5. Check data. + * @tc.expected: There is 3 data in table. + */ + std::string getDataSql = "SELECT count(*) FROM " + DBConstant::RELATIONAL_PREFIX + g_tableName + "_" + + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceID)) + ";"; + ExpectCount(db, getDataSql, 3); // 2,3,4 + + std::string getLogSql = "SELECT count(*) FROM " + DBConstant::RELATIONAL_PREFIX + g_tableName + "_log;"; + ExpectCount(db, getLogSql, 3); // 2,3,4 + + /** + * @tc.steps: step6. Update data. k2v2 to k2v102, k3v3 to k3v103, k4v4 to k4v104. + * @tc.expected: Update succeed. + */ + ExecSqlAndAssertOK(db, {"INSERT OR REPLACE INTO " + tableName + " VALUES(2, 102);", + "UPDATE " + tableName + " SET value=103 WHERE value=3;", + "DELETE FROM " + tableName + " WHERE key=4;", + "INSERT INTO " + tableName + " VALUES(4, 104);"}); + + /** + * @tc.steps: step7. Get all data from "dataPlus" table. + * @tc.expected: Succeed and the count is right. + */ + query = QueryObject(Query::Select(tableName).EqualTo("value", 2).Or().EqualTo("value", 3).Or().EqualTo("value", 4)); + EXPECT_EQ(store->GetSyncData(query, timeRange, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), 3U); // 3 miss query data. + + /** + * @tc.steps: step8. Put data into "data" table from deviceA for 10 times. + * @tc.expected: Succeed, return OK. + */ + query = QueryObject(Query::Select(g_tableName)); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(query, entries, deviceID), E_OK); + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps: step9. Check data. + * @tc.expected: There is 0 data in table. + */ + ExpectCount(db, getDataSql, 0U); // 0 data exists + ExpectCount(db, getLogSql, 0U); // 0 data exists + + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: CompatibleData1 + * @tc.desc: Check compatibility. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, CompatibleData1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + /** + * @tc.steps: step1. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + const string tableName = g_tableName + "Plus"; + std::string sql = "CREATE TABLE " + tableName + "(key INTEGER, value INTEGER NOT NULL, \ + extra_field TEXT NOT NULL DEFAULT 'default_value');"; + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + /** + * @tc.steps: step2. Put 1 record into data and dataPlus table. + * @tc.expected: Succeed, return OK. + */ + ASSERT_EQ(AddOrUpdateRecord(1, 101), E_OK); + sql = "INSERT INTO " + tableName + " VALUES(2, 102, 'f3');"; // k2v102 + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + /** + * @tc.steps: step3. Get all data from "data" table. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(g_tableName)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + /** + * @tc.steps: step4. Put data into "data_plus" table from deviceA. + * @tc.expected: Succeed, return OK. + */ + QueryObject queryPlus(Query::Select(tableName)); + const DeviceID deviceID = "deviceA"; + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(tableName), + DBCommon::GetDistributedTableName(deviceID, tableName))); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(queryPlus, entries, deviceID), E_OK); + SingleVerKvEntry::Release(entries); + /** + * @tc.steps: step4. Get all data from "dataPlus" table. + * @tc.expected: Succeed and the count is right. + */ + EXPECT_EQ(store->GetSyncData(queryPlus, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + /** + * @tc.steps: step5. Put data into "data" table from deviceA. + * @tc.expected: Succeed, return OK. + */ + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(g_tableName), + DBCommon::GetDistributedTableName(deviceID, g_tableName))); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(query, entries, deviceID), E_OK); + SingleVerKvEntry::Release(entries); + /** + * @tc.steps: step6. Check data. + * @tc.expected: All data in the two tables are same. + */ + sql = "SELECT count(*) FROM " + g_tableName + " as a," + DBConstant::RELATIONAL_PREFIX + tableName + "_" + + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceID)) + " as b " + + "WHERE a.key=b.key AND a.value=b.value;"; + size_t count = 0; + EXPECT_EQ(GetCount(db, sql, count), E_OK); + EXPECT_EQ(count, 1UL); + sql = "SELECT count(*) FROM " + tableName + " as a," + DBConstant::RELATIONAL_PREFIX + g_tableName + "_" + + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceID)) + " as b " + + "WHERE a.key=b.key AND a.value=b.value;"; + count = 0; + EXPECT_EQ(GetCount(db, sql, count), E_OK); + EXPECT_EQ(count, 1UL); + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: GetDataSortByTime1 + * @tc.desc: All query get data sort by time asc. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, GetDataSortByTime1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + /** + * @tc.steps: step2. Add 3 record into data. k1v105, k2v104, k3v103, timestamp desc. + * @tc.expected: Succeed, return OK. + */ + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + std::string sql = "INSERT INTO " + g_tableName + " VALUES(1, 101);"; // k1v101 + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + sql = "INSERT INTO " + g_tableName + " VALUES(2, 102);"; // k2v102 + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + sql = "INSERT INTO " + g_tableName + " VALUES(3, 103);"; // k3v103 + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + sql = "UPDATE " + g_tableName + " SET value=104 WHERE key=2;"; // k2v104 + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + sql = "UPDATE " + g_tableName + " SET value=105 WHERE key=1;"; // k1v105 + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + /** + * @tc.steps: step3. Get all data from "data" table by all query. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(g_tableName)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + ExpectMissQueryCnt(entries, 3UL); // 3 data + SingleVerKvEntry::Release(entries); + + query = QueryObject(Query::Select(g_tableName).EqualTo("key", 1).Or().EqualTo("key", 3)); + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + ExpectMissQueryCnt(entries, 2UL); // 2 data + SingleVerKvEntry::Release(entries); + + query = QueryObject(Query::Select(g_tableName).OrderBy("key", false)); + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + ExpectMissQueryCnt(entries, 3UL); // 3 data + SingleVerKvEntry::Release(entries); + + query = QueryObject(Query::Select(g_tableName).OrderBy("value", false)); + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + ExpectMissQueryCnt(entries, 3UL); // 3 data + SingleVerKvEntry::Release(entries); + + query = QueryObject(Query::Select(g_tableName).Limit(2)); + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + ExpectMissQueryCnt(entries, 2UL); // 2 data + SingleVerKvEntry::Release(entries); + + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: SameFieldWithLogTable1 + * @tc.desc: Get query data OK when the table has same field with log table. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, SameFieldWithLogTable1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + /** + * @tc.steps: step1. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + const string tableName = g_tableName + "Plus"; + std::string sql = "CREATE TABLE " + tableName + "(key INTEGER, flag INTEGER NOT NULL, \ + device TEXT NOT NULL DEFAULT 'default_value');"; + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + /** + * @tc.steps: step2. Put 1 record into dataPlus table. + * @tc.expected: Succeed, return OK. + */ + sql = "INSERT INTO " + tableName + " VALUES(1, 101, 'f3');"; // k1v101 + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + /** + * @tc.steps: step3. Get all data from dataPlus table. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(tableName).EqualTo("flag", 101).OrderBy("device", false)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + SingleVerKvEntry::Release(entries); + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: CompatibleData2 + * @tc.desc: Check compatibility. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, CompatibleData2, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + + /** + * @tc.steps: step1. Create distributed table from deviceA. + * @tc.expected: Succeed, return OK. + */ + const DeviceID deviceID = "deviceA"; + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(g_tableName), + DBCommon::GetDistributedTableName(deviceID, g_tableName))); + + /** + * @tc.steps: step2. Alter "data" table and create distributed table again. + * @tc.expected: Succeed. + */ + std::string sql = "ALTER TABLE " + g_tableName + " ADD COLUMN integer_type INTEGER DEFAULT 123 not null;" + "ALTER TABLE " + g_tableName + " ADD COLUMN text_type TEXT DEFAULT 'high_version' not null;" + "ALTER TABLE " + g_tableName + " ADD COLUMN real_type REAL DEFAULT 123.123456 not null;" + "ALTER TABLE " + g_tableName + " ADD COLUMN blob_type BLOB DEFAULT 123 not null;"; + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step3. Check deviceA's distributed table. + * @tc.expected: The create sql is correct. + */ + std::string expectSql = "CREATE TABLE naturalbase_rdb_aux_data_" + "265a9c8c3c690cdfdac72acfe7a50f748811802635d987bb7d69dc602ed3794f(key integer NOT NULL PRIMARY KEY," + "value integer, integer_type integer NOT NULL DEFAULT 123, text_type text NOT NULL DEFAULT 'high_version', " + "real_type real NOT NULL DEFAULT 123.123456, blob_type blob NOT NULL DEFAULT 123)"; + sql = "SELECT sql FROM sqlite_master WHERE tbl_name='" + DBConstant::RELATIONAL_PREFIX + g_tableName + "_" + + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceID)) + "';"; + EXPECT_EQ(GetOneText(db, sql), expectSql); + + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: PutSyncDataConflictDataTest001 + * @tc.desc: Check put with conflict sync data. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lianhuix + */ +HWTEST_F(DistributedDBRelationalGetDataTest, PutSyncDataConflictDataTest001, TestSize.Level1) +{ + const DeviceID deviceID_A = "deviceA"; + const DeviceID deviceID_B = "deviceB"; + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_storePath); + RelationalTestUtils::CreateDeviceTable(db, g_tableName, deviceID_B); + + DBStatus status = g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate); + EXPECT_EQ(status, DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + EXPECT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + auto store = const_cast(GetRelationalStore()); + ASSERT_NE(store, nullptr); + + RelationalTestUtils::ExecSql(db, "INSERT OR REPLACE INTO " + g_tableName + " (key,value) VALUES (1001,'VAL_1');"); + RelationalTestUtils::ExecSql(db, "INSERT OR REPLACE INTO " + g_tableName + " (key,value) VALUES (1002,'VAL_2');"); + RelationalTestUtils::ExecSql(db, "INSERT OR REPLACE INTO " + g_tableName + " (key,value) VALUES (1003,'VAL_3');"); + + DataSizeSpecInfo sizeInfo {MTU_SIZE, 50}; + ContinueToken token = nullptr; + QueryObject query(Query::Select(g_tableName)); + std::vector entries; + int errCode = store->GetSyncData(query, {}, sizeInfo, token, entries); + EXPECT_EQ(errCode, E_OK); + + errCode = store->PutSyncDataWithQuery(query, entries, deviceID_B); + EXPECT_EQ(errCode, E_OK); + GenericSingleVerKvEntry::Release(entries); + + QueryObject query2(Query::Select(g_tableName).EqualTo("key", 1001)); + std::vector entries2; + store->GetSyncData(query2, {}, sizeInfo, token, entries2); + + errCode = store->PutSyncDataWithQuery(query, entries2, deviceID_B); + EXPECT_EQ(errCode, E_OK); + GenericSingleVerKvEntry::Release(entries2); + + RefObject::DecObjRef(g_store); + + std::string deviceTable = DBCommon::GetDistributedTableName(deviceID_B, g_tableName); + EXPECT_EQ(RelationalTestUtils::CheckTableRecords(db, deviceTable), 3); + sqlite3_close_v2(db); +} + +/** + * @tc.name: SaveNonexistDevdata1 + * @tc.desc: Save non-exist device data and check errCode. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, SaveNonexistDevdata1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + /** + * @tc.steps: step1. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + const string tableName = g_tableName + "Plus"; + std::string sql = "CREATE TABLE " + tableName + "(key INTEGER, value INTEGER NOT NULL, \ + extra_field TEXT NOT NULL DEFAULT 'default_value');"; + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + ASSERT_EQ(sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + /** + * @tc.steps: step2. Put 1 record into data table. + * @tc.expected: Succeed, return OK. + */ + ASSERT_EQ(AddOrUpdateRecord(1, 101), E_OK); + + /** + * @tc.steps: step3. Get all data from "data" table. + * @tc.expected: Succeed and the count is right. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + ContinueToken token = nullptr; + QueryObject query(Query::Select(g_tableName)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, SyncTimeRange {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + + /** + * @tc.steps: step4. Put data into "data_plus" table from deviceA and deviceA does not exist. + * @tc.expected: Succeed, return OK. + */ + query = QueryObject(Query::Select(tableName)); + const DeviceID deviceID = "deviceA"; + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(query, entries, deviceID), + -1); // -1 means error + SingleVerKvEntry::Release(entries); + + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: GetMaxTimestamp1 + * @tc.desc: Get max timestamp. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, GetMaxTimestamp1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + /** + * @tc.steps: step1. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + const string tableName = g_tableName + "Plus"; + ExecSqlAndAssertOK(db, "CREATE TABLE " + tableName + "(key INTEGER, value INTEGER NOT NULL, \ + extra_field TEXT NOT NULL DEFAULT 'default_value');"); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + + /** + * @tc.steps: step2. Get max timestamp when no data exists. + * @tc.expected: Succeed and the time is 0; + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + + Timestamp time1 = 0; + store->GetMaxTimestamp(time1); + EXPECT_EQ(time1, 0ull); + + /** + * @tc.steps: step3. Put 1 record into data table and get max timestamp. + * @tc.expected: Succeed and the time is updated. + */ + ASSERT_EQ(AddOrUpdateRecord(1, 101), E_OK); + Timestamp time2 = 0; + store->GetMaxTimestamp(time2); + EXPECT_GT(time2, time1); + + /** + * @tc.steps: step4. Put 1 record into data table and get max timestamp. + * @tc.expected: Succeed and the time is updated. + */ + ASSERT_EQ(AddOrUpdateRecord(2, 102), E_OK); + Timestamp time3 = 0; + store->GetMaxTimestamp(time3); + EXPECT_GT(time3, time2); + + /** + * @tc.steps: step5. Put 1 record into data table and get the max timestamp of data table. + * @tc.expected: Succeed and the time is equals to max timestamp in DB. + */ + Timestamp time4 = 0; + store->GetMaxTimestamp(g_tableName, time4); + EXPECT_EQ(time4, time3); + + /** + * @tc.steps: step6. Put 1 record into data table and get the max timestamp of dataPlus table. + * @tc.expected: Succeed and the time is 0. + */ + Timestamp time5 = 0; + store->GetMaxTimestamp(tableName, time5); + EXPECT_EQ(time5, 0ull); + + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} + +/** + * @tc.name: NoPkData1 + * @tc.desc: For no pk data. + * @tc.type: FUNC + * @tc.require: AR000GK58H + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBRelationalGetDataTest, NoPkData1, TestSize.Level1) +{ + ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option {}, g_delegate), DBStatus::OK); + ASSERT_NE(g_delegate, nullptr); + + sqlite3 *db = nullptr; + ASSERT_EQ(sqlite3_open(g_storePath.c_str(), &db), SQLITE_OK); + ExecSqlAndAssertOK(db, "DROP TABLE IF EXISTS " + g_tableName + "; \ + CREATE TABLE " + g_tableName + "(key INTEGER NOT NULL, value INTEGER);"); + ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName), DBStatus::OK); + + /** + * @tc.steps: step1. Create distributed table "dataPlus". + * @tc.expected: Succeed, return OK. + */ + const string tableName = g_tableName + "Plus"; + ExecSqlAndAssertOK(db, "CREATE TABLE " + tableName + "(key INTEGER NOT NULL, value INTEGER);"); + ASSERT_EQ(g_delegate->CreateDistributedTable(tableName), DBStatus::OK); + + /** + * @tc.steps: step2. Put 2 data into "data" table. + * @tc.expected: Succeed. + */ + ASSERT_EQ(AddOrUpdateRecord(1, 1), E_OK); + ASSERT_EQ(AddOrUpdateRecord(2, 2), E_OK); + + /** + * @tc.steps: step3. Get data from "data" table. + * @tc.expected: Succeed. + */ + auto store = GetRelationalStore(); + ASSERT_NE(store, nullptr); + + ContinueToken token = nullptr; + QueryObject query(Query::Select(g_tableName)); + std::vector entries; + EXPECT_EQ(store->GetSyncData(query, {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), 2U); // expect 2 data. + + /** + * @tc.steps: step4. Put data into "data" table from deviceA. + * @tc.expected: Succeed, return OK. + */ + QueryObject queryPlus(Query::Select(tableName)); + const DeviceID deviceID = "deviceA"; + ASSERT_EQ(E_OK, SQLiteUtils::CreateSameStuTable(db, store->GetSchemaInfo().GetTable(tableName), + DBCommon::GetDistributedTableName(deviceID, tableName))); + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(queryPlus, entries, deviceID), E_OK); + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps: step5. Changet data in "data" table. + * @tc.expected: Succeed. + */ + ExecSqlAndAssertOK(db, {"UPDATE " + g_tableName + " SET value=101 WHERE key=1;", + "DELETE FROM " + g_tableName + " WHERE key=2;", + "INSERT INTO " + g_tableName + " VALUES(2, 102);"}); + + /** + * @tc.steps: step6. Get data from "data" table. + * @tc.expected: Succeed. + */ + EXPECT_EQ(store->GetSyncData(query, {}, DataSizeSpecInfo {}, token, entries), E_OK); + EXPECT_EQ(entries.size(), 2U); // expect 2 data. + + /** + * @tc.steps: step7. Put data into "data" table from deviceA. + * @tc.expected: Succeed, return OK. + */ + EXPECT_EQ(const_cast(store)->PutSyncDataWithQuery(queryPlus, entries, deviceID), E_OK); + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps: step8. Check data. + * @tc.expected: There is 2 data. + */ + std::string sql = "SELECT count(*) FROM " + DBConstant::RELATIONAL_PREFIX + tableName + "_" + + DBCommon::TransferStringToHex(DBCommon::TransferHashString(deviceID)) + ";"; + size_t count = 0; + EXPECT_EQ(GetCount(db, sql, count), E_OK); + EXPECT_EQ(count, 2U); // expect 2 data. + + sqlite3_close(db); + RefObject::DecObjRef(g_store); +} +#endif diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_sqlite_register_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_sqlite_register_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6219f1edc31702f13be538998a03f5c1a1aa050b --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_sqlite_register_test.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "distributeddb_tools_unit_test.h" +#include "platform_specific.h" +#include "sqlite_import.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + char *g_errMsg = nullptr; + sqlite3 *g_sqliteDb = nullptr; + + const char * const DB_NAME = "test.db"; + const char * const SQL_HASH = "SELECT CALC_HASH_KEY(KEY) FROM ADDRESS_TEST"; +#ifndef OMIT_JSON + const char * const SQL_JSON_RIGHT = "SELECT * FROM ADDRESS_TEST \ + WHERE JSON_EXTRACT_BY_PATH(VALUE, '$.population', 0) > 800000"; + const char * const SQL_JSON_WRONG_PATH = "SELECT * FROM ADDRESS_TEST \ + WHERE JSON_EXTRACT_BY_PATH(VALUE, '.populationWrong', 0) > 800000"; + const char * const SQL_JSON_WRONG_ARGS = "SELECT * FROM ADDRESS_TEST \ + WHERE JSON_EXTRACT_BY_PATH(VALUE, '$.population') > 800000"; +#endif + const char * const SQL_CREATE_TABLE = "CREATE TABLE IF NOT EXISTS ADDRESS_TEST(" \ + "KEY BLOB NOT NULL PRIMARY KEY," \ + "VALUE BLOB NOT NULL);"; + + const char * const SQL_INSERT = "INSERT INTO ADDRESS_TEST (KEY, VALUE)" \ + "VALUES ('1', '{\"province\":\"hunan\", \"city\":\"shaoyang\", \"population\":1000000}');" \ + "INSERT INTO ADDRESS_TEST (KEY, VALUE)" \ + "VALUES ('12', '{\"province\":\"jiangsu\", \"city\":\"nanjing\", \"population\":3500000}');" \ + "INSERT INTO ADDRESS_TEST (KEY, VALUE)" \ + "VALUES ('123', '{\"province\":\"guangdong\", \"city\":\"shenzhen\", \"population\":700000}');"; + +#ifndef OMIT_JSON + int Callback(void *data, int argc, char **argv, char **azColName) + { + for (int i = 0; i < argc; i++) { + LOGI("%s = %s", azColName[i], argv[i] ? argv[i] : "NULL"); + } + return 0; + } +#endif +} + +class DistributedDBSqliteRegisterTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSqliteRegisterTest::SetUpTestCase(void) +{ + DistributedDB::OS::RemoveFile(DB_NAME); + int errCode = sqlite3_open(DB_NAME, &g_sqliteDb); + ASSERT_EQ(errCode, SQLITE_OK); + errCode = SQLiteUtils::RegisterJsonFunctions(g_sqliteDb); + ASSERT_EQ(errCode, SQLITE_OK); + + errCode = sqlite3_exec(g_sqliteDb, SQL_CREATE_TABLE, nullptr, nullptr, &g_errMsg); + if (errCode != SQLITE_OK) { + if (g_errMsg != nullptr) { + LOGE("SQL error: %s\n", g_errMsg); + sqlite3_free(g_errMsg); + g_errMsg = nullptr; + } + } + ASSERT_EQ(errCode, SQLITE_OK); + + errCode = sqlite3_exec(g_sqliteDb, SQL_INSERT, nullptr, nullptr, &g_errMsg); + if (errCode != SQLITE_OK) { + if (g_errMsg != nullptr) { + LOGE("SQL error: %s\n", g_errMsg); + sqlite3_free(g_errMsg); + g_errMsg = nullptr; + } + } + ASSERT_EQ(errCode, SQLITE_OK); +} + +void DistributedDBSqliteRegisterTest::TearDownTestCase(void) +{ + if (g_sqliteDb != nullptr) { + sqlite3_close(g_sqliteDb); + } + if (g_errMsg != nullptr) { + sqlite3_free(g_errMsg); + g_errMsg = nullptr; + } +} + +void DistributedDBSqliteRegisterTest::SetUp() +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBSqliteRegisterTest::TearDown() +{ +} +#ifndef OMIT_JSON +/** + * @tc.name: JsonExtract001 + * @tc.desc: test json_extract_by_path function in sqlite + * @tc.type: FUNC + * @tc.require: AR000DR9K7 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBSqliteRegisterTest, JsonExtract001, TestSize.Level1) +{ + ASSERT_NE(g_sqliteDb, nullptr); + int errCode = sqlite3_exec(g_sqliteDb, SQL_JSON_RIGHT, Callback, nullptr, &g_errMsg); + if (errCode != SQLITE_OK) { + if (g_errMsg != nullptr) { + LOGE("SQL error: %s\n", g_errMsg); + sqlite3_free(g_errMsg); + g_errMsg = nullptr; + } + } + ASSERT_EQ(errCode, SQLITE_OK); +} + +/** + * @tc.name: JsonExtract002 + * @tc.desc: test json_extract_by_path function in sqlite + * @tc.type: FUNC + * @tc.require: AR000DR9K7 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBSqliteRegisterTest, JsonExtract002, TestSize.Level1) +{ + ASSERT_NE(g_sqliteDb, nullptr); + if (g_sqliteDb == nullptr) { + LOGE("Sqlite DB not exists."); + return; + } + int errCode = sqlite3_exec(g_sqliteDb, SQL_JSON_WRONG_PATH, Callback, nullptr, &g_errMsg); + if (errCode != SQLITE_OK) { + if (g_errMsg != nullptr) { + LOGE("SQL error: %s\n", g_errMsg); + sqlite3_free(g_errMsg); + g_errMsg = nullptr; + } + } + ASSERT_TRUE(errCode != SQLITE_OK); +} + +/** + * @tc.name: JsonExtract003 + * @tc.desc: test json_extract_by_path function in sqlite + * @tc.type: FUNC + * @tc.require: AR000DR9K7 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBSqliteRegisterTest, JsonExtract003, TestSize.Level1) +{ + ASSERT_NE(g_sqliteDb, nullptr); + int errCode = sqlite3_exec(g_sqliteDb, SQL_JSON_WRONG_ARGS, Callback, nullptr, &g_errMsg); + if (errCode != SQLITE_OK) { + if (g_errMsg != nullptr) { + LOGE("SQL error: %s\n", g_errMsg); + sqlite3_free(g_errMsg); + g_errMsg = nullptr; + } + } + ASSERT_NE(errCode, SQLITE_OK); +} +#endif +/** + * @tc.name: CalcHashValue001 + * @tc.desc: test calc_hash_key function in sqlite + * @tc.type: FUNC + * @tc.require: AR000DR9K7 + * @tc.author: yiguang + */ +HWTEST_F(DistributedDBSqliteRegisterTest, CalcHashValue001, TestSize.Level1) +{ + ASSERT_NE(g_sqliteDb, nullptr); + int errCode = sqlite3_exec(g_sqliteDb, SQL_HASH, nullptr, nullptr, &g_errMsg); + if (errCode != SQLITE_OK) { + if (g_errMsg != nullptr) { + LOGE("SQL error: %s\n", g_errMsg); + sqlite3_free(g_errMsg); + g_errMsg = nullptr; + } + } + ASSERT_EQ(errCode, SQLITE_OK); +} diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_commit_storage_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_commit_storage_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..615b8eb5dd547a714bf922f517788a4666c6faa9 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_commit_storage_test.cpp @@ -0,0 +1,839 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_errno.h" +#include "default_factory.h" +#include "distributeddb_tools_unit_test.h" +#include "ikvdb_factory.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + IKvDBCommitStorage::Property g_prop; + IKvDBCommitStorage *g_commitStorage = nullptr; + bool g_createFactory = false; + Version g_defaultCommitVer1 = 1; + Version g_defaultCommitVer2 = 2; + Version g_defaultCommitVer3 = 3; + Version g_defaultCommitVer4 = 4; + Version g_defaultCommitVer5 = 5; + Version g_defaultCommitVer6 = 6; + Version g_defaultCommitVer7 = 7; + Version g_defaultCommitVer8 = 8; + Version g_defaultCommitVer9 = 9; + Version g_defaultCommitVer10 = 10; + Version g_defaultCommitVer11 = 11; + Version g_defaultCommitVer12 = 12; + string g_defaultCommitID0 = ""; + string g_defaultCommitID1 = "commit_ID_1"; + string g_defaultCommitID2 = "commit_ID_2"; + string g_defaultCommitID3 = "commit_ID_3"; + string g_defaultCommitID4 = "commit_ID_4"; + string g_defaultCommitID5 = "commit_ID_5"; + string g_defaultCommitID6 = "commit_ID_6"; + string g_defaultCommitID7 = "commit_ID_7"; + string g_defaultCommitID8 = "commit_ID_8"; + string g_defaultCommitID9 = "commit_ID_9"; + string g_defaultCommitID10 = "commit_ID_10"; + string g_defaultCommitID11 = "commit_ID_11"; + string g_defaultCommitID12 = "commit_ID_12"; + string g_defaultCommitID13 = "commit_ID_13"; + string g_defaultCommitID14 = "commit_ID_14"; + DeviceID g_localDevice = "local"; + DeviceID g_remoteDeviceA = "remote_device_A"; + DeviceID g_remoteDeviceB = "remote_device_B"; + DeviceID g_remoteDeviceC = "remote_device_C"; + DeviceID g_remoteDeviceD = "remote_device_D"; + const Timestamp TIME_STAMP1 = 100; + const Timestamp TIME_STAMP2 = 200; + const Timestamp TIME_STAMP3 = 300; + const Timestamp TIME_STAMP4 = 400; + const Timestamp TIME_STAMP5 = 500; + const Timestamp TIME_STAMP6 = 600; + const Timestamp TIME_STAMP7 = 700; + const Timestamp TIME_STAMP8 = 800; + const Timestamp TIME_STAMP9 = 900; + const Timestamp TIME_STAMP10 = 1000; + const Timestamp TIME_STAMP11 = 1100; + const Timestamp TIME_STAMP12 = 1200; + + struct CommitInfo { + Version version; + string commitID; + string leftCommitID; + string rightCommitID; + Timestamp timestamp; + bool localFlag; + DeviceID deviceInfo; + }; + + string TransCommitIDToStr(const CommitID &inputCommitID) + { + string commitIDStr = ""; + if (inputCommitID.size() != 0) { + commitIDStr.resize(inputCommitID.size()); + commitIDStr.assign(inputCommitID.begin(), inputCommitID.end()); + } + return commitIDStr; + } + + void CompareCommitWithExpectation(const IKvDBCommit *commit, const CommitInfo &commitInfo) + { + Version versionInfo = commit->GetCommitVersion(); + ASSERT_EQ(versionInfo, commitInfo.version); + CommitID commitID = commit->GetCommitId(); + string commitIDStr = TransCommitIDToStr(commitID); + ASSERT_STREQ(commitIDStr.c_str(), commitInfo.commitID.c_str()); + CommitID leftCommitID = commit->GetLeftParentId(); + string leftCommitIDStr = TransCommitIDToStr(leftCommitID); + ASSERT_STREQ(leftCommitIDStr.c_str(), commitInfo.leftCommitID.c_str()); + CommitID rightCommitID = commit->GetRightParentId(); + string rightCommitIDStr = TransCommitIDToStr(rightCommitID); + ASSERT_STREQ(rightCommitIDStr.c_str(), commitInfo.rightCommitID.c_str()); + Timestamp timestamp = commit->GetTimestamp(); + ASSERT_EQ(timestamp, commitInfo.timestamp); + bool localFlag = commit->GetLocalFlag(); + ASSERT_EQ(localFlag, commitInfo.localFlag); + DeviceID deviceInfo = commit->GetDeviceInfo(); + ASSERT_EQ(deviceInfo == commitInfo.deviceInfo, true); + } + + void TestLatestCommitOfDevice(const std::map &latestCommits, + const DeviceID &deviceInfo, const CommitInfo &expectCommitInfo) + { + auto latestCommit = latestCommits.find(deviceInfo); + ASSERT_EQ(latestCommit != latestCommits.end(), true); + CompareCommitWithExpectation(latestCommit->second, expectCommitInfo); + } + + CommitID TransStrToCommitID(const string &commitIDStr) + { + CommitID commitID; + commitID.resize(commitIDStr.size()); + if (commitIDStr.size() != 0) { + commitID.assign(commitIDStr.begin(), commitIDStr.end()); + } + return commitID; + } + + void InsertCommitToCommitStorage(const CommitInfo &commitInfo, int expectedResult) + { + int result; + IKvDBCommit *commit = g_commitStorage->AllocCommit(result); + ASSERT_EQ(result, E_OK); + ASSERT_NE(commit, nullptr); + if (result != E_OK || commit == nullptr) { + return; + } + CommitID commitID = TransStrToCommitID(commitInfo.commitID); + CommitID leftCommitID = TransStrToCommitID(commitInfo.leftCommitID); + CommitID rightCommitID = TransStrToCommitID(commitInfo.rightCommitID); + commit->SetCommitVersion(commitInfo.version); + commit->SetLeftParentId(leftCommitID); + commit->SetRightParentId(rightCommitID); + commit->SetCommitId(commitID); + commit->SetTimestamp(commitInfo.timestamp); + commit->SetLocalFlag(commitInfo.localFlag); + commit->SetDeviceInfo(commitInfo.deviceInfo); + result = g_commitStorage->AddCommit(*commit, false); + ASSERT_EQ(result, expectedResult); + g_commitStorage->ReleaseCommit(commit); + commit = nullptr; + } + + void DeleteCommit(const string &inputCommitID, int expectedResult) + { + CommitID commitID = TransStrToCommitID(inputCommitID); + int result = g_commitStorage->RemoveCommit(commitID); + ASSERT_EQ(result, expectedResult); + } + + void TestCommit(const CommitInfo &commitInfo, int expectedResult) + { + int result; + CommitID inputCommitID = TransStrToCommitID(commitInfo.commitID); + IKvDBCommit *commit = g_commitStorage->GetCommit(inputCommitID, result); + ASSERT_EQ(result, expectedResult); + if (expectedResult == E_OK) { + ASSERT_NE(commit, nullptr); + } else { + ASSERT_EQ(commit, nullptr); + } + if (result != E_OK || commit == nullptr) { + return; + } + CompareCommitWithExpectation(commit, commitInfo); + g_commitStorage->ReleaseCommit(commit); + commit = nullptr; + } + + void SetCommitStorageHeader(const string &inputHeader, int expectedResult) + { + CommitID header = TransStrToCommitID(inputHeader); + int result = g_commitStorage->SetHeader(header); + ASSERT_EQ(result, expectedResult); + } + + void TestCommitStorageHeader(const string &expectedHeader) + { + int errCode = E_OK; + CommitID header = g_commitStorage->GetHeader(errCode); + string headerStr = TransCommitIDToStr(header); + ASSERT_STREQ(headerStr.c_str(), expectedHeader.c_str()); + } + + /* + * commit tree is as below: + * L A B C D + * 1 2 4 8 d + * |/|/| | + * 3 / | 9 + * |X| |/| + * 5 6 a e + * |/|/ + * 7 b + * |/ + * c + */ + void PrepareCommitTree() + { + CommitInfo commitInfo1 = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + CommitInfo commitInfo2 = {g_defaultCommitVer2, g_defaultCommitID2, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP2, false, g_remoteDeviceA}; + CommitInfo commitInfo3 = {g_defaultCommitVer3, g_defaultCommitID3, g_defaultCommitID1, g_defaultCommitID2, + TIME_STAMP3, true, g_localDevice}; + CommitInfo commitInfo4 = {g_defaultCommitVer4, g_defaultCommitID4, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP4, false, g_remoteDeviceB}; + CommitInfo commitInfo5 = {g_defaultCommitVer5, g_defaultCommitID5, g_defaultCommitID3, g_defaultCommitID4, + TIME_STAMP5, true, g_localDevice}; + CommitInfo commitInfo6 = {g_defaultCommitVer6, g_defaultCommitID6, g_defaultCommitID2, g_defaultCommitID3, + TIME_STAMP6, false, g_remoteDeviceA}; + CommitInfo commitInfo7 = {g_defaultCommitVer7, g_defaultCommitID7, g_defaultCommitID5, g_defaultCommitID6, + TIME_STAMP7, true, g_localDevice}; + CommitInfo commitInfo8 = {g_defaultCommitVer8, g_defaultCommitID8, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP8, false, g_remoteDeviceC}; + CommitInfo commitInfo9 = {g_defaultCommitVer9, g_defaultCommitID9, g_defaultCommitID8, g_defaultCommitID0, + TIME_STAMP9, false, g_remoteDeviceC}; + CommitInfo commitInfo10 = {g_defaultCommitVer10, g_defaultCommitID10, g_defaultCommitID4, g_defaultCommitID9, + TIME_STAMP10, false, g_remoteDeviceB}; + CommitInfo commitInfo11 = {g_defaultCommitVer11, g_defaultCommitID11, g_defaultCommitID6, g_defaultCommitID10, + TIME_STAMP11, false, g_remoteDeviceA}; + CommitInfo commitInfo12 = {g_defaultCommitVer12, g_defaultCommitID12, g_defaultCommitID7, g_defaultCommitID11, + TIME_STAMP12, true, g_localDevice}; + InsertCommitToCommitStorage(commitInfo1, E_OK); + InsertCommitToCommitStorage(commitInfo2, E_OK); + InsertCommitToCommitStorage(commitInfo3, E_OK); + InsertCommitToCommitStorage(commitInfo4, E_OK); + InsertCommitToCommitStorage(commitInfo5, E_OK); + InsertCommitToCommitStorage(commitInfo6, E_OK); + InsertCommitToCommitStorage(commitInfo7, E_OK); + InsertCommitToCommitStorage(commitInfo8, E_OK); + InsertCommitToCommitStorage(commitInfo9, E_OK); + InsertCommitToCommitStorage(commitInfo10, E_OK); + InsertCommitToCommitStorage(commitInfo11, E_OK); + InsertCommitToCommitStorage(commitInfo12, E_OK); + SetCommitStorageHeader(g_defaultCommitID12, E_OK); + } +} + +class DistributedDBStorageCommitStorageTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageCommitStorageTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_prop.path); + g_prop.isNeedCreate = true; + if (IKvDBFactory::GetCurrent() == nullptr) { + IKvDBFactory *factory = new (std::nothrow) DefaultFactory(); + ASSERT_NE(factory, nullptr); + if (factory == nullptr) { + LOGE("failed to new DefaultFactory!"); + return; + } + IKvDBFactory::Register(factory); + g_createFactory = true; + } +} + +void DistributedDBStorageCommitStorageTest::TearDownTestCase(void) +{ + if (g_createFactory) { + if (IKvDBFactory::GetCurrent() != nullptr) { + delete IKvDBFactory::GetCurrent(); + IKvDBFactory::Register(nullptr); + } + } + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_prop.path) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBStorageCommitStorageTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + ASSERT_NE(factory, nullptr); + if (factory == nullptr) { + LOGE("failed to get DefaultFactory!"); + return; + } + int result; + g_commitStorage = factory->CreateMultiVerCommitStorage(result); + ASSERT_EQ(result, E_OK); + ASSERT_NE(g_commitStorage, nullptr); + if (g_commitStorage == nullptr) { + return; + } + + int errCode = g_commitStorage->Open(g_prop); + ASSERT_EQ(errCode, E_OK); + if (errCode != E_OK) { + delete g_commitStorage; + g_commitStorage = nullptr; + return; + } +} + +void DistributedDBStorageCommitStorageTest::TearDown(void) +{ + if (g_commitStorage != nullptr) { + (void)g_commitStorage->Remove(g_prop); + delete g_commitStorage; + g_commitStorage = nullptr; + } +} + +/** + * @tc.name: MultiVerCommitStorage001 + * @tc.desc: Open a commit storage when it has been opened. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage001, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Open commit log database + * @tc.expected: step1/2. Return OK. + */ + int result = g_commitStorage->Open(g_prop); + ASSERT_EQ(result, E_OK); + return; +} + +/** + * @tc.name: MultiVerCommitStorage002 + * @tc.desc: Remove a commit storage database, then try to add, delete and query commit, set and get header. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage002, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Remove commit log database + * @tc.expected: step1/2. Return OK. + */ + int result = g_commitStorage->Remove(g_prop); + ASSERT_EQ(result, E_OK); + if (result != E_OK) { + return; + } + CommitInfo commitInfo = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step3/4. Insert recording commit log database + * @tc.expected: step3/4. Return E_INVALID_DB. + */ + InsertCommitToCommitStorage(commitInfo, -E_INVALID_DB); + + /** + * @tc.steps:step5/6. Delete commit History to commit log database + * @tc.expected: step5/6. Return E_INVALID_DB. + */ + DeleteCommit(g_defaultCommitID1, -E_INVALID_DB); + + /** + * @tc.steps:step7/8. Cheeck commit History to commit log database + * @tc.expected: step7/8. Return E_INVALID_DB. + */ + TestCommit(commitInfo, -E_INVALID_DB); + + /** + * @tc.steps:step9/10. Cheeck change commit header + * @tc.expected: step7/10. Return E_INVALID_DB. + */ + SetCommitStorageHeader(g_defaultCommitID1, -E_INVALID_DB); + + /** + * @tc.steps:step11/12. Cheeck query commit header + * @tc.expected: step11/12. Return failed. + */ + TestCommitStorageHeader(g_defaultCommitID0); +} + +/** + * @tc.name: MultiVerCommitStorage003 + * @tc.desc: Insert a commit to commit storage, and get it. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage003, TestSize.Level1) +{ + CommitInfo commitInfo = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1. Insert recording commit log database + * @tc.expected: step1. Return E_OK. + */ + InsertCommitToCommitStorage(commitInfo, E_OK); + + /** + * @tc.steps:step2. Cheeck commit History to commit log database + * @tc.expected: step2. Return E_OK. + */ + TestCommit(commitInfo, E_OK); + return; +} + +/** + * @tc.name: MultiVerCommitStorage004 + * @tc.desc: Set header of commit storage, and get it. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage004, TestSize.Level1) +{ + CommitInfo commitInfo = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1. Insert recording commit log database + * @tc.expected: step1. Return E_OK. + */ + InsertCommitToCommitStorage(commitInfo, E_OK); + + /** + * @tc.steps:step2. Cheeck change commit header + * @tc.expected: step2. Return E_OK. + */ + SetCommitStorageHeader(g_defaultCommitID1, E_OK); + + /** + * @tc.steps:step3. Cheeck query commit header + * @tc.expected: step3. Return success. + */ + TestCommitStorageHeader(g_defaultCommitID1); + return; +} + +/** + * @tc.name: MultiVerCommitStorage005 + * @tc.desc: Delete the header commit, test if it can be get, and get the new header. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage005, TestSize.Level1) +{ + CommitInfo commitInfo1 = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1. Insert recording commitInfo1 commit log database + * @tc.expected: step1. Return E_OK. + */ + InsertCommitToCommitStorage(commitInfo1, E_OK); + CommitInfo commitInfo2 = {g_defaultCommitVer2, g_defaultCommitID2, g_defaultCommitID1, g_defaultCommitID0, + TIME_STAMP2, true, g_localDevice}; + + /** + * @tc.steps:step2. Insert recording commitInfo2 commit log database + * @tc.expected: step2. Return E_OK. + */ + InsertCommitToCommitStorage(commitInfo2, E_OK); + + /** + * @tc.steps:step3. Cheeck change commit header + * @tc.expected: step3. Return E_OK. + */ + SetCommitStorageHeader(g_defaultCommitID2, E_OK); + + /** + * @tc.steps:step4. Delete commit History to commit log database + * @tc.expected: step4. Return E_INVALID_DB. + */ + DeleteCommit(g_defaultCommitID2, E_OK); + + /** + * @tc.steps:step5. Cheeck query commit header + * @tc.expected: step5. Return success. + */ + TestCommitStorageHeader(g_defaultCommitID1); + + /** + * @tc.steps:step6. Cheeck commit History to commit log database + * @tc.expected: step6. Return E_OK. + */ + TestCommit(commitInfo1, E_OK); + + /** + * @tc.steps:step7. Cheeck commit History to commit log database + * @tc.expected: step7. Return E_OK. + */ + TestCommit(commitInfo2, -E_NOT_FOUND); + return; +} + +/** + * @tc.name: MultiVerCommitStorage006 + * @tc.desc: Add commit with empty commit ID, and it will not be added. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage006, TestSize.Level1) +{ + CommitInfo commitInfo = {g_defaultCommitVer1, g_defaultCommitID0, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1/2. Insert commit ID is null to commit log database + * @tc.expected: step1/2. Return E_UNEXPECTED_DATA. + */ + InsertCommitToCommitStorage(commitInfo, -E_UNEXPECTED_DATA); +} + +/** + * @tc.name: MultiVerCommitStorage008 + * @tc.desc: Add commit with the same commit ID as its left parent, and it will not be added. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage008, TestSize.Level1) +{ + CommitInfo commitInfo1 = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1. Insert a recording to commit log database + * @tc.expected: step1. Return E_OK. + */ + InsertCommitToCommitStorage(commitInfo1, E_OK); + CommitInfo commitInfo2 = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID1, g_defaultCommitID0, + TIME_STAMP2, true, g_localDevice}; + + /** + * @tc.steps:step2. Add commit with the same commit ID as its right parent + * @tc.expected: step2. Return E_UNEXPECTED_DATA. + */ + InsertCommitToCommitStorage(commitInfo2, -E_UNEXPECTED_DATA); + + /** + * @tc.steps:step3. Cheeck commit History to commit log database + * @tc.expected: step3. Return E_OK. + */ + TestCommit(commitInfo1, E_OK); +} + +/** + * @tc.name: MultiVerCommitStorage009 + * @tc.desc: Add commit with the same commit ID as its right parent, and it will not be added. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage009, TestSize.Level1) +{ + CommitInfo commitInfo1 = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1. Insert a recording to commit log database + * @tc.expected: step1. Return E_OK. + */ + InsertCommitToCommitStorage(commitInfo1, E_OK); + CommitInfo commitInfo2 = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID1, + TIME_STAMP2, true, g_localDevice}; + + /** + * @tc.steps:step2. Add commit with the same commit ID as its right parent + * @tc.expected: step2. Return E_UNEXPECTED_DATA. + */ + InsertCommitToCommitStorage(commitInfo2, -E_UNEXPECTED_DATA); + + /** + * @tc.steps:step3. Cheeck commit History to commit log database + * @tc.expected: step3. Return E_OK. + */ + TestCommit(commitInfo1, E_OK); +} + +/** + * @tc.name: MultiVerCommitStorage010 + * @tc.desc: Add commit whose left parent and right parent is the same, and it will not be added. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage010, TestSize.Level1) +{ + CommitInfo commitInfo1 = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1. Insert a recording to commit log database + * @tc.expected: step1. Return E_OK. + */ + InsertCommitToCommitStorage(commitInfo1, E_OK); + CommitInfo commitInfo2 = {g_defaultCommitVer1, g_defaultCommitID2, g_defaultCommitID1, g_defaultCommitID1, + TIME_STAMP2, true, g_localDevice}; + + /** + * @tc.steps:step2. Add commit whose left parent and right parent is the same + * @tc.expected: step2. Return E_UNEXPECTED_DATA. + */ + InsertCommitToCommitStorage(commitInfo2, -E_UNEXPECTED_DATA); + + /** + * @tc.steps:step3. Cheeck commit History of commitInfo1 to commit log database + * @tc.expected: step3. Return E_OK. + */ + TestCommit(commitInfo1, E_OK); + + /** + * @tc.steps:step3. Cheeck commit History of commitInfo2 to commit log database + * @tc.expected: step3. Return E_NOT_FOUND. + */ + TestCommit(commitInfo2, -E_NOT_FOUND); +} + +/** + * @tc.name: MultiVerCommitStorage011 + * @tc.desc: Add commit with a non exist left parent, and it will not be added. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage011, TestSize.Level1) +{ + CommitInfo commitInfo = {g_defaultCommitVer1, g_defaultCommitID2, g_defaultCommitID1, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1. Add commit with a non exist left parent + * @tc.expected: step1. Return E_NOT_FOUND. + */ + InsertCommitToCommitStorage(commitInfo, -E_NOT_FOUND); + + /** + * @tc.steps:step2. Cheeck commit History to commit log database + * @tc.expected: step2. Return E_NOT_FOUND. + */ + TestCommit(commitInfo, -E_NOT_FOUND); +} + +/** + * @tc.name: MultiVerCommitStorage012 + * @tc.desc: Add commit with a non exist right parent, and it will not be added. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage012, TestSize.Level1) +{ + CommitInfo commitInfo = {g_defaultCommitVer1, g_defaultCommitID2, g_defaultCommitID0, g_defaultCommitID1, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1. Add commit with a non exist right parent + * @tc.expected: step1. Return E_NOT_FOUND. + */ + InsertCommitToCommitStorage(commitInfo, -E_NOT_FOUND); + + /** + * @tc.steps:step2. Cheeck commit History to commit log database + * @tc.expected: step2. Return E_NOT_FOUND. + */ + TestCommit(commitInfo, -E_NOT_FOUND); +} + +/** + * @tc.name: MultiVerCommitStorage013 + * @tc.desc: Delete a commit which is not header, and it will not be deleted. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage013, TestSize.Level1) +{ + CommitInfo commitInfo1 = {g_defaultCommitVer1, g_defaultCommitID1, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP1, true, g_localDevice}; + + /** + * @tc.steps:step1. Insert a recording to commit log database + */ + InsertCommitToCommitStorage(commitInfo1, E_OK); + CommitInfo commitInfo2 = {g_defaultCommitVer2, g_defaultCommitID2, g_defaultCommitID1, g_defaultCommitID0, + TIME_STAMP2, true, g_localDevice}; + + /** + * @tc.steps:step2. Insert a left parent to commit log database + */ + InsertCommitToCommitStorage(commitInfo2, E_OK); + + /** + * @tc.steps:step3. Set g_defaultCommitID2 is parent to commit log database + */ + SetCommitStorageHeader(g_defaultCommitID2, E_OK); + + /** + * @tc.steps:step4. Delete g_defaultCommitID1 + * @tc.expected: step4. Return E_UNEXPECTED_DATA. + */ + DeleteCommit(g_defaultCommitID1, -E_UNEXPECTED_DATA); + + /** + * @tc.steps:step5. Cheeck commit header is same as g_defaultCommitID2 + */ + TestCommitStorageHeader(g_defaultCommitID2); + + TestCommit(commitInfo1, E_OK); + TestCommit(commitInfo2, E_OK); + return; +} + +/** + * @tc.name: MultiVerCommitStorage014 + * @tc.desc: Set unexist commit to header, and it will not success. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage014, TestSize.Level1) +{ + SetCommitStorageHeader(g_defaultCommitID2, -E_NOT_FOUND); + TestCommitStorageHeader(g_defaultCommitID0); +} + +/** + * @tc.name: MultiVerCommitStorage015 + * @tc.desc: SDetermine whether commit exists + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage015, TestSize.Level1) +{ + PrepareCommitTree(); + int errCode = E_OK; + EXPECT_EQ(g_commitStorage->CommitExist(TransStrToCommitID(g_defaultCommitID7), errCode), true); + EXPECT_EQ(g_commitStorage->CommitExist(TransStrToCommitID(g_defaultCommitID13), errCode), false); +} + +/** + * @tc.name: MultiVerCommitStorage016 + * @tc.desc: Get latest commit of each device from commit storage + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage016, TestSize.Level1) +{ + PrepareCommitTree(); + std::map latestCommits; + int result = g_commitStorage->GetLatestCommits(latestCommits); + EXPECT_EQ(result, E_OK); + CommitInfo commitInfoLocal = {g_defaultCommitVer12, g_defaultCommitID12, g_defaultCommitID7, + g_defaultCommitID11, TIME_STAMP12, true, g_localDevice}; + CommitInfo commitInfoA = {g_defaultCommitVer11, g_defaultCommitID11, g_defaultCommitID6, + g_defaultCommitID10, TIME_STAMP11, false, g_remoteDeviceA}; + CommitInfo commitInfoB = {g_defaultCommitVer10, g_defaultCommitID10, g_defaultCommitID4, + g_defaultCommitID9, TIME_STAMP10, false, g_remoteDeviceB}; + CommitInfo commitInfoC = {g_defaultCommitVer9, g_defaultCommitID9, g_defaultCommitID8, + g_defaultCommitID0, TIME_STAMP9, false, g_remoteDeviceC}; + TestLatestCommitOfDevice(latestCommits, g_localDevice, commitInfoLocal); + TestLatestCommitOfDevice(latestCommits, g_remoteDeviceA, commitInfoA); + TestLatestCommitOfDevice(latestCommits, g_remoteDeviceB, commitInfoB); + TestLatestCommitOfDevice(latestCommits, g_remoteDeviceC, commitInfoC); + for (auto latestCommit : latestCommits) { + g_commitStorage->ReleaseCommit(latestCommit.second); + latestCommit.second = nullptr; + } +} + +/** + * @tc.name: MultiVerCommitStorage017 + * @tc.desc: Get commit tree from commit storage by latest commits + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageCommitStorageTest, MultiVerCommitStorage017, TestSize.Level1) +{ + PrepareCommitTree(); + map latestCommits; + latestCommits.insert(make_pair(g_localDevice, TransStrToCommitID(g_defaultCommitID3))); + latestCommits.insert(make_pair(g_remoteDeviceA, TransStrToCommitID(g_defaultCommitID2))); + latestCommits.insert(make_pair(g_remoteDeviceC, TransStrToCommitID(g_defaultCommitID14))); + latestCommits.insert(make_pair(g_remoteDeviceD, TransStrToCommitID(g_defaultCommitID13))); + list commits; + int result = g_commitStorage->GetCommitTree(latestCommits, commits); + EXPECT_EQ(result, E_OK); + vector commitsVector(commits.begin(), commits.end()); + LOGD("Commits.size%zu", commits.size()); + ASSERT_GT(commits.size(), 6UL); + CommitInfo commitInfo4 = {g_defaultCommitVer4, g_defaultCommitID4, g_defaultCommitID0, g_defaultCommitID0, + TIME_STAMP4, false, g_remoteDeviceB}; + CommitInfo commitInfo5 = {g_defaultCommitVer5, g_defaultCommitID5, g_defaultCommitID3, g_defaultCommitID4, + TIME_STAMP5, true, g_localDevice}; + CommitInfo commitInfo6 = {g_defaultCommitVer6, g_defaultCommitID6, g_defaultCommitID2, g_defaultCommitID3, + TIME_STAMP6, false, g_remoteDeviceA}; + CommitInfo commitInfo7 = {g_defaultCommitVer7, g_defaultCommitID7, g_defaultCommitID5, g_defaultCommitID6, + TIME_STAMP7, true, g_localDevice}; + CommitInfo commitInfo10 = {g_defaultCommitVer10, g_defaultCommitID10, g_defaultCommitID4, g_defaultCommitID9, + TIME_STAMP10, false, g_remoteDeviceB}; + CommitInfo commitInfo11 = {g_defaultCommitVer11, g_defaultCommitID11, g_defaultCommitID6, g_defaultCommitID10, + TIME_STAMP11, false, g_remoteDeviceA}; + CommitInfo commitInfo12 = {g_defaultCommitVer12, g_defaultCommitID12, g_defaultCommitID7, g_defaultCommitID11, + TIME_STAMP12, true, g_localDevice}; + CompareCommitWithExpectation(commitsVector[0], commitInfo4); + CompareCommitWithExpectation(commitsVector[1], commitInfo5); + CompareCommitWithExpectation(commitsVector[2], commitInfo6); + CompareCommitWithExpectation(commitsVector[3], commitInfo7); + CompareCommitWithExpectation(commitsVector[4], commitInfo10); + CompareCommitWithExpectation(commitsVector[5], commitInfo11); + CompareCommitWithExpectation(commitsVector[6], commitInfo12); + for (auto commit : commits) { + g_commitStorage->ReleaseCommit(commit); + commit = nullptr; + } +} diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_data_operation_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_data_operation_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..832cc32666fb8c57d3320d685dbc4d07e6615f0a --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_data_operation_test.cpp @@ -0,0 +1,642 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_common.h" +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "kvdb_manager.h" +#include "multi_ver_natural_store_transfer_data.h" +#include "sqlite_local_kvdb_connection.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + SQLiteLocalKvDBConnection *g_connection = nullptr; +} + +class DistributedDBStorageDataOperationTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageDataOperationTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); +} + +void DistributedDBStorageDataOperationTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBStorageDataOperationTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + KvDBProperties properties; + properties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + properties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE); + properties.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + properties.SetStringProp(KvDBProperties::STORE_ID, "test"); + properties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, "test"); + + int errCode = E_OK; + g_connection = static_cast(KvDBManager::GetDatabaseConnection(properties, errCode)); + EXPECT_EQ(errCode, E_OK); +} + +void DistributedDBStorageDataOperationTest::TearDown(void) +{ + if (g_connection != nullptr) { + g_connection->Close(); + g_connection = nullptr; + } + return; +} + +/** + * @tc.name: Insert001 + * @tc.desc: Insert a record into a distributed db + * @tc.type: FUNC + * @tc.require: AR000CQDV8 AR000CQDVB + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageDataOperationTest, Insert001, TestSize.Level1) +{ + EXPECT_NE(g_connection, nullptr); + if (g_connection == nullptr) { + return; + } + + Key key(3, 'w'); + Value value; + value.assign(8, 87); + IOption option; + + /** + * @tc.steps:step1. Put a kv into database + * @tc.expected: step1. Return OK. + */ + int errCode = g_connection->Put(option, key, value); + EXPECT_EQ(errCode, E_OK); + + Value valueRead; + valueRead.clear(); + + /** + * @tc.steps:step2. Get k from database + * @tc.expected: step2. Return OK. The size is right. + */ + errCode = g_connection->Get(option, key, valueRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(valueRead.size(), 8UL); + + for (auto iter = valueRead.begin(); iter != valueRead.end(); iter++) { + EXPECT_EQ(*iter, 87); + } +} + +/** + * @tc.name: InsertBatch001 + * @tc.desc: Insert some records into a distributed db + * @tc.type: FUNC + * @tc.require: AR000CQDV9 AR000CQDVE + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageDataOperationTest, InsertBatch001, TestSize.Level1) +{ + EXPECT_NE(g_connection, nullptr); + if (g_connection == nullptr) { + return; + } + + Key key(3, 'w'); + Value value; + value.assign(8, 87); + IOption option; + + Entry entry; + entry.key = key; + entry.value = value; + + std::vector entries; + entries.push_back(entry); + + entry.key.push_back('q'); + entry.value.assign(6, 76); + entries.push_back(entry); + + /** + * @tc.steps:step1. PutBatch series kv into database + * @tc.expected: step1. Return OK. + */ + int errCode = g_connection->PutBatch(option, entries); + EXPECT_EQ(errCode, E_OK); + + std::vector entriesRead; + Key keyRead(3, 'w'); + entriesRead.clear(); + + /** + * @tc.steps:step2. Get k from database by GetEntries + * @tc.expected: step2. Return OK. The size is right. + */ + errCode = g_connection->GetEntries(option, keyRead, entriesRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(entriesRead.size(), 2UL); + + if (entriesRead.size() > 2) { + EXPECT_EQ(entriesRead[0].value.size(), 8UL); + EXPECT_EQ(entriesRead[1].value.size(), 6UL); + } +} + +/** + * @tc.name: Clear001 + * @tc.desc: Clear some records from a distributed db + * @tc.type: FUNC + * @tc.require: AR000BVTO6 AR000CQDVA + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageDataOperationTest, Clear001, TestSize.Level1) +{ + EXPECT_NE(g_connection, nullptr); + if (g_connection == nullptr) { + return; + } + + Key key(3, 'w'); + Value value; + value.assign(8, 87); + IOption option; + + Entry entry; + entry.key = key; + entry.value = value; + + std::vector entries; + entries.push_back(entry); + + entry.key.push_back('q'); + entry.value.assign(6, 76); + entries.push_back(entry); + + /** + * @tc.steps:step1. PutBatch series kv into database + * @tc.expected: step1. Return OK. + */ + int errCode = g_connection->PutBatch(option, entries); + EXPECT_EQ(errCode, E_OK); + + std::vector entriesRead; + Key keyRead(3, 'w'); + entriesRead.clear(); + + /** + * @tc.steps:step2. Get k from database by GetEntries + * @tc.expected: step2. Return OK. The size is right. + */ + errCode = g_connection->GetEntries(option, keyRead, entriesRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(entriesRead.size(), 2UL); + + if (entriesRead.size() > 2) { + EXPECT_EQ(entriesRead[0].value.size(), 8UL); + EXPECT_EQ(entriesRead[1].value.size(), 6UL); + } + + /** + * @tc.steps:step3. Clear all data from database + * @tc.expected: step3. Return OK. + */ + errCode = g_connection->Clear(option); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps:step2. Get k from database by GetEntries + * @tc.expected: step2. Return E_NOT_FOUND. The result size is 0. + */ + entriesRead.clear(); + errCode = g_connection->GetEntries(option, keyRead, entriesRead); + EXPECT_EQ(errCode, -E_NOT_FOUND); + EXPECT_EQ(entriesRead.size(), 0UL); +} + +/** + * @tc.name: Delete001 + * @tc.desc: Delete a record from a distributed db + * @tc.type: FUNC + * @tc.require: AR000CQDVF AR000CQDVB + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageDataOperationTest, Delete001, TestSize.Level1) +{ + EXPECT_NE(g_connection, nullptr); + if (g_connection == nullptr) { + return; + } + + Key key(3, 'w'); + Value value; + value.assign(8, 87); + IOption option; + + Entry entry; + entry.key = key; + entry.value = value; + + std::vector entries; + entries.push_back(entry); + + entry.key.push_back('q'); + entry.value.assign(6, 76); + entries.push_back(entry); + + /** + * @tc.steps:step1. PutBatch series kv into database + * @tc.expected: step1. Return OK. + */ + int errCode = g_connection->PutBatch(option, entries); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps:step2. Get k from database by GetEntries + * @tc.expected: step2. Return OK. The size is right. + */ + Value valueRead; + errCode = g_connection->Get(option, entry.key, valueRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(valueRead.size(), 6UL); + + std::vector entriesRead; + Key keyRead(3, 'w'); + entriesRead.clear(); + + /** + * @tc.steps:step3. Get k from database by GetEntries + * @tc.expected: step3. Return E_OK. The result size is right. + */ + errCode = g_connection->GetEntries(option, keyRead, entriesRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(entriesRead.size(), 2UL); + + if (entriesRead.size() > 2) { + EXPECT_EQ(entriesRead[0].value.size(), 8UL); + EXPECT_EQ(entriesRead[1].value.size(), 6UL); + } + + /** + * @tc.steps:step3. Delete k from database + * @tc.expected: step3. Return E_OK. + */ + errCode = g_connection->Delete(option, key); + EXPECT_EQ(errCode, E_OK); + + entriesRead.clear(); + + /** + * @tc.steps:step3. Get k from database by GetEntries + * @tc.expected: step3. Return E_OK. The result size is reduction 1. + */ + errCode = g_connection->GetEntries(option, keyRead, entriesRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(entriesRead.size(), 1UL); +} + +/** + * @tc.name: DeleteBatch001 + * @tc.desc: Delete some records from a distributed db + * @tc.type: FUNC + * @tc.require: AR000CQDVG AR000CQDVB + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageDataOperationTest, DeleteBatch001, TestSize.Level1) +{ + EXPECT_NE(g_connection, nullptr); + if (g_connection == nullptr) { + return; + } + + Key key(3, 'w'); + Value value; + value.assign(8, 87); + IOption option; + + Entry entry; + entry.key = key; + entry.value = value; + + std::vector entries; + entries.push_back(entry); + + entry.key.push_back('q'); + entry.value.assign(6, 76); + entries.push_back(entry); + + /** + * @tc.steps:step1. PutBatch series kv into database + * @tc.expected: step1. Return OK. + */ + int errCode = g_connection->PutBatch(option, entries); + EXPECT_EQ(errCode, E_OK); + + std::vector entriesRead; + Key keyRead(3, 'w'); + entriesRead.clear(); + + /** + * @tc.steps:step2. Get k from database by GetEntries + * @tc.expected: step2. Return E_OK. The result size is right. + */ + errCode = g_connection->GetEntries(option, keyRead, entriesRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(entriesRead.size(), 2UL); + + if (entriesRead.size() > 2) { + EXPECT_EQ(entriesRead[0].value.size(), 8UL); + EXPECT_EQ(entriesRead[1].value.size(), 6UL); + } + + std::vector keys; + Key keyTmp = key; + + keys.push_back(keyTmp); + keyTmp.push_back('q'); + keys.push_back(keyTmp); + + /** + * @tc.steps:step3. DeleteBatch keys from database by DeleteBatch + * @tc.expected: step3. Return E_OK. + */ + errCode = g_connection->DeleteBatch(option, keys); + EXPECT_EQ(errCode, E_OK); + + entriesRead.clear(); + + /** + * @tc.steps:step3. Get k from database by GetEntries + * @tc.expected: step3. Return E_OK. The result size is 0. + */ + errCode = g_connection->GetEntries(option, keyRead, entriesRead); + EXPECT_EQ(errCode, -E_NOT_FOUND); + EXPECT_EQ(entriesRead.size(), 0UL); +} + +static void CheckSplitData(const Value &oriValue, const uint32_t numBlock, + std::map &valueDic, Value &savedValue) +{ + MultiVerValueObject valueObject; + MultiVerNaturalStoreTransferData transferData; + std::vector partValues; + int errCode = transferData.SegmentAndTransferValueToHash(oriValue, partValues); + // Default threshold + if (oriValue.size() <= DistributedDB::DBConstant::MAX_VALUE_SIZE) { + valueObject.SetFlag(0); + valueObject.SetValue(oriValue); + valueObject.GetSerialData(savedValue); + EXPECT_EQ(errCode, -E_UNEXPECTED_DATA); + EXPECT_EQ(partValues.size(), numBlock); + return; + } + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(partValues.size(), numBlock); + + valueObject.SetFlag(MultiVerValueObject::HASH_FLAG); + std::vector hashValues; + ValueSliceHash hashValue; + for (const auto &value : partValues) { + errCode = DBCommon::CalcValueHash(value, hashValue); + EXPECT_EQ(errCode, E_OK); + + // prepare for recover + valueDic[hashValue] = value; + hashValues.push_back(std::move(hashValue)); + } + + valueObject.SetValueHash(hashValues); + valueObject.GetSerialData(savedValue); + + return; +} + +static void CheckRecoverData(const Value &savedValue, std::map &valueDic, + Value &checkValue) +{ + Value value; + MultiVerValueObject valueObject; + EXPECT_EQ(valueObject.DeSerialData(savedValue), E_OK); + if (!valueObject.IsHash()) { + EXPECT_EQ(valueObject.GetValue(value), E_OK); + } + + std::vector sliceHashVect; + EXPECT_EQ(valueObject.GetValueHash(sliceHashVect), E_OK); + + value.reserve(valueObject.GetDataLength()); + for (const auto &item : sliceHashVect) { + Value itemValue = valueDic[item]; + value.insert(value.end(), itemValue.begin(), itemValue.end()); + } + + EXPECT_EQ(value, checkValue); + return; +} + +/** + * @tc.name: BlockDataIndex001 + * @tc.desc: Determine the block threshold of the database. + * @tc.type: FUNC + * @tc.require: AR000CQDTT SR000CQDTR + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageDataOperationTest, BlockDataIndex001, TestSize.Level1) +{ + /** + * @tc.steps:step1/2/3. Put 100B 1K 100k size of unique value into database + */ + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, 100); // 100B + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, 1024); // 1K + Value value3; + DistributedDBToolsUnitTest::GetRandomKeyValue(value3, 1024 * 100); // 100K + + IOption option; + option.dataType = IOption::SYNC_DATA; + int errCode = g_connection->Put(option, KEY_1, value1); + EXPECT_EQ(errCode, E_OK); + errCode = g_connection->Put(option, KEY_2, value2); + EXPECT_EQ(errCode, E_OK); + errCode = g_connection->Put(option, KEY_3, value3); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps:step4. Check split status + * @tc.expected: step4. Value1 not cut, value2 cut into 1 block, value3 cut into 2 blocks. + */ + std::map valueDic; + Value savedValue1; + CheckSplitData(value1, 0ul, valueDic, savedValue1); + Value savedValue2; + CheckSplitData(value2, 0ul, valueDic, savedValue2); + Value savedValue3; + CheckSplitData(value3, 0ul, valueDic, savedValue3); + + /** + * @tc.steps:step5. Get the original before key + * @tc.expected: step5. Return the right original value. + */ + Value valueRead; + valueRead.clear(); + errCode = g_connection->Get(option, KEY_1, valueRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(value1, valueRead); + valueRead.clear(); + errCode = g_connection->Get(option, KEY_2, valueRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(value2, valueRead); + valueRead.clear(); + errCode = g_connection->Get(option, KEY_3, valueRead); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(value3, valueRead); +} + +/** + * @tc.name: CutValueIntoBlock001 + * @tc.desc: Database block size test + * @tc.type: FUNC + * @tc.require: AR000CQDTS AR000CQDTU + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageDataOperationTest, CutValueIntoBlock001, TestSize.Level1) +{ + /** + * @tc.steps:step1/2/3/4. Put 100B 1K 100k 64k size of unique value into database + */ + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, 100); // 100B + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, 1024); // 1K + Value value3; + DistributedDBToolsUnitTest::GetRandomKeyValue(value3, 1024 * 100); // 100k + Value value4; + DistributedDBToolsUnitTest::GetRandomKeyValue(value4, 1024 * 64); // 64K + + /** + * @tc.steps:step4. Split and check repeat value block. + * @tc.expected: step4. No repeat block. + */ + IOption option; + option.dataType = IOption::SYNC_DATA; + int errCode = g_connection->Put(option, KEY_1, value1); + EXPECT_EQ(errCode, E_OK); + errCode = g_connection->Put(option, KEY_2, value2); + EXPECT_EQ(errCode, E_OK); + errCode = g_connection->Put(option, KEY_3, value3); + EXPECT_EQ(errCode, E_OK); + errCode = g_connection->Put(option, KEY_4, value4); + EXPECT_EQ(errCode, E_OK); + + std::map valueDic; + Value savedValue; + CheckSplitData(value1, 0ul, valueDic, savedValue); + CheckRecoverData(savedValue, valueDic, value1); + + valueDic.clear(); + savedValue.clear(); + CheckSplitData(value2, 0ul, valueDic, savedValue); + CheckRecoverData(savedValue, valueDic, value2); + + valueDic.clear(); + savedValue.clear(); + CheckSplitData(value3, 0ul, valueDic, savedValue); + CheckRecoverData(savedValue, valueDic, value3); + EXPECT_EQ(valueDic.size(), 0ul); + + valueDic.clear(); + savedValue.clear(); + CheckSplitData(value4, 0ul, valueDic, savedValue); + CheckRecoverData(savedValue, valueDic, value4); +} + +/** + * @tc.name: CutValueIntoBlock002 + * @tc.desc: Block data index + * @tc.type: FUNC + * @tc.require: AR000CQDTT AR000CQDTV + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageDataOperationTest, CutValueIntoBlock002, TestSize.Level1) +{ + /** + * @tc.steps:step1/2/3. Put 64k 100k 200k size of value into database(some blocks are repeated). + */ + Value valueTemp; + DistributedDBToolsUnitTest::GetRandomKeyValue(valueTemp, 1024 * 36); // 36K add 64K equal 100K + + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, 1024 * 64); // 64K + Value value2; + value2.insert(value2.end(), value1.begin(), value1.end()); + value2.insert(value2.end(), valueTemp.begin(), valueTemp.end()); + + Value value3; + // repeat twice value1 in front of value3 + for (int i = 0; i < 2; i++) { + value3.insert(value3.end(), value1.begin(), value1.end()); + } + value3.insert(value3.end(), valueTemp.begin(), valueTemp.end()); + value3.insert(value3.end(), valueTemp.begin(), valueTemp.end()); + + IOption option; + option.dataType = IOption::SYNC_DATA; + int errCode = g_connection->Put(option, KEY_1, value1); + EXPECT_EQ(errCode, E_OK); + errCode = g_connection->Put(option, KEY_2, value2); + EXPECT_EQ(errCode, E_OK); + errCode = g_connection->Put(option, KEY_3, value3); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps:step4. Split and check repeat value block. + * @tc.expected: step4. Duplicate blocks are eliminated + */ + std::map valueDic; + Value savedValue; + CheckSplitData(value3, 0ul, valueDic, savedValue); + CheckRecoverData(savedValue, valueDic, value3); + EXPECT_EQ(valueDic.size(), 0ul); + + savedValue.clear(); + CheckSplitData(value1, 0ul, valueDic, savedValue); + CheckRecoverData(savedValue, valueDic, value1); + EXPECT_EQ(valueDic.size(), 0ul); + + savedValue.clear(); + CheckSplitData(value2, 0ul, valueDic, savedValue); + CheckRecoverData(savedValue, valueDic, value2); + EXPECT_EQ(valueDic.size(), 0ul); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_encrypt_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_encrypt_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca927e8b6c1794598bf321108c1205051e577fdc --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_encrypt_test.cpp @@ -0,0 +1,1397 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include "db_types.h" +#include "log_print.h" +#include "securec.h" +#include "sqlite_import.h" + +#ifndef OMIT_ENCRYPT +using namespace testing::ext; +using namespace DistributedDB; +using namespace std; +using namespace std::placeholders; + +namespace { + sqlite3 *g_db = nullptr; + const string STORE_ID = "test"; + const string STORE_ID2 = "test2"; + const string STORE_ID3 = "test3"; + const int PASSWD_TEST_SIZE = 16; + char g_oldPasswd[PASSWD_TEST_SIZE + 1] = {0}; + char g_newPasswd[PASSWD_TEST_SIZE + 1] = {0}; + char g_diffPasswd[PASSWD_TEST_SIZE + 1] = {0}; + const string ALG1 = "'aes-256-gcm'"; + const string ALG2 = "'aes-256-cbc'"; + const string ALG3 = "'ABCDEG'"; + const int ITERATION = 64000; + const int ITERATION2 = 1000; + const std::string CREATE_SQL = "CREATE TABLE IF NOT EXISTS data(key TEXT PRIMARY KEY, value TEXT);"; + const int SLEEP_TIME = 1; + + const std::vector KEY_1 = {'A'}; + const std::vector VALUE_1 = {'1'}; + const std::vector VALUE_2 = {'2'}; +#ifndef USE_SQLITE_CODEC_CIPHER + const std::string PRAGMA_CIPHER = "PRAGMA cipher="; + const std::string PRAGMA_KDF_ITER = "PRAGMA kdf_iter="; + const std::string EXPORT_STRING = "sqlcipher_export"; +#else + const std::string PRAGMA_CIPHER = "PRAGMA codec_cipher="; + const std::string PRAGMA_KDF_ITER = "PRAGMA codec_kdf_iter="; + const std::string EXPORT_STRING = "export_database"; +#endif + + int Callback(void *data, int argc, char **argv, char **azColName) + { + vector *value = static_cast *>(data); + value->push_back(*argv[0]); + return 0; + } + + int Open(sqlite3 *&db, const string &storeID) + { + std::string uri = "file:" + storeID + ".db"; + sqlite3 *dbTemp = nullptr; + int errCode = sqlite3_open_v2(uri.c_str(), &dbTemp, + SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); + if (errCode != SQLITE_OK) { + if (dbTemp != nullptr) { + (void)sqlite3_close_v2(dbTemp); + dbTemp = nullptr; + } + return errCode; + } + db = dbTemp; + + return errCode; + } + + int CreateTable() + { + char *zErrMsg = nullptr; + int errCode = sqlite3_exec(g_db, CREATE_SQL.c_str(), nullptr, nullptr, &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + + return errCode; + } + + int SetEncryptParam(const char *passwd, int iterNumber, const string &algName) + { + char *zErrMsg = nullptr; + int errCode = sqlite3_key(g_db, static_cast(passwd), strlen(passwd)); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + + errCode = sqlite3_exec(g_db, (PRAGMA_KDF_ITER + to_string(iterNumber)).c_str(), nullptr, nullptr, + &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + + errCode = sqlite3_exec(g_db, (PRAGMA_CIPHER + algName + ";").c_str(), nullptr, nullptr, + &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + + return errCode; + } + + int OpenWithKey(const char *passwd, int iterNumber, const string &algName, bool isEncrypted) + { + int errCode = Open(g_db, STORE_ID); + if (errCode != SQLITE_OK) { + return errCode; + } + if (isEncrypted) { + errCode = SetEncryptParam(passwd, iterNumber, algName); + if (errCode != SQLITE_OK) { + return errCode; + } + } + + errCode = CreateTable(); + if (errCode != SQLITE_OK) { + return errCode; + } + errCode = sqlite3_close(g_db); + if (errCode != SQLITE_OK) { + return errCode; + } + + errCode = Open(g_db, STORE_ID); + if (errCode != SQLITE_OK) { + return errCode; + } + + return errCode; + } + + int InputPasswd(const char *passwd, int iterNumber, const string &algName) + { + int errCode = SetEncryptParam(passwd, iterNumber, algName); + if (errCode != SQLITE_OK) { + return errCode; + } + + return errCode; + } + + int Reconnect(const char *passwd, int iterNumber, const string &algName) + { + int errCode = Open(g_db, STORE_ID); + if (errCode != SQLITE_OK) { + return errCode; + } + errCode = InputPasswd(passwd, iterNumber, algName); + if (errCode != SQLITE_OK) { + return errCode; + } + errCode = InputPasswd(passwd, iterNumber, algName); + if (errCode != SQLITE_OK) { + return errCode; + } + + return errCode; + } + + int PutValue(const Key &key, const Value &value) + { + char *zErrMsg = nullptr; + string keyStr(key.begin(), key.end()); + string valueStr(value.begin(), value.end()); + int errCode = sqlite3_exec(g_db, ("INSERT OR REPLACE INTO data VALUES('" + keyStr + "','" + valueStr + + "');").c_str(), nullptr, nullptr, &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + return errCode; + } + + int DeleteValue(const Key &key) + { + char *zErrMsg = nullptr; + string keyStr(key.begin(), key.end()); + int errCode = sqlite3_exec(g_db, ("DELETE FROM data WHERE key='" + keyStr + "';").c_str(), nullptr, + nullptr, &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + return errCode; + } + + int UpdateValue(const Key &key, const Value &value) + { + char *zErrMsg = nullptr; + string keyStr(key.begin(), key.end()); + string valueStr(value.begin(), value.end()); + int errCode = sqlite3_exec(g_db, ("INSERT OR REPLACE INTO data VALUES('" + keyStr + "','" + valueStr + + "');").c_str(), nullptr, nullptr, &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + return errCode; + } + + int GetValue(const Key &key, Value &value) + { + char *zErrMsg = nullptr; + string keyStr(key.begin(), key.end()); + int errCode = sqlite3_exec(g_db, ("SELECT value from data WHERE key='" + keyStr + "';").c_str(), + Callback, static_cast(&value), &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + return errCode; + } + + int Export(const string &dbName) + { + char *zErrMsg = nullptr; + int errCode = sqlite3_exec(g_db, ("SELECT " + EXPORT_STRING + "('" + dbName + "');").c_str(), nullptr, nullptr, + &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + return errCode; + } + + int Attach(const string &dbName) + { + char *zErrMsg = nullptr; + int errCode = sqlite3_exec(g_db, ("attach '" + dbName + ".db' as " + dbName + " key '';").c_str(), + nullptr, nullptr, &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + return errCode; + } + + int AttachWithKey(const string &dbName, const char *passwd) + { + char *zErrMsg = nullptr; + int errCode = sqlite3_exec(g_db, ("attach '" + dbName + ".db' as " + dbName + " key '" + passwd + "';").c_str(), + nullptr, nullptr, &zErrMsg); + if (errCode != SQLITE_OK && zErrMsg != nullptr) { + LOGE(" [SQLITE]: %s", zErrMsg); + sqlite3_free(zErrMsg); + return errCode; + } + return errCode; + } + + int MultipleOperation(Value &valueGet, const Value &valueUpdate) + { + int errCode = PutValue(KEY_1, VALUE_1); + if (errCode != SQLITE_OK) { + return errCode; + } + errCode = UpdateValue(KEY_1, valueUpdate); + if (errCode != SQLITE_OK) { + return errCode; + } + GetValue(KEY_1, valueGet); + errCode = DeleteValue(KEY_1); + if (errCode != SQLITE_OK) { + return errCode; + } + return errCode; + } +} + +class DistributedDBStorageEncryptTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageEncryptTest::SetUpTestCase(void) +{ + unsigned char initialByte = 0; + RAND_bytes(&initialByte, 1); + for (int i = 0; i < PASSWD_TEST_SIZE; i++) { + initialByte %= 20; // keep the number < 20, so 'A' + 20 is the maximum alphabet. + g_oldPasswd[i] = ('A' + initialByte++); + g_newPasswd[i] = ('A' + initialByte++); + g_diffPasswd[i] = ('A' + initialByte++); + } +} + +void DistributedDBStorageEncryptTest::TearDownTestCase(void) +{ +} + +void DistributedDBStorageEncryptTest::SetUp(void) +{ + testing::UnitTest *test = testing::UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const testing::TestInfo *testInfo = test->current_test_info(); + ASSERT_NE(testInfo, nullptr); + LOGI("Start unit test: %s.%s", testInfo->test_case_name(), testInfo->name()); + /** + * @tc.Clean DB files created from every test case. + */ + if (remove((STORE_ID + ".db").c_str()) != 0) { + LOGE("remove db failed, errno:%d", errno); + } + if (remove((STORE_ID2 + ".db").c_str()) != 0) { + LOGE("remove db failed, errno:%d", errno); + } +} + +void DistributedDBStorageEncryptTest::TearDown(void) +{ + /** + * @tc.make sure g_db is nullptr and is closed. + */ + if (g_db != nullptr) { + g_db = nullptr; + } + /** + * @tc.Clean DB files created from every test case. + */ + if (remove((STORE_ID + ".db").c_str()) != 0) { + LOGE("remove db failed, errno:%d", errno); + } + if (remove((STORE_ID2 + ".db").c_str()) != 0) { + LOGE("remove db failed, errno:%d", errno); + } + /** + * @tc.Wait a number of SLEEP_TIME until remove done. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME)); +} + +/** + * @tc.name: EncryptTest001 + * @tc.desc: Check if opening database possible without encryption + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database without being encrypted. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, false), SQLITE_OK); + + /** + * @tc.steps:step2/5. Add, Update, Get and Delete the data. + * @tc.expected: step2/5. Return SQLITE_OK. + */ + Value valueGet; + Value valueUpdate = VALUE_2; + EXPECT_EQ(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_EQ(valueGet, valueUpdate); + + /** + * @tc.steps:step6. Close DB. + * @tc.expected: step6. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest002 + * @tc.desc: Check if it is possible to open nonencrypted database with password. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a nonencrypted DB. + * @tc.expected: step1. Return 0. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, false), SQLITE_OK); + + /** + * @tc.steps:step2. Set the key to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3/6. Add, Update, Get and Delete the data. + * @tc.expected: step3/6. Return NOT_EQUAL_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_NE(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_NE(valueGet, valueUpdate); + + /** + * @tc.steps:step7. Close DB. + * @tc.expected: step7. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest003 + * @tc.desc: Check if deciphering an encrypted database possible with wrong password. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open an encrypted DB with password g_oldPasswd. + * @tc.expected: step1. Return 0. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_diffPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_diffPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3/6. Add, Update, Get and Delete the data. + * @tc.expected: step3/6. Return NOT_EQUAL_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_NE(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_NE(valueGet, valueUpdate); + + /** + * @tc.steps:step7. Close DB. + * @tc.expected: step7. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest004 + * @tc.desc: Check if deciphering an encrypted database possible with correct password. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open an encrypted DB with password g_oldPasswd. + * @tc.expected: step1. Return 0. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3/6. Add, Update, Get and Delete the data. + * @tc.expected: step3/6. Return SQLITE_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_EQ(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_EQ(valueGet, valueUpdate); + + /** + * @tc.steps:step7. Close DB. + * @tc.expected: step7. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} +#ifdef USE_SQLITE_CODEC_CIPHER +/** + * @tc.name: EncryptTest005 + * @tc.desc: Check if rekeying possible with wrong password. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest005, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open an encrypted DB with password g_oldPasswd. + * @tc.expected: step1. Return 0. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_diffPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_diffPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. Reset the key by invoking the sqlite3_rekey() with the password as g_newPasswd. + * @tc.expected: step3. Return SQLITE_ERROR values. + */ + EXPECT_EQ(sqlite3_rekey(g_db, static_cast(g_newPasswd), strlen(g_newPasswd)), SQLITE_ERROR); + + /** + * @tc.steps:step4. Close DB. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} +#endif +/** + * @tc.name: EncryptTest006 + * @tc.desc: Check if rekeying possible with correct password. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest006, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open an encrypted DB with password g_oldPasswd. + * @tc.expected: step1. Return 0. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. Reset the key by invoking the sqlite3_rekey() with the password as g_newPasswd. + * @tc.expected: step3. Return SQLITE_OK values. + */ + EXPECT_EQ(sqlite3_rekey(g_db, static_cast(g_newPasswd), strlen(g_newPasswd)), SQLITE_OK); + + /** + * @tc.steps:step4. Close DB. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest007 + * @tc.desc: Check if manipulating data possible after rekeying before disconnecting with DB. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest007, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open an encrypted DB with password g_oldPasswd. + * @tc.expected: step1. Return 0. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. Reset the key by invoking the sqlite3_rekey() with the password as g_newPasswd. + * @tc.expected: step3. Return SQLITE_OK values. + */ + EXPECT_EQ(sqlite3_rekey(g_db, static_cast(g_newPasswd), strlen(g_newPasswd)), SQLITE_OK); + + /** + * @tc.steps:step4/7. Add, Update, Get and Delete the data. + * @tc.expected: step4/7. Return SQLITE_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_EQ(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_EQ(valueGet, valueUpdate); + + /** + * @tc.steps:step8. Close DB. + * @tc.expected: step8. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest008 + * @tc.desc: Check if manipulating data possible after rekeying and reconnection with a wrong password. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest008, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open an encrypted DB with password g_oldPasswd. + * @tc.expected: step1. Return 0. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. Reset the key by invoking the sqlite3_rekey() with the password as g_newPasswd. + * @tc.expected: step3. Return SQLITE_OK values. + */ + EXPECT_EQ(sqlite3_rekey(g_db, static_cast(g_newPasswd), strlen(g_newPasswd)), SQLITE_OK); + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); + + /** + * @tc.steps:step4. Open DB with the original password 'g_oldPasswd'. + * @tc.expected: step4. Return SQLITE_OK values. + */ + EXPECT_EQ(Reconnect(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step5/8. Add, Update, Get and Delete the data. + * @tc.expected: step5/8. Return NOT_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_NE(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_NE(valueGet, valueUpdate); + + /** + * @tc.steps:step9. Close DB. + * @tc.expected: step9. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest009 + * @tc.desc: Check if manipulating data possible after rekeying and reconnection with a correct password. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng +*/ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest009, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open an encrypted DB with password g_oldPasswd. + * @tc.expected: step1. Return 0. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. Reset the key by invoking the sqlite3_rekey() with the password as g_newPasswd. + * @tc.expected: step3. Return SQLITE_OK values. + */ + EXPECT_EQ(sqlite3_rekey(g_db, static_cast(g_newPasswd), strlen(g_newPasswd)), SQLITE_OK); + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); + + /** + * @tc.steps:step4. Open DB with the new password 'g_newPasswd'. + * @tc.expected: step4. Return SQLITE_OK values. + */ + EXPECT_EQ(Reconnect(g_newPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step5/8. Add, Update, Get and Delete the data. + * @tc.expected: step5/8. Return SQLITE_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_EQ(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_EQ(valueGet, valueUpdate); + + /** + * @tc.steps:step9. Close DB. + * @tc.expected: step9. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest010 + * @tc.desc: Export DB when there is no encryption. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng +*/ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest010, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database without being encrypted. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, false), SQLITE_OK); + + /** + * @tc.steps:step2. Attach DB. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step3. export DB. + * @tc.expected: step3. Return SQLITE_OK. + */ + EXPECT_EQ(Export(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step4. Close DB. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest011 + * @tc.desc: Export DB when there is no encryption but decipherment is attempted. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest011, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database without being encrypted. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, false), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. Attach DB. + * @tc.expected: step3. Return is not SQLITE_OK. + */ + EXPECT_NE(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step4. export DB. + * @tc.expected: step4. Return NOT_OK. + */ + EXPECT_NE(Export(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step5. Close DB. + * @tc.expected: step5. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest012 + * @tc.desc: Export DB when there is encryption but password is wrong. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest012, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_diffPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_diffPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. Attach DB. + * @tc.expected: step3. Return NOT_SQLITE_OK. + */ + EXPECT_NE(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step4. export DB. + * @tc.expected: step4. Return NOT_OK. + */ + EXPECT_NE(Export(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step5. Close DB. + * @tc.expected: step5. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest013 + * @tc.desc: Export DB when there is encryption and password matches. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest013, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_diffPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. Attach DB. + * @tc.expected: step3. Return SQLITE_OK. + */ + EXPECT_EQ(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step4. export DB. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(Export(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step5. Close DB. + * @tc.expected: step5. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest014 + * @tc.desc: Attach DB files when there is no encryption. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest014, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database without being encrypted. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, false), SQLITE_OK); + + /** + * @tc.steps:step2. attach DB file STORE_ID2. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step3. Close DB. + * @tc.expected: step3. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest015 + * @tc.desc: Attach DB files when there is no encryption but decipherment is attempted. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest015, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database without being encrypted. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, false), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. attach DB file STORE_ID2. + * @tc.expected: step3. Return is not SQLITE_OK. + */ + EXPECT_NE(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step4. Close DB. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest016 + * @tc.desc: Attach DB files when there is encryption but password dismatches. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest016, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_diffPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_diffPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. attach DB file STORE_ID2. + * @tc.expected: step3. Return NOT_SQLITE_OK. + */ + EXPECT_NE(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step4. Close DB. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest017 + * @tc.desc: Attach DB files when there is encryption and password matches. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest017, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. attach DB file STORE_ID2. + * @tc.expected: step3. Return SQLITE_OK. + */ + EXPECT_EQ(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step4. Close DB. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest018 + * @tc.desc: Export attached DB file failed if the file does not exist. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest018, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. attach DB file STORE_ID2. + * @tc.expected: step3. Return SQLITE_OK. + */ + EXPECT_EQ(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step3. export DB. + * @tc.expected: step3. Return NOT_OK. + */ + EXPECT_NE(Export(STORE_ID3), SQLITE_OK); + + /** + * @tc.steps:step4. Close DB. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest019 + * @tc.desc: Export attached DB file succeeded if the file exists. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng +*/ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest019, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. attach DB file STORE_ID2. + * @tc.expected: step3. Return SQLITE_OK. + */ + EXPECT_EQ(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step4. export DB. + * @tc.expected: step4. Return NOT_OK. + */ + EXPECT_EQ(Export(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step5. Close DB. + * @tc.expected: step5. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest020 + * @tc.desc: Failed to manipulate the data if the parameter of number of iteration dismatches. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest020, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd and choose not to save password. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION2, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3/6. Add, Update, Get and Delete the data. + * @tc.expected: step3/6. Return SQLITE_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_NE(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_NE(valueGet, valueUpdate); + + /** + * @tc.steps:step7. Close DB. + * @tc.expected: step7. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest021 + * @tc.desc: Succeeded to manipulate the data if the parameter of number of iteration matches. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest021, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd and choose not to save password. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3/6. Add, Update, Get and Delete the data. + * @tc.expected: step3/6. Return SQLITE_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_EQ(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_EQ(valueGet, valueUpdate); + + /** + * @tc.steps:step7. Close DB. + * @tc.expected: step7. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest022 + * @tc.desc: Failed to manipulate the data if the parameter of encryption algorithm dismatches. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest022, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd and choose not to save password. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG2), SQLITE_OK); + + /** + * @tc.steps:step3/6. Add, Update, Get and Delete the data. + * @tc.expected: step3/6. Return SQLITE_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_NE(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_NE(valueGet, valueUpdate); + + /** + * @tc.steps:step7. Close DB. + * @tc.expected: step7. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest023 + * @tc.desc: Succeeded to manipulate the data if the parameter of encryption algorithm matches. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest023, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd and choose not to save password. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3/6. Add, Update, Get and Delete the data. + * @tc.expected: step3/6. Return SQLITE_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_EQ(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_EQ(valueGet, valueUpdate); + + /** + * @tc.steps:step7. Close DB. + * @tc.expected: step7. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest024 + * @tc.desc: Export attached DB (no password) file and check the context. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest024, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database with password g_oldPasswd. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG1), SQLITE_OK); + + /** + * @tc.steps:step3. Put key into DB + * @tc.expected: step3. Return SQLITE_OK. + */ + EXPECT_EQ(PutValue(KEY_1, VALUE_1), SQLITE_OK); + + /** + * @tc.steps:step4. attach DB file STORE_ID2. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(Attach(STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step5. Export DB. + * @tc.expected: step5. Return SQLITE_OK. + */ + EXPECT_EQ(Export(STORE_ID2), SQLITE_OK); + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); + + /** + * @tc.steps:step6. Open exported DB. + * @tc.expected: step6. Return SQLITE_OK. + */ + EXPECT_EQ(Open(g_db, STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step7. Get Value from exported DB and the value shall be the same as the original one. + * @tc.expected: step7. Return SQLITE_OK. + */ + Value valueGet; + GetValue(KEY_1, valueGet); + EXPECT_EQ(valueGet, VALUE_1); + + /** + * @tc.steps:step8. Close DB. + * @tc.expected: step8. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest025 + * @tc.desc: Export attached DB (password) file and check the context. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest025, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open a database without password. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(Open(g_db, STORE_ID), SQLITE_OK); + + sqlite3_exec(g_db, ("PRAGMA cipher_default_attach_kdf_iter=5000;"), nullptr, nullptr, nullptr); + sqlite3_exec(g_db, ("PRAGMA cipher_default_attach_cipher='aes-256-gcm';"), nullptr, nullptr, nullptr); + + EXPECT_EQ(CreateTable(), SQLITE_OK); + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); + EXPECT_EQ(Open(g_db, STORE_ID), SQLITE_OK); + EXPECT_EQ(PutValue(KEY_1, VALUE_1), SQLITE_OK); + + /** + * @tc.steps:step2. attach DB file STORE_ID2 with password. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(AttachWithKey(STORE_ID2, g_oldPasswd), SQLITE_OK); + + /** + * @tc.steps:step3. export DB. + * @tc.expected: step3. Return SQLITE_OK. + */ + EXPECT_EQ(Export(STORE_ID2), SQLITE_OK); + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); + + /** + * @tc.steps:step4. Open exported DB. + * @tc.expected: step4. Return SQLITE_OK. + */ + EXPECT_EQ(Open(g_db, STORE_ID2), SQLITE_OK); + + /** + * @tc.steps:step5. Input password to g_oldPasswd. + * @tc.expected: step5. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_key(g_db, static_cast(g_oldPasswd), strlen(g_oldPasswd)), SQLITE_OK); + + EXPECT_EQ(sqlite3_exec(g_db, (PRAGMA_CIPHER + "'aes-256-gcm';").c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + EXPECT_EQ(sqlite3_exec(g_db, (PRAGMA_KDF_ITER + "5000;").c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + + /** + * @tc.steps:step6. Get Value from exported DB and the value shall be the same as the original one. + * @tc.expected: step6. Return SQLITE_OK. + */ + Value valueGet; + GetValue(KEY_1, valueGet); + EXPECT_EQ(valueGet, VALUE_1); + + /** + * @tc.steps:step6. Close DB. + * @tc.expected: step6. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} + +/** + * @tc.name: EncryptTest026 + * @tc.desc: Check if deciphering with a non-existing algorithm can be detected. + * @tc.type: FUNC + * @tc.require: AR000CQDT6 + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageEncryptTest, EncryptTest026, TestSize.Level1) +{ + /** + * @tc.steps:step1. Open an encrypted DB with password g_oldPasswd. + * @tc.expected: step1. Return 0. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG3, true), SQLITE_OK); + + /** + * @tc.steps:step2. Set password to g_oldPasswd. + * @tc.expected: step2. Return SQLITE_OK. + */ + EXPECT_EQ(InputPasswd(g_oldPasswd, ITERATION, ALG3), SQLITE_OK); + + /** + * @tc.steps:step3/6. Add, Update, Get and Delete the data. + * @tc.expected: step3/6. Return SQLITE_OK values. + */ + Value valueGet; + Value valueUpdate = { VALUE_2 }; + EXPECT_EQ(MultipleOperation(valueGet, valueUpdate), SQLITE_OK); + EXPECT_EQ(valueGet, valueUpdate); + + /** + * @tc.steps:step7. Close DB. + * @tc.expected: step7. Return SQLITE_OK. + */ + EXPECT_EQ(sqlite3_close(g_db), SQLITE_OK); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_index_optimize_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_index_optimize_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..77805452792af6cd27bedefee9bfc16292a5b903 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_index_optimize_test.cpp @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMIT_JSON +#include + +#include "db_common.h" +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "platform_specific.h" +#include "sqlite_import.h" +#include "store_types.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + std::string g_testDir; + std::string g_identifier; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + DBStatus g_kvNbDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + std::placeholders::_1, std::placeholders::_2, std::ref(g_kvNbDelegateStatus), std::ref(g_kvNbDelegatePtr)); + + const std::string BASE_SCHEMA_STRING = "{\"SCHEMA_VERSION\" : \"1.0\"," + "\"SCHEMA_MODE\" : \"COMPATIBLE\"," + "\"SCHEMA_DEFINE\" : {" + "\"name\" : \"STRING\"," + "\"id\" : \"INTEGER\"," + "\"father\" : {" + "\"name\" : \"STRING\"," + "\"id\" : \"INTEGER\"" + "}," + "\"phone\" : \"INTEGER\"" + "}," + "\"SCHEMA_INDEXES\" : "; + + const std::string JSON_VALUE ="{\"name\":\"Tom\"," + "\"id\":10," + "\"father\":{\"name\":\"Jim\", \"id\":20}," + "\"phone\":20}"; + + void GenerateSchemaString(std::string &schema, const std::string &indexString) + { + schema = BASE_SCHEMA_STRING + indexString + "}"; + } + + std::string GetKvStoreDirectory(const std::string &userId, const std::string &appId, const std::string &storeId) + { + string identifier = DBCommon::GenerateIdentifierId(storeId, appId, userId); + string hashIdentifierName = DBCommon::TransferHashString(identifier); + string identifierName = DBCommon::TransferStringToHex(hashIdentifierName); + string filePath = g_testDir + "/" + identifierName + "/" + DBConstant::SINGLE_SUB_DIR + "/main/"; + filePath += DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + return filePath; + } + + bool CheckIndexFromDbFile(const::std::string &filePath, const std::string &indexName) + { + sqlite3 *db = nullptr; + if (sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE, nullptr) != SQLITE_OK) { + LOGD("DB open failed %s", filePath.c_str()); + if (db != nullptr) { + (void)sqlite3_close_v2(db); + } + return false; + } + + std::string querySQL = "select sql from sqlite_master where name = '" + indexName + "'"; + int errCode = sqlite3_exec(db, querySQL.c_str(), nullptr, nullptr, nullptr); + (void)sqlite3_close_v2(db); + if (errCode == SQLITE_OK) { + return true; + } + return false; + } +} + +class DistributedDBStorageIndexOptimizeTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageIndexOptimizeTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + std::string origIdentifier = USER_ID + "-" + APP_ID + "-" + STORE_ID_1; + std::string identifier = DBCommon::TransferHashString(origIdentifier); + g_identifier = DBCommon::TransferStringToHex(identifier); + std::string dir = g_testDir + g_identifier + "/" + DBConstant::SINGLE_SUB_DIR; + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + + KvStoreConfig config; + config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(config); +} + +void DistributedDBStorageIndexOptimizeTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBStorageIndexOptimizeTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBStorageIndexOptimizeTest::TearDown(void) +{ +} + +/** + * @tc.name: ParseAndCheckUnionIndex001 + * @tc.desc: Test the Json union index parse and check function Open function + * @tc.type: FUNC + * @tc.require: AR000F3OPD + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBStorageIndexOptimizeTest, ParseAndCheckUnionIndex001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a correct shema string include a correct union index. + */ + std::string schema1; + GenerateSchemaString(schema1, "[[\"name\", \"father.name\", \"father.id\", \"id\", \"phone\"]]"); + + /** + * @tc.steps: step2. Call SchemaObject.ParseFromSchemaString to parse the string. + * @tc.expected: step2. Expect return E_OK. + */ + SchemaObject so1; + EXPECT_EQ(so1.ParseFromSchemaString(schema1), E_OK); + + /** + * @tc.steps: step3. Create a correct shema string include a single index and a union index + */ + std::string schema2; + GenerateSchemaString(schema2, "[[\"name\", \"father.name\", \"father.id\", \"id\", \"phone\"], \"id\"]"); + + /** + * @tc.steps: step4. Call SchemaObject.ParseFromSchemaString to parse the string. + * @tc.expected: step4. Expect return E_OK. + */ + SchemaObject so2; + EXPECT_EQ(so2.ParseFromSchemaString(schema2), E_OK); + + /** + * @tc.steps: step5. Create a shema string include a single index and a union index, and the two index has + the same sort column. + */ + std::string schema3; + GenerateSchemaString(schema3, "[[\"name\", \"father.name\", \"father.id\", \"id\", \"phone\"], \"name\"]"); + + /** + * @tc.steps: step6. Call SchemaObject.ParseFromSchemaString to parse the string. + * @tc.expected: step6. Expect return E_SCHEMA_PARSE_FAIL. + */ + SchemaObject so3; + EXPECT_EQ(so3.ParseFromSchemaString(schema3), -E_SCHEMA_PARSE_FAIL); + + /** + * @tc.steps: step7. Create a shema string include a single index with a not exist column + */ + std::string schema4; + GenerateSchemaString(schema4, "[[\"name\", \"father.name\", \"father.id\", \"id\", \"tel\"]]"); + + /** + * @tc.steps: step8. Call SchemaObject.ParseFromSchemaString to parse the string. + * @tc.expected: step8. Expect return -E_SCHEMA_PARSE_FAIL. + */ + SchemaObject so4; + EXPECT_EQ(so4.ParseFromSchemaString(schema4), -E_SCHEMA_PARSE_FAIL); + + /** + * @tc.steps: step9. Create a shema string include a single index with all columns not exists + */ + std::string schema5; + GenerateSchemaString(schema5, "[[\"name1\", \"father.name2\", \"father1.id\", \"id2\", \"tel\"]]"); + + /** + * @tc.steps: step10. Call SchemaObject.ParseFromSchemaString to parse the string. + * @tc.expected: step10. Expect return -E_SCHEMA_PARSE_FAIL. + */ + SchemaObject so5; + EXPECT_EQ(so5.ParseFromSchemaString(schema5), -E_SCHEMA_PARSE_FAIL); +} + +/** + * @tc.name: UnionIndexCreatTest001 + * @tc.desc: Test the Json uoin index create function + * @tc.type: FUNC + * @tc.require: AR000F3OPD + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBStorageIndexOptimizeTest, UnionIndexCreatTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a correct shema string include a correct union index. + */ + std::string schema; + GenerateSchemaString(schema, "[[\"name\", \"father.name\"]]"); + + /** + * @tc.steps: step2. Create a kvStore with the schema string. + */ + KvStoreNbDelegate::Option option; + option.schema = schema; + g_mgr.GetKvStore(STORE_ID_1, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(CheckIndexFromDbFile(GetKvStoreDirectory(USER_ID, APP_ID, STORE_ID_1), "$.name")); +} + +/** + * @tc.name: SuggestIndexTest001 + * @tc.desc: Test the Suggest index verify function + * @tc.type: FUNC + * @tc.require: AR000F3OPE + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBStorageIndexOptimizeTest, SuggestIndexTest001, TestSize.Level1) +{ + std::string schema; + GenerateSchemaString(schema, "[\"name\", \"id\"]"); + SchemaObject schemaObject1; + ASSERT_EQ(schemaObject1.ParseFromSchemaString(schema), E_OK); + + /** + * @tc.steps: step1. Create a Query and call GreaterThan().SuggestIndex(), then check the query1 + * @tc.expected: step1. query1 is valid. + */ + Query query1 = Query::Select().GreaterThan("id", 1).SuggestIndex("id"); + QueryObject queryObject1(query1); + queryObject1.SetSchema(schemaObject1); + queryObject1.Init(); + EXPECT_TRUE(queryObject1.IsValid()); + + /** + * @tc.steps: step2. Create a Query and call SuggestIndex().SuggestIndex(), then check the query2 + * @tc.expected: step2. query1 is invalid. + */ + SchemaObject schemaObject2; + ASSERT_EQ(schemaObject2.ParseFromSchemaString(schema), E_OK); + Query query2 = Query::Select().SuggestIndex("id").SuggestIndex("id"); + QueryObject queryObject2(query2); + queryObject2.SetSchema(schemaObject2); + queryObject2.Init(); + EXPECT_FALSE(queryObject2.IsValid()); + + /** + * @tc.steps: step3. Create a Query and call SuggestIndex().GreaterThan(), then check the query3 + * @tc.expected: step4. query3 is invalid. + */ + SchemaObject schemaObject3; + ASSERT_EQ(schemaObject3.ParseFromSchemaString(schema), E_OK); + Query query3 = Query::Select().SuggestIndex("id").GreaterThan("id", 1); + QueryObject queryObject3(query3); + queryObject3.SetSchema(schemaObject3); + queryObject3.Init(); + EXPECT_FALSE(queryObject3.IsValid()); +} + +/** + * @tc.name: SuggestIndexTest002 + * @tc.desc: Test the Query parse sql the SuggestIndex + * @tc.type: FUNC + * @tc.require: AR000F3OPE + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBStorageIndexOptimizeTest, SuggestIndexTest002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a schema include index name, id, phone. + */ + std::string schema; + GenerateSchemaString(schema, "[\"name\", \"id\", \"phone\"]"); + + /** + * @tc.steps: step2. Create Query,call GreaterThan("id").GreaterThan("phone").SuggestIndex("id") + */ + SchemaObject schemaObject1; + ASSERT_EQ(schemaObject1.ParseFromSchemaString(schema), E_OK); + Query query1 = Query::Select().GreaterThan("id", 1).And().GreaterThan("phone", 1).SuggestIndex("id"); + + /** + * @tc.steps: step3. Create QueryObject with query1 and call GetQuerySql to check the sql + * @tc.expected: step3. the sql contains "INDEXED BY $.id". + */ + QueryObject queryObject1(query1); + queryObject1.SetSchema(schemaObject1); + int errCode = E_OK; + SqliteQueryHelper helper = queryObject1.GetQueryHelper(errCode); + ASSERT_EQ(errCode, E_OK); + std::string sql1; + ASSERT_EQ(helper.GetQuerySql(sql1, false), E_OK); + size_t pos = sql1.find("INDEXED BY '$.id'", 0); + ASSERT_TRUE(pos != std::string::npos); + + SqliteQueryHelper helper1 = queryObject1.GetQueryHelper(errCode); + ASSERT_EQ(errCode, E_OK); + /** + * @tc.steps: step4. Create a kvStore with the schema string. + */ + KvStoreNbDelegate::Option option; + option.schema = schema; + g_mgr.GetKvStore(STORE_ID_1, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + + /** + * @tc.steps: step5. put a valid value + */ + Value value(JSON_VALUE.begin(), JSON_VALUE.end()); + ASSERT_EQ(g_kvNbDelegatePtr->Put(KEY_1, value), OK); + std::vector entries; + + /** + * @tc.steps: step6. GetEntries with the query1 + * @tc.expected: step6. GetEntries return OK, and the out value is the given value. + */ + ASSERT_EQ(errCode, E_OK); + ASSERT_EQ(g_kvNbDelegatePtr->GetEntries(query1, entries), OK); + EXPECT_TRUE(value == entries[0].value); + ASSERT_EQ(errCode, E_OK); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + SqliteQueryHelper helper2 = queryObject1.GetQueryHelper(errCode); + ASSERT_EQ(errCode, E_OK); + /** + * @tc.steps: step7. Create Query,call GreaterThan("id").SuggestIndex("car") + */ + SchemaObject schemaObject3; + ASSERT_EQ(schemaObject3.ParseFromSchemaString(schema), E_OK); + Query query3 = Query::Select().GreaterThan("id", 1).SuggestIndex("car"); + + /** + * @tc.steps: step8. Create QueryObject with query3 and call GetQuerySql to check the sql + * @tc.expected: step8. the sql not contains "INDEXED BY $.car". + */ + QueryObject queryObject3(query3); + queryObject3.SetSchema(schemaObject3); + SqliteQueryHelper helper3 = queryObject1.GetQueryHelper(errCode); + ASSERT_EQ(errCode, E_OK); + std::string sql3; + ASSERT_EQ(helper3.GetQuerySql(sql3, false), E_OK); + pos = sql3.find("INDEXED BY '$.car'", 0); + ASSERT_TRUE(pos == std::string::npos); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_memory_single_ver_naturall_store_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_memory_single_ver_naturall_store_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d7eb017f6a953d0a0733bf2eac1daefc80affcc --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_memory_single_ver_naturall_store_test.cpp @@ -0,0 +1,1040 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_constant.h" +#include "distributeddb_storage_single_ver_natural_store_testcase.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + DistributedDB::KvStoreConfig g_config; + std::string g_testDir; + const std::string MEM_URL = "file:31?mode=memory&cache=shared"; + DistributedDB::SQLiteSingleVerNaturalStore *g_store = nullptr; + DistributedDB::SQLiteSingleVerNaturalStoreConnection *g_connection = nullptr; +} + +class DistributedDBStorageMemorySingleVerNaturalStoreTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageMemorySingleVerNaturalStoreTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + LOGD("Test dir is %s", g_testDir.c_str()); + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/TestGeneralNB/" + DBConstant::SINGLE_SUB_DIR); +} + +void DistributedDBStorageMemorySingleVerNaturalStoreTest::TearDownTestCase(void) {} + +void DistributedDBStorageMemorySingleVerNaturalStoreTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + KvDBProperties property; + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, "TestGeneralNB"); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, "31"); + property.SetBoolProp(KvDBProperties::MEMORY_MODE, true); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + g_store = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(g_store, nullptr); + ASSERT_EQ(g_store->Open(property), E_OK); + + int erroCode = E_OK; + g_connection = static_cast(g_store->GetDBConnection(erroCode)); + ASSERT_NE(g_connection, nullptr); + g_store->DecObjRef(g_store); + EXPECT_EQ(erroCode, E_OK); +} + +void DistributedDBStorageMemorySingleVerNaturalStoreTest::TearDown(void) +{ + if (g_connection != nullptr) { + g_connection->Close(); + } + + g_store = nullptr; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/TestGeneralNB/" + DBConstant::SINGLE_SUB_DIR); +} + +/** + * @tc.name: GetSyncData001 + * @tc.desc: To test the function of querying the data in the time stamp range in the database. + * @tc.type: FUNC + * @tc.require: AR000CRAKO + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, GetSyncData001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, C) interface of the NaturalStore, where AB + * @tc.expected: step1. The value of GetSyncData is E_INVALID_ARG. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData003(g_store, g_connection); +} + +/** + * @tc.name: GetSyncData004 + * @tc.desc: To the test database Subcon reading, a large number of data records exist in the time stamp range. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, GetSyncData004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, B) interface of the NaturalStore. + * @tc.expected: step1. Return E_GET_UNFINISHED. + */ + /** + * @tc.steps:step2. Continue to obtain data through the GetSyncDataNext() interface + * of the NaturalStore until the E_GET_FINISHED message is returned. + * @tc.expected: step2. When the GetSyncDataNext returns E_GET_FINISHED, + * the total number of obtained data is the number of inserted data and the data is consistent. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData004(g_store, g_connection); +} + +/** + * @tc.name: GetSyncData005 + * @tc.desc: In the test database, if a large number of data records exist + * in the time stamp range, a packet is read successfully. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, GetSyncData005, TestSize.Level1) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, B) interface of the NaturalStore. + * @tc.expected: step1. The total size of all data in OK, dataItems is 99K. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData005(g_store, g_connection); +} + +/** + * @tc.name: GetSyncData006 + * @tc.desc: To test the function of reading data when the time stamp range in the database + * is greater than the value of blockSize. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, GetSyncData006, TestSize.Level1) +{ + /** + * @tc.steps:step1. Use the GetSyncData(A, B) interface of the NaturalStore + * and set blockSize to 50 kb to obtain the data within the time stamp range. + * @tc.expected: step1. The system returns E_GET_FINISHED. The size of the obtained data is 1 kb. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData006(g_store, g_connection); +} + +/** + * @tc.name: PutSyncData001 + * @tc.desc: To test the function of synchronizing the new data of the remote device that synchronizes the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, PutSyncData001, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Set Ioption to synchronous data and insert a (key1, value1) data record by put interface. + */ + /** + * @tc.steps:step3. Insert a (key1, value2!=value1, timestamp, false) data record + * through the PutSyncData interface. The value of timestamp is less than or equal + * to the value of timestamp. For Compare the timestamp to determine whether to synchronization data. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. The Ioption is set to synchronize data + * through the Get interface to obtain the value data of the key1. + * @tc.expected: step4. Return OK.The obtained value is value1. + */ + /** + * @tc.steps:step5. Insert a (key1, value3!=value1, timestamp, false) data record + * through the PutSyncData interface of the NaturalStore. The value of timestamp + * is greater than that of timestamp inserted in 2. + * @tc.expected: step5. Return OK. + */ + /** + * @tc.steps:step6. The Ioption is set to synchronize data through the Get interface + * to obtain the value data of the key1. + * @tc.expected: step6. Return OK. + */ + /** + * @tc.steps:step7. Insert a (key2, value4) data record through the PutSyncData interface. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps:step8. The Ioption is set to synchronize data + * through the Get interface to obtain the value data of the key2. + * @tc.expected: step8. Returns OK, and the obtained data is value4. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::PutSyncData001(g_store, g_connection); +} + +/** + * @tc.name: PutSyncData002 + * @tc.desc: To test the function of synchronizing data from the remote device + * to the local device after the data is deleted from the remote device. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, PutSyncData002, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Set Ioption to synchronous data and insert a (key1, value1) data record by put interface. + */ + /** + * @tc.steps:step3. Insert a (key1, value2!=value1, timestamp, false) data record + * through the PutSyncData interface. The value of timestamp is less than or equal + * to the value of timestamp. For Compare the timestamp to determine whether delete data. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. The Ioption is set to synchronize data + * through the Get interface to obtain the value data of the key1. + * @tc.expected: step4. Return OK.The obtained value is value1. + */ + /** + * @tc.steps:step5. Insert a (key1, value3!=value1, timestamp, false) data record + * through the PutSyncData interfac. The value of timestamp + * is greater than that of timestamp inserted in step2. + * @tc.expected: step5. Return OK. + */ + /** + * @tc.steps:step6. The Ioption is set to synchronize data through the Get interface + * to obtain the value data of the key1. + * @tc.expected: step6. Return E_NOT_FOUND. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::PutSyncData002(g_store, g_connection); +} + +/** + * @tc.name: PutSyncData003 + * @tc.desc: To test the function of synchronizing the mixed data of the added + * and deleted data from the remote device to the local device. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, PutSyncData003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Insert a data record (key1,value1 is not null) and (key2, value2 is not null) + * through the PutSyncData interface. + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. Set Ioption as the synchronization data to obtain the data of key1 and key2. + * @tc.expected: step2. The Get interface returns OK. The value of key1 is value1, + * and the value of key2 is value2. + */ + /** + * @tc.steps:step3. Insert a (key3, value3) and delete the data of the (key1, value1). + * @tc.expected: step3. The PutSyncData returns OK. + */ + /** + * @tc.steps:step4. Set Ioption to the synchronization data and obtain the data of key1, key2, and key3. + * @tc.expected: step4. Get key1 returns E_NOT_FOUND,Get key2. + * The value of OK,value is value2, the value of Get key3 is OK, + * and the value of value is value3. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::PutSyncData003(g_store, g_connection); +} + +/** + * @tc.name: PutMetaData001 + * @tc.desc: Test metadata insertion and modification. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, PutMetaData001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step2. The obtained value is the same as the value of value1. + */ + /** + * @tc.steps:step3. The key value is key1, the value is not empty, + * and the value of value2 is different from the value of value1 through the PutMetaData interface. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. Run the GetMetaData command to obtain the value of key1 + * and check whether the value is the same as the value of value2. + * @tc.expected: step4. The obtained value is the same as the value of value2. + */ + /** + * @tc.steps:step5. Use PutMetaData to insert a record whose key is empty and value is not empty. + * @tc.expected: step5. Return E_INVALID_ARGS. + */ + /** + * @tc.steps:step6. Use PutMetaData in NaturalStore to insert data whose key2(!=key1) + * is not empty and value is empty. + * @tc.expected: step6. Return OK. + */ + /** + * @tc.steps:step7. Obtain the value of key2 and check whether the value is empty. + * @tc.expected: step7. The obtained value is empty. + */ + /** + * @tc.steps:step8. Insert the data whose key size is 1024 and value size is 4Mb + * through PutMetaData of NaturalStore. + * @tc.expected: step8. Return OK. + */ + /** + * @tc.steps:step9/10. Insert data items whose key size is greater than 1 kb + * or value size greater than 4Mb through PutMetaData of NaturalStore. + * @tc.expected: step9/10. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::PutMetaData001(g_store, g_connection); +} + +/** + * @tc.name: GetMetaData001 + * @tc.desc: To test the function of reading the metadata of a key in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, GetMetaData001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step2. The obtained value is the same as the value of value1. + */ + /** + * @tc.steps:step3. The key value is key1, the value is not empty, + * and the value of value2 is different from the value of value1 through the PutMetaData interface. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. Run the GetMetaData command to obtain the value of key1 + * and check whether the value is the same as the value of value2. + * @tc.expected: step4. The obtained value is the same as the value of value2. + */ + /** + * @tc.steps:step5. Use PutMetaData to insert a record whose key is empty and value is not empty. + * @tc.expected: step5. Return E_INVALID_ARGS. + */ + /** + * @tc.steps:step6. Use PutMetaData in NaturalStore to insert data whose key2(!=key1) + * is not empty and value is empty. + * @tc.expected: step6. Return OK. + */ + /** + * @tc.steps:step7. Obtain the value of key2 and check whether the value is empty. + * @tc.expected: step7. The obtained value is empty. + */ + /** + * @tc.steps:step8. Insert the data whose key size is 1024 and value size is 4Mb + * through PutMetaData of NaturalStore. + * @tc.expected: step8. Return OK. + */ + /** + * @tc.steps:step9/10. Insert data items whose key size is greater than 1 kb + * or value size greater than 4Mb through PutMetaData of NaturalStore. + * @tc.expected: step9/10. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetMetaData001(g_store, g_connection); +} + +/** + * @tc.name: GetCurrentMaxTimestamp001 + * @tc.desc: To test the function of obtaining the maximum timestamp when a record exists in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, GetCurrentMaxTimestamp001, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Insert a data record into the synchronization database. + */ + /** + * @tc.steps:step3. The current maximum timestamp is A. + */ + /** + * @tc.steps:step4. Insert a data record into the synchronization database. + */ + /** + * @tc.steps:step5. Obtain the maximum timestamp B and check whether B>=A exists. + * @tc.expected: step5. The obtained timestamp is B>=A. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetCurrentMaxTimestamp001(g_store, g_connection); +} + +/** + * @tc.name: GetCurrentMaxTimestamp002 + * @tc.desc: Obtain the maximum timestamp when no record exists in the test record library. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, GetCurrentMaxTimestamp002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Obtains the maximum timestamp in the current database record. + * @tc.expected: step1. Return timestamp is 0. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetCurrentMaxTimestamp002(g_store); +} + +/** + * @tc.name: LocalDatabaseOperate001 + * @tc.desc: Test the function of inserting data in the local database of the NaturalStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, LocalDatabaseOperate001, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Set Ioption to the local data and insert a record of key1 and value1. + * @tc.expected: step1/2. Return OK. + */ + /** + * @tc.steps: step3. Set Ioption to the local data and obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step3. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step4. Ioption Set this parameter to the local data. Insert key1. + * The value cannot be empty. value2(!=value1) + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data, GetMetaData to obtain the value of key1, + * and check whether the value is the same as the value of value2. + * @tc.expected: step5. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step6. The Ioption parameter is set to the local data. + * The data record whose key is empty and value is not empty is inserted. + * @tc.expected: step6. Return E_INVALID_DATA. + */ + /** + * @tc.steps: step7. Set Ioption to the local data, insert data + * whose key2(!=key1) is not empty, and value is empty. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps: step8. Set option to local data, obtain the value of key2, + * and check whether the value is empty. + * @tc.expected: step8. Return OK, value is empty. + */ + /** + * @tc.steps: step9. Ioption Set the local data. + * Insert the data whose key size is 1024 and value size is 4Mb. + * @tc.expected: step9. Return OK. + */ + /** + * @tc.steps: step10/11. Set Ioption to the local data and insert data items + * whose value is greater than 4Mb or key is bigger than 1Kb + * @tc.expected: step10/11. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::LocalDatabaseOperate001(g_store, g_connection); +} + +/** + * @tc.name: LocalDatabaseOperate002 + * @tc.desc: Test the function of deleting data from the local database of the NaturalStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, LocalDatabaseOperate002, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Set Ioption to the local data and insert a record of key1 and value1. + * @tc.expected: step1/2. Return OK. + */ + /** + * @tc.steps: step3. Set Ioption to the local data and obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step3. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step4. Ioption Set this parameter to the local data. Insert key1. + * The value cannot be empty. value2(!=value1) + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data, GetMetaData to obtain the value of key1, + * and check whether the value is the same as the value of value2. + * @tc.expected: step5. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step6. The Ioption parameter is set to the local data. + * The data record whose key is empty and value is not empty is inserted. + * @tc.expected: step6. Return E_INVALID_DATA. + */ + /** + * @tc.steps: step7. Set Ioption to the local data, insert data + * whose key2(!=key1) is not empty, and value is empty. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps: step8. Set option to local data, obtain the value of key2, + * and check whether the value is empty. + * @tc.expected: step8. Return OK, value is empty. + */ + /** + * @tc.steps: step9. Ioption Set the local data. + * Insert the data whose key size is 1024 and value size is 4Mb. + * @tc.expected: step9. Return OK. + */ + /** + * @tc.steps: step10/11. Set Ioption to the local data and insert data items + * whose value is greater than 4Mb or key is bigger than 1Kb + * @tc.expected: step10/11. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::LocalDatabaseOperate002(g_store, g_connection); +} + +/** + * @tc.name: LocalDatabaseOperate003 + * @tc.desc: To test the function of reading data from the local database of the NaturalStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, LocalDatabaseOperate003, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Set Ioption to the local data and insert a record of key1 and value1. + * @tc.expected: step1/2. Return OK. + */ + /** + * @tc.steps: step3. Set Ioption to the local data and obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step3. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step4. Ioption Set this parameter to the local data. Insert key1. + * The value cannot be empty. value2(!=value1) + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data, GetMetaData to obtain the value of key1, + * and check whether the value is the same as the value of value2. + * @tc.expected: step5. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step6. The Ioption parameter is set to the local data. + * The data record whose key is empty and value is not empty is inserted. + * @tc.expected: step6. Return E_INVALID_DATA. + */ + /** + * @tc.steps: step7. Set Ioption to the local data, insert data + * whose key2(!=key1) is not empty, and value is empty. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps: step8. Set option to local data, obtain the value of key2, + * and check whether the value is empty. + * @tc.expected: step8. Return OK, value is empty. + */ + /** + * @tc.steps: step9. Ioption Set the local data. + * Insert the data whose key size is 1024 and value size is 4Mb. + * @tc.expected: step9. Return OK. + */ + /** + * @tc.steps: step10/11. Set Ioption to the local data and insert data items + * whose value is greater than 4Mb or key is bigger than 1Kb + * @tc.expected: step10/11. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::LocalDatabaseOperate003(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate001 + * @tc.desc: To test the function of inserting data of the local device in the synchronization database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, SyncDatabaseOperate001, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Set Ioption to the local data and insert a record of key1 and value1. + * @tc.expected: step1/2. Return OK. + */ + /** + * @tc.steps: step3. Set Ioption to the local data and obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step3. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step4. Ioption Set this parameter to the local data. Insert key1. + * The value cannot be empty. value2(!=value1) + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data, GetMetaData to obtain the value of key1, + * and check whether the value is the same as the value of value2. + * @tc.expected: step5. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step6. The Ioption parameter is set to the local data. + * The data record whose key is empty and value is not empty is inserted. + * @tc.expected: step6. Return E_INVALID_DATA. + */ + /** + * @tc.steps: step7. Set Ioption to the local data, insert data + * whose key2(!=key1) is not empty, and value is empty. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps: step8. Set option to local data, obtain the value of key2, + * and check whether the value is empty. + * @tc.expected: step8. Return OK, value is empty. + */ + /** + * @tc.steps: step9. Ioption Set the local data. + * Insert the data whose key size is 1024 and value size is 4Mb. + * @tc.expected: step9. Return OK. + */ + /** + * @tc.steps: step10/11. Set Ioption to the local data and insert data items + * whose value is greater than 4Mb or key is bigger than 1Kb + * @tc.expected: step10/11. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate001(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate002 + * @tc.desc: test the put operation after data synced from other devices. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, SyncDatabaseOperate002, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Add a remote synchronization data record. (key1, value1). + */ + /** + * @tc.steps: step3. Ioption is set to synchronous data. Obtains the value data of the key1. + * @tc.expected: step3. Return OK. The value is the same as the value of value1. + */ + /** + * @tc.steps: step4. Ioption Set the data to be synchronized and insert the data of key1,value2. + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step3. Ioption is set to synchronous data. Obtains the value data of the key1. + * @tc.expected: step3. Return OK. The value is the same as the value of value2. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate002(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate003 + * @tc.desc: test the delete operation in sync database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, SyncDatabaseOperate003, TestSize.Level1) +{ + /** + * @tc.steps: step2. Set Ioption to the local data and delete the data whose key is key1 (empty). + * @tc.expected: step2. Return E_INVALID_ARGS. + */ + /** + * @tc.steps: step3. Set Ioption to the local data, insert non-null key1, and non-null value1 data. + * @tc.expected: step3. Return E_OK. + */ + /** + * @tc.steps: step4. Set Ioption to the local data, obtain the value of key1, + * and check whether the value is the same as that of value1. + * @tc.expected: step4. Return E_OK. The obtained value is the same as the value of value1. + */ + /** + * @tc.steps: step5. Set Ioption to the local data and delete the data whose key is key1. + * @tc.expected: step5. Return E_OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data and obtain the value of Key1. + * @tc.expected: step5. Return E_NOT_FOUND. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate003(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate004 + * @tc.desc: test the delete for the data from other devices in sync database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, SyncDatabaseOperate004, TestSize.Level1) +{ + /** + * @tc.steps: step2. The Ioption parameter is set to synchronize data to obtain the value data of the key1. + * @tc.expected: step2. Return OK. The value is the same as the value of value1. + */ + /** + * @tc.steps: step3. The Ioption parameter is set to synchronize data, and the key1 data is deleted. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps: step4. The Ioption parameter is set to synchronize data to obtain the value data of the key1. + * @tc.expected: step4. Return E_NOT_FOUND. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate004(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate005 + * @tc.desc: test the reading for sync database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, SyncDatabaseOperate005, TestSize.Level1) +{ + /** + * @tc.steps: step2. Set Ioption to the local data and delete the data whose key is key1 (empty). + * @tc.expected: step2. Return E_INVALID_ARGS. + */ + /** + * @tc.steps: step3. Set Ioption to the local data, insert non-null key1, and non-null value1 data. + * @tc.expected: step3. Return E_OK. + */ + /** + * @tc.steps: step4. Set Ioption to the local data, obtain the value of key1, + * and check whether the value is the same as that of value1. + * @tc.expected: step4. Return E_OK. The obtained value is the same as the value of value1. + */ + /** + * @tc.steps: step5. Set Ioption to the local data and obtain the value data of Key1. + * Check whether the value is the same as the value of value2. + * @tc.expected: step4. Return E_OK, and the value is the same as the value of value2. + */ + /** + * @tc.steps: step5. The Ioption is set to the local. + * The data of the key1 and value2(!=value1) is inserted. + * @tc.expected: step4. Return E_OK. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate005(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate006 + * @tc.desc: test the get entries for sync database + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, SyncDatabaseOperate006, TestSize.Level1) +{ + /** + * @tc.steps: step2/3/4. Set Ioption to synchronous data. + * Insert the data of key=keyPrefix + 'a', value1. + * Insert the data of key=keyPrefix + 'c', value2. + * Insert the data of key length=keyPrefix length - 1, value3. + * @tc.expected: step2/3/4. Return E_NOT_FOUND. + */ + /** + * @tc.steps: step5. Obtain all data whose prefixKey is keyPrefix. + * @tc.expected: step5. Return OK. The number of obtained data records is 2. + */ + /** + * @tc.steps: step6. Obtain all data whose prefixKey is empty. + * @tc.expected: step6. Return OK. The number of obtained data records is 3. + */ + /** + * @tc.steps: step7. Obtain all data whose prefixKey is keyPrefix. + * @tc.expected: step7. Return E_NOT_SUPPORT. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate006(g_store, g_connection); +} + +/** + * @tc.name: ClearRemoteData001 + * @tc.desc: test the clear data synced from the remote by device. + * @tc.type: FUNC + * @tc.require: AR000CIFDA AR000CQS3T + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, ClearRemoteData001, TestSize.Level1) +{ + /** + * @tc.steps: step1. New data is inserted to the B end of the device. [keyB, valueB]. + */ + /** + * @tc.steps: step2. The device pulls the data of the device B, and the device inserts the [keyA, valueA]. + */ + /** + * @tc.steps: step3. The device obtains the data of keyA and valueB. + * @tc.expected: step3. Obtain [keyA, valueA] and [keyB, valueB]. + */ + /** + * @tc.steps: step4.Invoke the interface for clearing the synchronization data of the B device. + */ + /** + * @tc.steps: step5. The device obtains the data of keyA and valueB. + * @tc.expected: step5. The value of [keyA, valueA] is obtained, + * and the value of NOT_FOUND is obtained by querying keyB. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::ClearRemoteData001(g_store, g_connection); +} + +/** + * @tc.name: DeleteUserKeyValue001 + * @tc.desc: When a user deletes a data record, the system clears the user record. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, DeleteUserKeyValue001, TestSize.Level1) +{ + /** + * @tc.steps: step1. delete K1. + * @tc.expected: step1. delete K1 successfully. + */ + /** + * @tc.steps: step2. Real query by sqlite3. + * @tc.expected: step2. Find KEY_1, not find K2. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::MemoryDbDeleteUserKeyValue001(g_store, g_connection, MEM_URL); +} + +/** + * @tc.name: DeleteUserKeyValue002 + * @tc.desc: After the synchronization library data is deleted locally, add the same key data locally. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, DeleteUserKeyValue002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Delete key1 data via Delete interface. + * @tc.expected: step1. Delete successfully. + */ + /** + * @tc.steps: step2. New data from key1, value3 via Put interface. + * @tc.expected: step2. New data from key1, value3 via Put interface successfully. + */ + /** + * @tc.steps: step3. Query key1 data via Get interface. + * @tc.expected: step3. Query key1 data via Get interface successfully, get value3 by key1. + */ + /** + * @tc.steps: step4. Query key1 real data by sqlite3. + * @tc.expected: step4. Two records were found. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue002(g_store, g_connection, MEM_URL); +} + +/** + * @tc.name: DeleteUserKeyValue003 + * @tc.desc: After the synchronization database data is deleted locally, the same key data is added from the remote end. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, DeleteUserKeyValue003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Delete data by key1. + * @tc.expected: step1. Delete successfully. + */ + /** + * @tc.steps: step2. Get data by key1. + * @tc.expected: step1. Key1 not exist in database. + */ + /** + * @tc.steps: step3. Get a new data from remote device B , key1, value3, + * with a smaller timestamp than the current timestamp. + */ + /** + * @tc.steps: step4. Get data by key1. + * @tc.expected: step4. Key1 not exist in database. + */ + /** + * @tc.steps: step5. Get a new data from remote device C , key1, value4, + * and the timestamp is larger than the current timestamp. + */ + /** + * @tc.steps: step6. Get data by key1. + * @tc.expected: step6. Key1 not exist in database. + */ + /** + * @tc.steps: step7. Get real data by key1. + * @tc.expected: step7. Get 1 record. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue003(g_store, g_connection, MEM_URL); +} + +/** + * @tc.name: DeleteUserKeyValue004 + * @tc.desc: Changes in key after remote delete data syncs to local + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, DeleteUserKeyValue004, TestSize.Level1) +{ + /** + * @tc.steps: step1 2 3. Synchronize data to another device B; delete key1 data from device B; + * pull the action of key1 to local. + */ + /** + * @tc.steps: step4. Close database. + */ + /** + * @tc.steps: step5 6. Get real data by key1;and get the number of records. + * @tc.expected: step5 6. Not exist key1 real data in database;Get 1 record. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::MemoryDbDeleteUserKeyValue004(g_store, g_connection, MEM_URL); +} + +/** + * @tc.name: DeleteUserKeyValue005 + * @tc.desc: New unified key data locally after remote delete data syncs to local + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, DeleteUserKeyValue005, TestSize.Level1) +{ + /** + * @tc.steps: step1 2 3. Synchronize data to another device B; delete key1 data from device B; + * pull the action of key1 to local. + */ + /** + * @tc.steps: step4. Put K1 V1 to database. + * @tc.expected: step4. Put successfully. + */ + /** + * @tc.steps: step5. Close database. + */ + /** + * @tc.steps: step6 7. Get real data by key1;and get the number of records. + * @tc.expected: step6 7. Not exist key1 real data in database;Get 2 record. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::MemoryDbDeleteUserKeyValue005(g_store, g_connection, MEM_URL); +} + +/** + * @tc.name: DeleteUserKeyValue006 + * @tc.desc: After the remote delete data is synced to the local, + * the same key data is added from the remote other devices + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageMemorySingleVerNaturalStoreTest, DeleteUserKeyValue006, TestSize.Level1) +{ + /** + * @tc.steps: step1. Remote device B sync deletes data key1 and pushes to local. + */ + /** + * @tc.steps: step2. Get key1 from database. + * @tc.expected: step2. Not exist key1. + */ + /** + * @tc.steps: step3. Remote device C syncs new data (key1, value2), + * timestamp is less than delete timestamp, to local. + */ + /** + * @tc.steps: step4. Get key1 from database. + * @tc.expected: step4. Not exist key1. + */ + /** + * @tc.steps: step5. Remote device C syncs new data (key1, value2), + * timestamp is bigger than delete timestamp, to local. + */ + /** + * @tc.steps: step6. Get key1 from database. + * @tc.expected: step6. Exist key1. + */ + /** + * @tc.steps: step7. Get real data from database. + * @tc.expected: step7. Get 1 record. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue006(g_store, g_connection, MEM_URL); +} + diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_query_sync_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_query_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c8791c199cbcc59a5f9dee27afd21ee762bd5200 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_query_sync_test.cpp @@ -0,0 +1,1233 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "db_common.h" +#include "db_errno.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "generic_single_ver_kv_entry.h" +#include "kvdb_manager.h" +#include "query_sync_object.h" +#include "sqlite_single_ver_continue_token.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + DistributedDB::KvStoreConfig g_config; + std::string g_testDir; + DistributedDB::SQLiteSingleVerNaturalStore *g_store = nullptr; + DistributedDB::SQLiteSingleVerNaturalStore *g_schemaStore = nullptr; + DistributedDB::SQLiteSingleVerNaturalStoreConnection *g_connection = nullptr; + DistributedDB::SQLiteSingleVerNaturalStoreConnection *g_schemaConnect = nullptr; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + + const std::string REMOTE_DEVICE_ID = "remote_device_id"; + const Key PREFIX_KEY = { 'k' }; + const Key KEY1 = { 'k', '1' }; + const Key KEY2 = { 'k', '2' }; + const Key KEY3 = { 'k', '3' }; + const Value VALUE1 = { 'v', '1' }; + const Value VALUE2 = { 'v', '2' }; + const Value VALUE3 = { 'v', '3' }; + + void ReleaseKvEntries(std::vector &entries) + { + for (auto &itemEntry : entries) { + delete itemEntry; + itemEntry = nullptr; + } + entries.clear(); + } + + const string SCHEMA_STRING = + "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"BOOL\"," + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, NOT NULL, DEFAULT '3.1415'\"," + "\"field_name7\":\"LONG, DEFAULT 100\"," + "\"field_name8\":\"LONG, DEFAULT 100\"," + "\"field_name9\":\"LONG, DEFAULT 100\"," + "\"field_name10\":\"LONG, DEFAULT 100\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2\"]}"; + + const std::string SCHEMA_VALUE1 = + "{\"field_name1\":true," + "\"field_name2\":false," + "\"field_name3\":10," + "\"field_name4\":20," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415\"," + "\"field_name7\":100," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; +} + +class DistributedDBStorageQuerySyncTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageQuerySyncTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + LOGD("Test dir is %s", g_testDir.c_str()); + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/TestQuerySync/" + DBConstant::SINGLE_SUB_DIR); + + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + // Create schema database + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_STRING; + g_mgr.GetKvStore("QuerySyncSchema", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + Value value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end()); + g_kvNbDelegatePtr->Put(KEY_1, value); + + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); +} + +void DistributedDBStorageQuerySyncTest::TearDownTestCase(void) +{ + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +void DistributedDBStorageQuerySyncTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + KvDBProperties property; + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, "31"); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, "TestQuerySync"); + property.SetBoolProp(KvDBProperties::MEMORY_MODE, false); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + property.SetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, ConflictResolvePolicy::DEVICE_COLLABORATION); + g_store = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(g_store, nullptr); + ASSERT_EQ(g_store->Open(property), E_OK); + + int erroCode = E_OK; + g_connection = static_cast(g_store->GetDBConnection(erroCode)); + ASSERT_NE(g_connection, nullptr); + g_store->DecObjRef(g_store); + EXPECT_EQ(erroCode, E_OK); + + std::string oriIdentifier = USER_ID + "-" + APP_ID + "-" + "QuerySyncSchema"; + std::string identifier = DBCommon::TransferHashString(oriIdentifier); + property.SetStringProp(KvDBProperties::IDENTIFIER_DATA, identifier); + std::string identifierHex = DBCommon::TransferStringToHex(identifier); + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, "QuerySyncSchema"); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, identifierHex); + + g_schemaStore = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(g_schemaStore, nullptr); + ASSERT_EQ(g_schemaStore->Open(property), E_OK); + g_schemaConnect = static_cast(g_schemaStore->GetDBConnection(erroCode)); + ASSERT_NE(g_schemaConnect, nullptr); + + std::vector entries; + IOption option; + option.dataType = IOption::SYNC_DATA; + g_schemaConnect->GetEntries(option, Query::Select(), entries); + ASSERT_FALSE(entries.empty()); + + g_schemaStore->DecObjRef(g_schemaStore); +} + +void DistributedDBStorageQuerySyncTest::TearDown(void) +{ + if (g_connection != nullptr) { + g_connection->Close(); + } + + if (g_schemaConnect != nullptr) { + g_schemaConnect->Close(); + } + + g_store = nullptr; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/TestQuerySync/" + DBConstant::SINGLE_SUB_DIR); +} + +/** + * @tc.name: GetSyncData001 + * @tc.desc: To test the function of querying the data in the time stamp range in the database. + * @tc.type: FUNC + * @tc.require: AR000CRAKO + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQuerySyncData001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, C) interface of the NaturalStore, where APut(option, key, value), E_OK); + + Query query = Query::Select().PrefixKey(key); + QueryObject queryObj(query); + DataSizeSpecInfo specInfo = {4 * 1024 * 1024, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + std::vector entries; + ContinueToken token = nullptr; + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + ReleaseKvEntries(entries); + + Key keyOther = key; + keyOther.push_back('1'); + g_store->ReleaseContinueToken(token); + EXPECT_EQ(g_connection->Put(option, keyOther, value), E_OK); + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 2UL); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); +} + +/** + * @tc.name: GetQuerySyncData002 + * @tc.desc: To test GetSyncData function is available and check the boundary value. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQuerySyncData002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put k1 k2. k1's timestamp is 0 and k2's timestamp is INT64_MAX-1. + * @tc.expected: step1. Put k1 k2 successfully. + */ + DataItem data1{KEY1, VALUE1, 0, DataItem::LOCAL_FLAG, REMOTE_DEVICE_ID}; + DataItem data2{KEY2, VALUE2, INT64_MAX - 1, DataItem::LOCAL_FLAG, REMOTE_DEVICE_ID}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vector{data1, data2}, REMOTE_DEVICE_ID), E_OK); + + /** + * @tc.steps: step2. Get k1 k2. SyncTimeRange is default(all time range). + * @tc.expected: step2. Get k1 k2 successfully. + */ + Query query = Query::Select().PrefixKey(PREFIX_KEY); + QueryObject queryObj(query); + DataSizeSpecInfo specInfo = {4 * 1024 * 1024, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + std::vector entries; + ContinueToken token = nullptr; + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 2UL); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); + + /** + * @tc.steps: step3. Put k3. k3's timestamp t3 is random. + * @tc.expected: step3. Put k3. + */ + auto time3 = static_cast(DistributedDBToolsUnitTest::GetRandInt64(0, g_store->GetCurrentTimestamp())); + DataItem data3{KEY3, VALUE3, time3, DataItem::LOCAL_FLAG, REMOTE_DEVICE_ID}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vector{data3}, REMOTE_DEVICE_ID), E_OK); + + /** + * @tc.steps: step4. Get k3. SyncTimeRange is between t3 and t3 + 1. + * @tc.expected: step4. Get k3 successfully. + */ + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{time3, 0, time3 + 1, 0}, + specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); + + /** + * @tc.steps: step5. Delete k1 k3. + * @tc.expected: step5. Delete k1 k3 successfully. + */ + IOption option{ IOption::SYNC_DATA }; + Timestamp deleteBeginTime = g_store->GetCurrentTimestamp(); + g_connection->DeleteBatch(option, vector{KEY1, KEY3}); + + /** + * @tc.steps: step6. Get deleted data. + * @tc.expected: step6. Get k1 k3. + */ + Timestamp deleteEndTime = g_store->GetCurrentTimestamp(); + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{0, deleteBeginTime, 0, deleteEndTime}, specInfo, token, + entries), E_OK); + EXPECT_EQ(entries.size(), 2UL); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); +} + +/** + * @tc.name: GetQuerySyncData004 + * @tc.desc: To test GetSyncDataNext function is available and check the boundary value. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQuerySyncData004, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put k1 k2. k1's timestamp is 0 and k2's timestamp is INT64_MAX-1. + * @tc.expected: step1. Put k1 k2 successfully. + */ + DataItem data1{KEY1, VALUE1, 0, DataItem::LOCAL_FLAG, REMOTE_DEVICE_ID}; + DataItem data2{KEY2, VALUE2, INT64_MAX - 1, DataItem::LOCAL_FLAG, REMOTE_DEVICE_ID}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vector{data1, data2}, REMOTE_DEVICE_ID), E_OK); + + /** + * @tc.steps: step2. Get k1 k2. SyncTimeRange is default(all time range). + * @tc.expected: step2. Get k1 k2 successfully. + */ + Query query = Query::Select().PrefixKey(PREFIX_KEY); + QueryObject queryObj(query); + DataSizeSpecInfo specInfo = {4 * 1024 * 1024, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + std::vector entries; + ContinueToken token = new (std::nothrow) SQLiteSingleVerContinueToken{SyncTimeRange{}, queryObj}; + EXPECT_EQ(g_store->GetSyncDataNext(entries, token, specInfo), E_OK); + EXPECT_EQ(entries.size(), 2UL); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); + + /** + * @tc.steps: step3. Put k3. k3's timestamp t3 is random. + * @tc.expected: step3. Put k3. + */ + auto time3 = static_cast(DistributedDBToolsUnitTest::GetRandInt64(0, g_store->GetCurrentTimestamp())); + DataItem data3{KEY3, VALUE3, time3, DataItem::LOCAL_FLAG, REMOTE_DEVICE_ID}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vector{data3}, REMOTE_DEVICE_ID), E_OK); + + /** + * @tc.steps: step4. Get k3. SyncTimeRange is between t3 and t3 + 1. + * @tc.expected: step4. Get k3 successfully. + */ + token = new (std::nothrow) SQLiteSingleVerContinueToken{SyncTimeRange{time3, 0, time3 + 1}, queryObj}; + EXPECT_EQ(g_store->GetSyncDataNext(entries, token, specInfo), E_OK); + EXPECT_EQ(entries.size(), 1UL); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); + + /** + * @tc.steps: step5. Delete k1 k3. + * @tc.expected: step5. Delete k1 k3 successfully. + */ + IOption option{ IOption::SYNC_DATA }; + Timestamp deleteBeginTime = g_store->GetCurrentTimestamp(); + g_connection->DeleteBatch(option, vector{KEY1, KEY3}); + + /** + * @tc.steps: step6. Get deleted data. + * @tc.expected: step6. Get k1 k3. + */ + Timestamp deleteEndTime = g_store->GetCurrentTimestamp(); + token = new (std::nothrow) SQLiteSingleVerContinueToken{SyncTimeRange{0, deleteBeginTime, 0, deleteEndTime}, + queryObj}; + EXPECT_EQ(g_store->GetSyncDataNext(entries, token, specInfo), E_OK); + EXPECT_EQ(entries.size(), 2UL); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); +} + +/** + * @tc.name: GetQuerySyncData006 + * @tc.desc: To test if parameter is invalid, GetSyncData function return an E_INVALID_ARGS code. If no data found, + GetSyncData will return E_OK but entries will be empty. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQuerySyncData006, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get sync data when no data exists in DB. + * @tc.expected: step1. GetSyncData return E_OK and entries is empty. + */ + Query query = Query::Select().PrefixKey(PREFIX_KEY); + QueryObject queryObj(query); + DataSizeSpecInfo specInfo = {4 * 1024 * 1024, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + std::vector entries; + ContinueToken token = nullptr; + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_TRUE(entries.empty()); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); + + /** + * @tc.steps: step2. Get sync data with invalid SyncTimeRange(beginTime is greater than endTime). + * @tc.expected: step2. GetSyncData return E_INVALID_ARGS. + */ + auto time = static_cast(DistributedDBToolsUnitTest::GetRandInt64(0, INT64_MAX)); + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{time, 0, 0}, specInfo, token, entries), + -E_INVALID_ARGS); +} + +/** + * @tc.name: GetQuerySyncData006 + * @tc.desc: To test QUERY_SYNC_THRESHOLD works. When all query data is found in one get sync data operation, + if the size of query data is greater than QUERY_SYNC_THRESHOLD*MAX_ITEM_SIZE , will not get deleted data next. + Otherwise, will get deleted data next. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQuerySyncData008, TestSize.Level1) +{ + const size_t maxItemSize = 200; + const float querySyncThreshold = 0.50; + + Key key; + Value value; + string str; + IOption option{ IOption::SYNC_DATA }; + + /** + * @tc.steps: step1. Put MAX_ITEM_SIZE / 2 + 1 entries from k0 to k100. + * @tc.expected: step1. Put data successfully. + */ + for (unsigned i = 0; i <= maxItemSize * querySyncThreshold; i++) { + str = "k" + to_string(i); + key = Key(str.begin(), str.end()); + str[0] = 'v'; + value = Value(str.begin(), str.end()); + EXPECT_EQ(g_connection->Put(option, key, value), E_OK); + } + + DataItem item{key, value}; + auto oneBlockSize = SQLiteSingleVerStorageExecutor::GetDataItemSerialSize(item, Parcel::GetAppendedLen()); + + /** + * @tc.steps: step2. Delete k0. + * @tc.expected: step2. Delete k0 successfully. + */ + str = "k0"; + g_connection->Delete(option, Key(str.begin(), str.end())); + + /** + * @tc.steps: step3. Get sync data when 100 query data and 1 deleted data exists in DB. + * @tc.expected: step3. Get 100 query data and no deleted data. + */ + Query query = Query::Select().PrefixKey(PREFIX_KEY); + QueryObject queryObj(query); + + DataSizeSpecInfo specInfo = {static_cast((maxItemSize) * oneBlockSize), maxItemSize}; + std::vector entries; + ContinueToken token = nullptr; + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries), -E_UNFINISHED); + EXPECT_EQ(entries.size(), 100UL); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); + + /** + * @tc.steps: step4. Delete k1. + * @tc.expected: step4. Delete k1 successfully. + */ + str = "k1"; + g_connection->Delete(option, Key(str.begin(), str.end())); + + /** + * @tc.steps: step5. Get sync data when 99 query data and 2 deleted data exists in DB. + * @tc.expected: step5. Get 99 query data and 2 deleted data. + */ + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 101UL); + ReleaseKvEntries(entries); + g_store->ReleaseContinueToken(token); +} + +/** + * @tc.name: GetQuerySyncData009 + * @tc.desc: To test GetSyncData and GetSyncDataNext function works with large amounts of data. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQuerySyncData009, TestSize.Level2) +{ + /** + * @tc.steps: step1. Put 500 entries from k0 to k499. + * @tc.expected: step1. Put data successfully. + */ + Key key; + Value value; + string str; + IOption option{ IOption::SYNC_DATA }; + const uint64_t totalSize = 500; // 500 data in DB. + for (unsigned i = 0; i < totalSize; i++) { + str = "k" + to_string(i); + key = Key(str.begin(), str.end()); + str[0] = 'v'; + value = Value(str.begin(), str.end()); + EXPECT_EQ(g_connection->Put(option, key, value), E_OK); + } + + /** + * @tc.steps: step2. Delete 150 entries from k150 to k299. + * @tc.expected: step2. Delete data successfully. + */ + for (unsigned i = 150; i < 300; i++) { + str = "k" + to_string(i); + g_connection->Delete(option, Key(str.begin(), str.end())); + } + + /** + * @tc.steps: step3. Get all sync data; + * @tc.expected: step3. Get 500 data. + */ + Query query = Query::Select().PrefixKey(PREFIX_KEY); + QueryObject queryObj(query); + + uint64_t getSize = 0; + std::vector entries; + const size_t maxItemSize = 100; // Get 100 data at most in one GetSyncData operation. + DataSizeSpecInfo specInfo = {MTU_SIZE, maxItemSize}; + ContinueToken token = nullptr; + g_store->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries); + getSize += entries.size(); + ReleaseKvEntries(entries); + + while (token != nullptr) { + g_store->GetSyncDataNext(entries, token, specInfo); + getSize += entries.size(); + ReleaseKvEntries(entries); + } + + EXPECT_EQ(getSize, totalSize); +} + +/** + * @tc.name: GetQuerySyncData010 + * @tc.desc: To test GetSyncData when Query with limit. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQuerySyncData010, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put 100 entries from k100 to k1. + * @tc.expected: step1. Put data successfully. + */ + Key key; + Value value; + string str; + IOption option{ IOption::SYNC_DATA }; + const uint64_t totalSize = 100; // 100 data in DB. + for (unsigned i = totalSize; i > 0; i--) { + str = "k" + to_string(i); + key = Key(str.begin(), str.end()); + str[0] = 'v'; + value = Value(str.begin(), str.end()); + EXPECT_EQ(g_connection->Put(option, key, value), E_OK); + } + + /** + * @tc.steps: step3. Get half of sync data; + * @tc.expected: step3. Get half of sync data successfully. + */ + Query query = Query::Select().PrefixKey(PREFIX_KEY).Limit(totalSize / 2); + QueryObject queryObj(query); + + uint64_t getSize = 0; + std::vector entries; + const size_t maxItemSize = 10; // Get 10 data at most in one GetSyncData operation. + DataSizeSpecInfo specInfo = {MTU_SIZE, maxItemSize}; + ContinueToken token = nullptr; + g_store->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries); + getSize += entries.size(); + ReleaseKvEntries(entries); + + while (token != nullptr) { + g_store->GetSyncDataNext(entries, token, specInfo); + getSize += entries.size(); + ReleaseKvEntries(entries); + } + + EXPECT_EQ(getSize, totalSize / 2); +} + +/** + * @tc.name: GetQueryID001 + * @tc.desc: To test the function of generating query identity. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQueryID001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get illegal query object, get this object identify + * @tc.expected: step1. GetIdentify will get empty string + */ + Query errQuery = Query::Select().GreaterThan("$.test", true); + QuerySyncObject querySync(errQuery); + EXPECT_EQ(querySync.GetIdentify().empty(), true); + + /** + * @tc.steps:step2. use illegal query object to serialized + * @tc.expected: step2. SerializeData will not return E_OK + */ + vector buffer(querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT), 0); + Parcel writeParcel(buffer.data(), querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + Parcel readParcel(buffer.data(), querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + EXPECT_NE(querySync.SerializeData(writeParcel, SOFTWARE_VERSION_CURRENT), E_OK); +} + +/** + * @tc.name: GetQueryID002 + * @tc.desc: To test the function of generating query identity. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQueryID002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get empty condition query object, get this object identify + * @tc.expected: step1. GetIdentify result not change + */ + Query query1 = Query::Select(); + + QuerySyncObject querySync(query1); + EXPECT_EQ(querySync.GetIdentify().empty(), false); + // same object identify is same + EXPECT_EQ(querySync.GetIdentify(), querySync.GetIdentify()); + + IOption option; + option.dataType = IOption::SYNC_DATA; + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + EXPECT_EQ(g_connection->Put(option, key, value), E_OK); + EXPECT_EQ(g_connection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(g_connection->Put(option, KEY_2, VALUE_2), E_OK); + + /** + * @tc.steps:step2. Get prefix key condition query object, get this object identify + * @tc.expected: step2. GetIdentify result not same as other condition query object + */ + Query query2 = Query::Select().PrefixKey(key); + QuerySyncObject querySync1(query2); + EXPECT_EQ(querySync1.GetIdentify().empty(), false); + // same object identify is not same + EXPECT_NE(querySync.GetIdentify(), querySync1.GetIdentify()); + + /** + * @tc.steps:step3. empty condition query object can serialized and deserialized normally + * @tc.expected: step3. after deserialized, can get all key value in database + */ + vector buffer(querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT), 0); + Parcel writeParcel(buffer.data(), querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + Parcel readParcel(buffer.data(), querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + EXPECT_EQ(querySync.SerializeData(writeParcel, SOFTWARE_VERSION_CURRENT), E_OK); + + QuerySyncObject queryObj2; + EXPECT_EQ(QuerySyncObject::DeSerializeData(readParcel, queryObj2), E_OK); + LOGD("Query obj after serialize!"); + + DataSizeSpecInfo specInfo = {4 * 1024 * 1024, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + std::vector entries; + ContinueToken token = nullptr; + EXPECT_EQ(g_store->GetSyncData(queryObj2, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 3UL); + SingleVerKvEntry::Release(entries); +} + +/** + * @tc.name: GetQueryID003 + * @tc.desc: To test the function of generating query identity ignore limit, orderby, suggestion. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, GetQueryID003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Get empty condition query object, get this object identify + */ + Query query1 = Query::Select().PrefixKey({}); + QuerySyncObject querySync1(query1); + std::string id1 = querySync1.GetIdentify(); + EXPECT_EQ(id1.empty(), false); + + /** + * @tc.steps:step2. Get limit condition query object, get this object identify + * @tc.expected: step2. GetIdentify result same as no contain limit condition + */ + Query query2 = query1.Limit(1, 1); + QuerySyncObject querySync2(query2); + std::string id2 = querySync2.GetIdentify(); + EXPECT_EQ(id2, id1); + + /** + * @tc.steps:step3. Get orderby condition query object, get this object identify + * @tc.expected: step3. GetIdentify result same as no contain orderby condition + */ + Query query3 = query2.OrderBy("$.test"); + QuerySyncObject querySync3(query3); + std::string id3 = querySync3.GetIdentify(); + EXPECT_EQ(id2, id3); +} + +/** + * @tc.name: Serialize001 + * @tc.desc: To test the function of querying the data after serialized and deserialized. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, Serialize001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Put K1V1 K2V2 and rand KV for query + */ + IOption option; + option.dataType = IOption::SYNC_DATA; + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + EXPECT_EQ(g_connection->Put(option, key, value), E_OK); + EXPECT_EQ(g_connection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(g_connection->Put(option, KEY_2, VALUE_2), E_OK); + + /** + * @tc.steps:step2. Put K1V1 K2V2 and rand KV for query + * @tc.expected: step2. GetIdentify result same as no contain limit condition + */ + Query query = Query::Select().PrefixKey(key); + QueryObject queryObj(query); + DataSizeSpecInfo specInfo = {4 * 1024 * 1024, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + std::vector entries; + ContinueToken token = nullptr; + LOGD("Ori query obj!"); + EXPECT_EQ(g_store->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + SingleVerKvEntry::Release(entries); + + /** + * @tc.steps:step3. query result after serialized and deserialized + * @tc.expected: step3. Get same result + */ + QuerySyncObject querySync(query); + vector buffer(querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT), 0); + Parcel writeParcel(buffer.data(), querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + Parcel readParcel(buffer.data(), querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + EXPECT_EQ(querySync.SerializeData(writeParcel, SOFTWARE_VERSION_CURRENT), E_OK); + + QuerySyncObject queryObj1; + EXPECT_EQ(QuerySyncObject::DeSerializeData(readParcel, queryObj1), E_OK); + + LOGD("Query obj after serialize!"); + EXPECT_EQ(g_store->GetSyncData(queryObj1, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + SingleVerKvEntry::Release(entries); + + std::string id = querySync.GetIdentify().c_str(); + EXPECT_EQ(id.size(), 64u); + LOGD("query identify [%s] [%zu]", id.c_str(), id.size()); +} + +/** + * @tc.name: Serialize002 + * @tc.desc: To test the function of serialized illegal query object. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, Serialize002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Serialized illegal query object + * @tc.expected: step1. return not E_OK + */ + Query query = Query::Select().PrefixKey({}).GreaterThan("$.test", true); // bool can not compare + QuerySyncObject querySync(query); + vector buffer(querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT), 0); + Parcel writeParcel(buffer.data(), querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + EXPECT_NE(querySync.SerializeData(writeParcel, SOFTWARE_VERSION_CURRENT), E_OK); +} + +/** + * @tc.name: DeSerialize001 + * @tc.desc: To test the function of deserialized illegal query object. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, DeSerialize001, TestSize.Level1) +{ + /** + * @tc.steps:step1. deserialized empty content query object + * @tc.expected: step1. return not E_OK + */ + QuerySyncObject querySync; + vector buffer(querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT), 0); + Parcel readParcel(buffer.data(), querySync.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + + QuerySyncObject queryObj; + EXPECT_NE(QuerySyncObject::DeSerializeData(readParcel, queryObj), E_OK); + + /** + * @tc.steps:step2. deserialized empty parcel + * @tc.expected: step2. return not E_OK + */ + buffer.resize(0); + Parcel readParcel1(buffer.data(), 0); + EXPECT_NE(QuerySyncObject::DeSerializeData(readParcel1, queryObj), E_OK); + + /** + * @tc.steps:step3. deserialized error size parcel + * @tc.expected: step3. return not E_OK + */ + uint8_t simSize = 0; + RAND_bytes(&simSize, 1); + buffer.resize(simSize); + Parcel readParcel2(buffer.data(), simSize); + EXPECT_NE(QuerySyncObject::DeSerializeData(readParcel2, queryObj), E_OK); +} + +/** + * @tc.name: SameQueryObjectIdInDiffVer001 + * @tc.desc: Same query object have same id in different version. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, SameQueryObjectIdInDiffVer001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Record the fixed id of the query object of the current version, + * and keep it unchanged in subsequent versions + * @tc.expected: step1. Never change in diff version + */ + Query query1 = Query::Select().PrefixKey({}); + QuerySyncObject querySync1(query1); + EXPECT_EQ(querySync1.GetIdentify(), "A9AB721457C4CA98726EECC7CB16F94E31B9752BEE6D08569CFE797B4A64A304"); + + Query query2 = Query::Select(); + QuerySyncObject querySync2(query2); + EXPECT_EQ(querySync2.GetIdentify(), "AF5570F5A1810B7AF78CAF4BC70A660F0DF51E42BAF91D4DE5B2328DE0E83DFC"); + + Query query3 = Query::Select().NotLike("$.test", "testValue"); + QuerySyncObject querySync3(query3); + EXPECT_EQ(querySync3.GetIdentify(), "F2BAC2B53FE81F9928E5F8DCDF502F2419E8CEB5DFC157EEBDDB955A66C0148B"); + + vector fieldValues{1, 1, 1}; + Query query4 = Query::Select().In("$.test", fieldValues); + QuerySyncObject querySync4(query4); + EXPECT_EQ(querySync4.GetIdentify(), "EEAECCD0E1A7217574ED3092C8DAA39469388FA1B8B7B210185B4257B785FE4D"); + + Query query5 = Query::Select().OrderBy("$.test.test_child", false); + QuerySyncObject querySync5(query5); + EXPECT_EQ(querySync5.GetIdentify(), "AF5570F5A1810B7AF78CAF4BC70A660F0DF51E42BAF91D4DE5B2328DE0E83DFC"); + + Query query6 = Query::Select().Limit(1, 2); + QuerySyncObject querySync6(query6); + EXPECT_EQ(querySync6.GetIdentify(), "AF5570F5A1810B7AF78CAF4BC70A660F0DF51E42BAF91D4DE5B2328DE0E83DFC"); + + Query query7 = Query::Select().IsNull("$.test.test_child"); + QuerySyncObject querySync7(query7); + EXPECT_EQ(querySync7.GetIdentify(), "762AB5FDF9B1433D6F398269D4DDD6DE6444953F515E87C6796654180A7FF422"); + + Query query8 = Query::Select().EqualTo("$.test.test_child", true).And().GreaterThan("$.test.test_child", 1); + QuerySyncObject querySync8(query8); + EXPECT_EQ(querySync8.GetIdentify(), "B97FBFFBC690DAF25031FD4EE8ADC92F4698B9E81FD4877CD54EDEA122F6A6E0"); + + Query query9 = Query::Select().GreaterThan("$.test", 1).OrderBy("$.test"); + QuerySyncObject querySync9(query9); + EXPECT_EQ(querySync9.GetIdentify(), "77480E3EE04EB1500BB2F1A31704EE5676DC81F088A7A300F6D30E3FABA7D0A3"); + + Query query = Query::Select().GreaterThan("$.test1", 1).OrderBy("$.test1"); + QuerySyncObject querySync(query); + EXPECT_EQ(querySync.GetIdentify(), "170F5137C0BB49011D7415F706BD96B86F5FAFADA356374981362B1E177263B9"); +} + +/** + * @tc.name: querySyncByField + * @tc.desc: Test for illegal query conditions, use GetSyncData + * @tc.type: FUNC + * @tc.require: + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, querySyncByField, TestSize.Level1) +{ + Query queryInvalidField = Query::Select().EqualTo("$.field_name11", 1); + Query queryInvalidCombine = Query::Select().EqualTo("$.field_name3", 1).BeginGroup(); + Query queryAll = Query::Select(); + Query queryPrefixKeyLimit = Query::Select().PrefixKey({}).Limit(1, 0); + + DataSizeSpecInfo specInfo = {4 * 1024 * 1024, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + std::vector entries; + ContinueToken token = nullptr; + + QueryObject queryObj(queryInvalidCombine); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries), -E_INVALID_QUERY_FORMAT); + + QueryObject queryObj2(queryAll); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj2, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + ReleaseKvEntries(entries); + + QueryObject queryObj1(queryInvalidField); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj1, SyncTimeRange{}, specInfo, token, entries), -E_INVALID_QUERY_FIELD); + + QueryObject queryObj3(queryPrefixKeyLimit); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj3, SyncTimeRange{}, specInfo, token, entries), E_OK); + ReleaseKvEntries(entries); +} + +/** + * @tc.name: IsQueryOnlyByKey + * @tc.desc: The test can correctly determine whether the value is used for query + * @tc.type: FUNC + * @tc.require: + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, IsQueryOnlyByKey, TestSize.Level1) +{ + Query queryAll = Query::Select(); + Query queryPrefixKeyLimit = Query::Select().PrefixKey({}).Limit(1, 0); + Query queryPrefix = Query::Select().PrefixKey({}); + Query queryPrefixKeyLimitIndex = Query::Select().PrefixKey({}).Limit(1, 0).SuggestIndex("$.field_name3"); + Query queryPrefixKeyLimitEQ = Query::Select().PrefixKey({}).Limit(1, 0).EqualTo("$.field_name3", 1); + Query queryEQ = Query::Select().EqualTo("$.field_name3", 1); + Query queryLimitEQ = Query::Select().Limit(1, 0).EqualTo("$.field_name3", 1); + + QueryObject queryObj(queryAll); + EXPECT_TRUE(queryObj.IsQueryOnlyByKey()); + + QueryObject queryObj1(queryPrefixKeyLimit); + EXPECT_TRUE(queryObj1.IsQueryOnlyByKey()); + + QueryObject queryObj2(queryPrefix); + EXPECT_TRUE(queryObj2.IsQueryOnlyByKey()); + + QueryObject queryObj3(queryPrefixKeyLimitIndex); + EXPECT_FALSE(queryObj3.IsQueryOnlyByKey()); + + QueryObject queryObj4(queryPrefixKeyLimitEQ); + EXPECT_FALSE(queryObj4.IsQueryOnlyByKey()); + + QueryObject queryObj5(queryEQ); + EXPECT_FALSE(queryObj5.IsQueryOnlyByKey()); + + QueryObject queryObj6(queryLimitEQ); + EXPECT_FALSE(queryObj6.IsQueryOnlyByKey()); +} + +/** + * @tc.name: MultiQueryParcel + * @tc.desc: Mix multiple conditions for simultaneous query can be serialize + * @tc.type: FUNC + * @tc.require: + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, MultiQueryParcel, TestSize.Level1) +{ + Query queryInvalidField = Query::Select().LessThan("$.field_name1", 1); + Query queryInvalidCombine = Query::Select().EqualTo("$.field_name3", 1).BeginGroup(); + Query queryPrefixKeyLimit = Query::Select().PrefixKey({}).Limit(1, 0); + + DataSizeSpecInfo specInfo = {4 * 1024 * 1024, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + std::vector entries; + ContinueToken token = nullptr; + + QuerySyncObject querySyncObj(queryInvalidField); + vector buffer(querySyncObj.CalculateParcelLen(SOFTWARE_VERSION_CURRENT), 0); + Parcel writeParcel(buffer.data(), querySyncObj.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + Parcel readParcel(buffer.data(), querySyncObj.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + EXPECT_EQ(querySyncObj.SerializeData(writeParcel, SOFTWARE_VERSION_CURRENT), E_OK); + QuerySyncObject queryObjAfterSer; + EXPECT_EQ(QuerySyncObject::DeSerializeData(readParcel, queryObjAfterSer), E_OK); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObjAfterSer, SyncTimeRange{}, specInfo, token, entries), + -E_INVALID_QUERY_FORMAT); + + QuerySyncObject querySyncObj1(queryInvalidCombine); + vector buffer1(querySyncObj1.CalculateParcelLen(SOFTWARE_VERSION_CURRENT), 0); + Parcel writeParcel1(buffer1.data(), querySyncObj1.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + Parcel readParcel1(buffer1.data(), querySyncObj1.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + EXPECT_EQ(querySyncObj1.SerializeData(writeParcel1, SOFTWARE_VERSION_CURRENT), E_OK); + QuerySyncObject queryObjAfterSer1; + EXPECT_EQ(QuerySyncObject::DeSerializeData(readParcel1, queryObjAfterSer1), E_OK); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObjAfterSer1, SyncTimeRange{}, specInfo, token, entries), + -E_INVALID_QUERY_FORMAT); + + QuerySyncObject querySyncObj2(queryPrefixKeyLimit); + vector buffer2(querySyncObj2.CalculateParcelLen(SOFTWARE_VERSION_CURRENT), 0); + Parcel writeParcel2(buffer2.data(), querySyncObj2.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + Parcel readParcel2(buffer2.data(), querySyncObj2.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + EXPECT_EQ(querySyncObj2.SerializeData(writeParcel2, SOFTWARE_VERSION_CURRENT), E_OK); + QuerySyncObject queryObjAfterSer2; + EXPECT_EQ(QuerySyncObject::DeSerializeData(readParcel2, queryObjAfterSer2), E_OK); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObjAfterSer2, SyncTimeRange{}, specInfo, token, entries), + E_OK); + EXPECT_FALSE(entries.empty()); + ReleaseKvEntries(entries); +} + + +/** + * @tc.name: QueryParcel001 + * @tc.desc: Query object should has same attribute(Limit, OrderBy) after deserialized + * @tc.type: FUNC + * @tc.require: + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, QueryParcel001, TestSize.Level1) +{ + Query query = Query::Select().OrderBy("$.field_name1").Limit(10, 5); + + QuerySyncObject querySyncObj(query); + vector buffer(querySyncObj.CalculateParcelLen(SOFTWARE_VERSION_CURRENT), 0); + Parcel writeParcel(buffer.data(), querySyncObj.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + Parcel readParcel(buffer.data(), querySyncObj.CalculateParcelLen(SOFTWARE_VERSION_CURRENT)); + EXPECT_EQ(querySyncObj.SerializeData(writeParcel, SOFTWARE_VERSION_CURRENT), E_OK); + QuerySyncObject queryObjAfterSer; + EXPECT_EQ(QuerySyncObject::DeSerializeData(readParcel, queryObjAfterSer), E_OK); + EXPECT_EQ(queryObjAfterSer.HasLimit(), true); + int limit = 0; + int offset = 0; + queryObjAfterSer.GetLimitVal(limit, offset); + EXPECT_EQ(limit, 10); + EXPECT_EQ(offset, 5); + EXPECT_EQ(queryObjAfterSer.HasOrderBy(), true); +} + +/** + * @tc.name: MultiQueryGetSyncData001 + * @tc.desc: Mix multiple conditions for simultaneous query + * @tc.type: FUNC + * @tc.require: + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, MultiQueryGetSyncData001, TestSize.Level1) +{ + Query query = Query::Select(); + Query query1 = Query::Select().EqualTo("$.field_name1", true); + Query query2 = Query::Select().BeginGroup().GreaterThan("$.field_name3", 1).EndGroup(); + Query query3 = Query::Select().Like("field_name7", ""); + Query query4 = Query::Select().PrefixKey({}).OrderBy("$.field_name6"); + Query query5 = Query::Select().PrefixKey({}).IsNull("field_name10"); + + DataSizeSpecInfo specInfo = {4 * 1024 * 1024, DBConstant::MAX_HPMODE_PACK_ITEM_SIZE}; + std::vector entries; + ContinueToken token = nullptr; + + QueryObject queryObj(query); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj, SyncTimeRange{}, specInfo, token, entries), E_OK); + ReleaseKvEntries(entries); + + QueryObject queryObj1(query1); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj1, SyncTimeRange{}, specInfo, token, entries), E_OK); + EXPECT_EQ(entries.size(), 1UL); + ReleaseKvEntries(entries); + + QueryObject queryObj2(query2); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj2, SyncTimeRange{}, specInfo, token, entries), E_OK); + ReleaseKvEntries(entries); + + QueryObject queryObj3(query3); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj3, SyncTimeRange{}, specInfo, token, entries), E_OK); + ReleaseKvEntries(entries); + + QueryObject queryObj4(query4); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj4, SyncTimeRange{}, specInfo, token, entries), E_OK); + ReleaseKvEntries(entries); + + QueryObject queryObj5(query5); + EXPECT_EQ(g_schemaStore->GetSyncData(queryObj5, SyncTimeRange{}, specInfo, token, entries), E_OK); + ReleaseKvEntries(entries); +} + +/** + * @tc.name: QueryPredicateValidation001 + * @tc.desc: check query object is query only by key and has orderBy or not + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, QueryPredicateValidation001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create a query object with prefixKey only, check it's predicate + * @tc.expected: step1. check IsQueryOnlyByKey true; check HasOrderBy false + */ + Query query1 = Query::Select().PrefixKey({}); + QuerySyncObject querySync1(query1); + EXPECT_EQ(querySync1.IsQueryOnlyByKey(), true); + EXPECT_EQ(querySync1.HasOrderBy(), false); + + /** + * @tc.steps:step2. Create a query object with prefixKey and equalTo, check it's predicate + * @tc.expected: step2. check IsQueryOnlyByKey false; check HasOrderBy false + */ + Query query2 = Query::Select().PrefixKey({}).EqualTo("$.testField", 0); + QuerySyncObject querySync2(query2); + EXPECT_EQ(querySync2.IsQueryOnlyByKey(), false); + EXPECT_EQ(querySync2.HasOrderBy(), false); + + /** + * @tc.steps:step3. Create a query object with orderBy only, check it's predicate + * @tc.expected: step3. check IsQueryOnlyByKey false; check HasOrderBy true + */ + Query query3 = Query::Select().OrderBy("$.testField"); + QuerySyncObject querySync3(query3); + EXPECT_EQ(querySync3.IsQueryOnlyByKey(), false); + EXPECT_EQ(querySync3.HasOrderBy(), true); +} + +/** + * @tc.name: RelationalQuerySyncTest001 + * @tc.desc: Test querySyncObject serialize with table name is specified + * @tc.type: FUNC + * @tc.require: + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, RelationalQuerySyncTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create a query object with table name is specified + * @tc.expected: ok + */ + Query query1 = Query::Select("Relational_table").EqualTo("field1", "abc"); + QuerySyncObject obj1(query1); + + /** + * @tc.steps:step2. Serialize the object + * @tc.expected: ok + */ + uint32_t buffLen = obj1.CalculateParcelLen(SOFTWARE_VERSION_CURRENT); + vector buffer(buffLen, 0); + Parcel writeParcel(buffer.data(), buffLen); + EXPECT_EQ(obj1.SerializeData(writeParcel, SOFTWARE_VERSION_CURRENT), E_OK); + + /** + * @tc.steps:step3. DeSerialize the data + * @tc.expected: ok, And the queryId is same + */ + Parcel readParcel(buffer.data(), buffLen); + QuerySyncObject queryObj2; + EXPECT_EQ(QuerySyncObject::DeSerializeData(readParcel, queryObj2), E_OK); + EXPECT_EQ(obj1.GetIdentify(), queryObj2.GetIdentify()); +} + +/** + * @tc.name: RelationalQuerySyncTest002 + * @tc.desc: Test querySyncObject with different table name has different identity + * @tc.type: FUNC + * @tc.require: + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, RelationalQuerySyncTest002, TestSize.Level1) +{ + Query query1 = Query::Select("Relational_table1").EqualTo("field1", "abc"); + QuerySyncObject obj1(query1); + + Query query2 = Query::Select("Relational_table2").EqualTo("field1", "abc"); + QuerySyncObject obj2(query2); + + /** + * @tc.steps:step1. check object identity + * @tc.expected: identity should be different. + */ + EXPECT_NE(obj1.GetIdentify(), obj2.GetIdentify()); +} + +/** + * @tc.name: SerializeAndDeserializeForVer1 + * @tc.desc: Test querySyncObject serialization and deserialization. + * @tc.type: FUNC + * @tc.require: AR000GOHO7 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, SerializeAndDeserializeForVer1, TestSize.Level1) +{ + Query qeury1 = Query::Select("table1").EqualTo("field1", "abc").InKeys({KEY_1, KEY_2, KEY_3}); + QuerySyncObject obj1(qeury1); + + /** + * @tc.steps:step1. Serialize obj1. + * @tc.expected: Serialize successfully. + */ + auto len = obj1.CalculateParcelLen(SOFTWARE_VERSION_CURRENT); + std::vector buffer(len); + Parcel parcel1(buffer.data(), buffer.size()); + obj1.SerializeData(parcel1, SOFTWARE_VERSION_CURRENT); + ASSERT_EQ(parcel1.IsError(), false); + + /** + * @tc.steps:step2. Deserialize obj1. + * @tc.expected: Deserialize successfully. + */ + QuerySyncObject obj2; + Parcel parcel2(buffer.data(), buffer.size()); + ASSERT_EQ(parcel2.IsError(), false); + + /** + * @tc.steps:step3. check object identity + * @tc.expected: identity should be the same. + */ + EXPECT_NE(obj1.GetIdentify(), obj2.GetIdentify()); +} + +/** + * @tc.name: MultiInkeys1 + * @tc.desc: Test the rc when multiple inkeys exists. + * @tc.type: FUNC + * @tc.require: AR000GOHO7 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBStorageQuerySyncTest, MultiInkeys1, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create an invalid query, with multiple inkeys. + */ + Query query = Query::Select().InKeys({KEY_1, KEY_2}).InKeys({KEY_3}); + + /** + * @tc.steps:step2. Get data. + * @tc.expected: Return INVALID_QUERY_FORMAT. + */ + std::vector entries; + IOption option; + option.dataType = IOption::SYNC_DATA; + EXPECT_EQ(g_schemaConnect->GetEntries(option, query, entries), -E_INVALID_QUERY_FORMAT); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_register_conflict_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_register_conflict_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a7d5f6228c482ac3f14875b9c87e480890e68de7 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_register_conflict_test.cpp @@ -0,0 +1,836 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "distributeddb_data_generate_unit_test.h" +#include "kv_store_nb_conflict_data.h" +#include "kv_store_nb_delegate_impl.h" +#include "kvdb_conflict_entry.h" +#include "platform_specific.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "time_helper.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + const string STORE_ID = STORE_ID_SYNC; + std::string g_identifier; + const int CONFLICT_ALL = 15; + const int TIME_LAG = 100; + const int DATA_TIME_LAG = 1000; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + DBStatus g_kvDelegateStatus = INVALID_ARGS; + SQLiteSingleVerNaturalStore *g_store = nullptr; + + const Value DEFT_VALUE; + DistributedDB::SQLiteSingleVerNaturalStoreConnection *g_connection = nullptr; + const auto OLD_VALUE_TYPE = KvStoreNbConflictData::ValueType::OLD_VALUE; + const auto NEW_VALUE_TYPE = KvStoreNbConflictData::ValueType::NEW_VALUE; + std::list g_conflictDataList; + std::vector g_conflictDataConnect; + struct SingleVerConflictData { + KvStoreNbConflictType type = CONFLICT_NATIVE_ALL; + Key key; + Value oldValue; + Value newValue; + bool oldIsDeleted = false; + bool newIsDeleted = false; + bool oldIsNative = false; + bool newIsNative = false; + int getoldValueErrCode = 0; + int getNewValueErrCode = 0; + bool operator==(const SingleVerConflictData &comparedData) const + { + if (this->type == comparedData.type && + this->key == comparedData.key && + this->oldValue == comparedData.oldValue && + this->newValue == comparedData.newValue && + this->oldIsDeleted == comparedData.oldIsDeleted && + this->newIsDeleted == comparedData.newIsDeleted && + this->oldIsNative == comparedData.oldIsNative && + this->newIsNative == comparedData.newIsNative && + this->getoldValueErrCode == comparedData.getoldValueErrCode && + this->getNewValueErrCode == comparedData.getNewValueErrCode) { + return true; + } + LOGD("type = %d, ctype = %d", this->type, comparedData.type); + DBCommon::PrintHexVector(this->key, __LINE__, "key"); + DBCommon::PrintHexVector(comparedData.key, __LINE__, "ckey"); + DBCommon::PrintHexVector(this->oldValue, __LINE__, "value"); + DBCommon::PrintHexVector(comparedData.oldValue, __LINE__, "oldValue"); + DBCommon::PrintHexVector(this->newValue, __LINE__, "oldvalue"); + DBCommon::PrintHexVector(comparedData.newValue, __LINE__, "newValue"); + + LOGD("oldIsDeleted = %d, coldIsDeleted = %d", this->oldIsDeleted, comparedData.oldIsDeleted); + LOGD("newIsDeleted = %d, cnewIsDeleted = %d", this->newIsDeleted, comparedData.newIsDeleted); + LOGD("oldIsNative = %d, coldIsNative = %d", this->oldIsNative, comparedData.oldIsNative); + LOGD("newIsNative = %d, cnewIsNative = %d", this->newIsNative, comparedData.newIsNative); + LOGD("getoldValueErrCode = %d, cgetoldValueErrCode = %d", this->getoldValueErrCode, + comparedData.getoldValueErrCode); + LOGD("getNewValueErrCode = %d, cgetNewValueErrCode = %d", this->getNewValueErrCode, + comparedData.getNewValueErrCode); + + return false; + } + }; + std::vector g_conflictData; + + void NotifierConnectCallback(const KvDBCommitNotifyData &data) + { + int errCode; + g_conflictDataList = data.GetCommitConflicts(errCode); + for (const auto &element : g_conflictDataList) { + g_conflictDataConnect.push_back(element); + } + } + + void NotifierCallback(const KvStoreNbConflictData &data) + { + Key key; + Value oldValue; + Value newValue; + data.GetKey(key); + data.GetValue(OLD_VALUE_TYPE, oldValue); + LOGD("Get new value status:%d", data.GetValue(NEW_VALUE_TYPE, newValue)); + LOGD("Type:%d", data.GetType()); + DBCommon::PrintHexVector(oldValue, __LINE__); + DBCommon::PrintHexVector(newValue, __LINE__); + LOGD("Type:IsDeleted %d vs %d, IsNative %d vs %d", data.IsDeleted(OLD_VALUE_TYPE), + data.IsDeleted(NEW_VALUE_TYPE), data.IsNative(OLD_VALUE_TYPE), data.IsNative(NEW_VALUE_TYPE)); + g_conflictData.push_back({data.GetType(), key, oldValue, newValue, data.IsDeleted(OLD_VALUE_TYPE), + data.IsDeleted(NEW_VALUE_TYPE), data.IsNative(OLD_VALUE_TYPE), data.IsNative(NEW_VALUE_TYPE), + data.GetValue(OLD_VALUE_TYPE, oldValue), data.GetValue(NEW_VALUE_TYPE, newValue)}); + } + + // the type of g_kvDelegateCallback is function + auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); +} + +class DistributedDBStorageRegisterConflictTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageRegisterConflictTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + std::string origIdentifier = USER_ID + "-" + APP_ID + "-" + STORE_ID; + std::string identifier = DBCommon::TransferHashString(origIdentifier); + g_identifier = DBCommon::TransferStringToHex(identifier); + + string dir = g_testDir + "/" + g_identifier + "/" + DBConstant::SINGLE_SUB_DIR; + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } +} + +void DistributedDBStorageRegisterConflictTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/" + g_identifier + "/" + + DBConstant::SINGLE_SUB_DIR) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBStorageRegisterConflictTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /* + * Here, we create STORE_ID before test, + * and it will be closed in TearDown(). + */ + KvStoreNbDelegate::Option option = {true}; + g_mgr.GetKvStore(STORE_ID, option, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + + KvDBProperties property; + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, STORE_ID); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, g_identifier); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + g_store = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(g_store, nullptr); + ASSERT_EQ(g_store->Open(property), E_OK); + + int erroCode = E_OK; + g_connection = static_cast(g_store->GetDBConnection(erroCode)); + ASSERT_NE(g_connection, nullptr); + g_store->DecObjRef(g_store); + EXPECT_EQ(erroCode, E_OK); + + g_conflictData.clear(); + g_conflictDataConnect.clear(); +} + +void DistributedDBStorageRegisterConflictTest::TearDown(void) +{ + if (g_connection != nullptr) { + g_connection->Close(); + } + + g_store = nullptr; + + if (g_kvNbDelegatePtr != nullptr) { + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore(STORE_ID) == OK); + } +} + +static bool CheckNewConflictData(KvDBConflictEntry ¬ifyData, KvDBConflictEntry &expectNotifyData) +{ + if (notifyData.newData.value != expectNotifyData.newData.value) { + LOGD("New Data value ERROR! Actual vs Expected"); + DBCommon::PrintHexVector(notifyData.newData.value); + DBCommon::PrintHexVector(expectNotifyData.newData.value); + return false; + } + + if (notifyData.oldData.isDeleted != expectNotifyData.oldData.isDeleted) { + LOGD("Old Data IsDeleted ERROR! Actual %d vs Expected %d", notifyData.oldData.isDeleted, + expectNotifyData.oldData.isDeleted); + return false; + } + if (notifyData.oldData.isLocal != expectNotifyData.oldData.isLocal) { + LOGD("Old Data IsLocal ERROR! Actual %d vs Expected %d", + notifyData.oldData.isLocal, expectNotifyData.oldData.isLocal); + return false; + } + + if (notifyData.oldData.value != expectNotifyData.oldData.value) { + LOGD("Old Data value ERROR! Actualvs Expected"); + DBCommon::PrintHexVector(notifyData.oldData.value); + DBCommon::PrintHexVector(expectNotifyData.oldData.value); + return false; + } + + return true; +} + +static bool CheckOldConflictData(KvDBConflictEntry ¬ifyData, KvDBConflictEntry &expectNotifyData) +{ + if (notifyData.type != expectNotifyData.type) { + LOGD("Conflict Type ERROR! Actual %d vs Expected %d", notifyData.type, expectNotifyData.type); + return false; + } + + if (notifyData.key != expectNotifyData.key) { + LOGD("key not match"); + return false; + } + + if (notifyData.newData.isDeleted != expectNotifyData.newData.isDeleted) { + LOGD("New Data IsDeleted ERROR! Actual %d vs Expected %d", notifyData.newData.isDeleted, + expectNotifyData.newData.isDeleted); + return false; + } + + if (notifyData.newData.isLocal != expectNotifyData.newData.isLocal) { + LOGD("New Data IsLocal ERROR! Actual %d vs Expected %d", notifyData.newData.isLocal, + expectNotifyData.newData.isLocal); + return false; + } + + return true; +} + +static void SyncPutConflictData(int deltaTime) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + + Timestamp timeEnd = TimeHelper::GetSysCurrentTime(); + std::vector vect; + vect.push_back({KEY_1, VALUE_1, timeEnd, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + ASSERT_EQ(g_connection->SetConflictNotifier(CONFLICT_ALL, NotifierConnectCallback), E_OK); + EXPECT_EQ(g_conflictDataConnect.empty(), true); + + vect.clear(); + vect.push_back({KEY_1, VALUE_2, timeEnd + deltaTime, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceC"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + ASSERT_EQ(g_conflictDataConnect.empty(), false); + + KvDBConflictEntry expectNotifyData1 = {KvStoreNbConflictType::CONFLICT_FOREIGN_KEY_ONLY, KEY_1, + {VALUE_2, false, false}, {VALUE_1, false, true}}; + KvDBConflictEntry expectNotifyData2 = {KvStoreNbConflictType::CONFLICT_FOREIGN_KEY_ONLY, KEY_1, + {VALUE_1, false, true}, {VALUE_2, false, false}}; + if (deltaTime > 0) { + EXPECT_EQ(CheckOldConflictData(g_conflictDataConnect.front(), expectNotifyData2), true); + EXPECT_EQ(CheckNewConflictData(g_conflictDataConnect.front(), expectNotifyData2), true); + } else { + EXPECT_EQ(CheckOldConflictData(g_conflictDataConnect.front(), expectNotifyData1), true); + EXPECT_EQ(CheckNewConflictData(g_conflictDataConnect.front(), expectNotifyData1), true); + } +} + +static void SyncDeleteConflictData(const int deltaTime) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + + Timestamp time = TimeHelper::GetSysCurrentTime(); + + std::vector vect; + vect.push_back({KEY_1, VALUE_1, time, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + ASSERT_EQ(g_connection->SetConflictNotifier(CONFLICT_ALL, NotifierConnectCallback), E_OK); + EXPECT_EQ(g_conflictDataConnect.empty(), true); + + vect.clear(); + std::vector hashKey; + DistributedDBToolsUnitTest::CalcHash(KEY_1, hashKey); + vect.push_back({hashKey, DEFT_VALUE, time + deltaTime, 1, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceC"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); +} + +static void SyncPutFromDiffDevConflictData(const int deltaTime) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + + Timestamp time = TimeHelper::GetSysCurrentTime(); + + std::vector vect; + vect.push_back({KEY_1, VALUE_1, time, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + g_connection->SetConflictNotifier(CONFLICT_ALL, NotifierConnectCallback); + + vect.clear(); + vect.push_back({KEY_1, VALUE_2, time + deltaTime, 0, DBCommon::TransferHashString("deviceC")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceC"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); +} + +static void SyncDeleteFromDiffDevConflictData(const int deltaTime) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + + Timestamp time = TimeHelper::GetSysCurrentTime(); + + std::vector vect; + vect.push_back({KEY_1, VALUE_1, time, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + + ASSERT_EQ(g_connection->SetConflictNotifier(CONFLICT_ALL, NotifierConnectCallback), E_OK); + EXPECT_EQ(g_conflictDataConnect.empty(), true); + + vect.clear(); + std::vector hashKey; + DistributedDBToolsUnitTest::CalcHash(KEY_1, hashKey); + vect.push_back({hashKey, DEFT_VALUE, time + deltaTime, 1, DBCommon::TransferHashString("deviceC")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceC"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); +} + +/** + * @tc.name: ConflictNotificationTest001 + * @tc.desc: Put a non-conflict key and expect no conflict being triggered. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Setup conflict notifier. + * @tc.expected: step1. Expect setup success + */ + EXPECT_TRUE(g_kvNbDelegatePtr->SetConflictNotifier(CONFLICT_ALL, NotifierCallback) == OK); + /** + * @tc.steps:step2. Put a key into the database. + * @tc.expected: step2. Return no conflict. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + EXPECT_EQ(g_conflictData.empty(), true); +} + +/** + * @tc.name: ConflictNotificationTest002 + * @tc.desc: Put a native conflict key and expect native conflict being triggered. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Put a kv data into database with KEY_1, VALUE_1 and setup conflict notifier. + * @tc.expected: step1/2. setup success. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + EXPECT_TRUE(g_kvNbDelegatePtr->SetConflictNotifier(CONFLICT_ALL, NotifierCallback) == OK); + /** + * @tc.steps:step3. Put another kv data into database with the same key KEY_1. + * @tc.expected: step3. Expect to trigger a conflict. Return a SingleVerConflictData with { + * CONFLICT_NATIVE_ALL, KEY_1, VALUE_1, VALUE_2, false, false, true, true} + */ + EXPECT_EQ(g_kvNbDelegatePtr->Put(KEY_1, VALUE_2), E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + ASSERT_FALSE(g_conflictData.empty()); + SingleVerConflictData expectNotifyData = {KvStoreNbConflictType::CONFLICT_NATIVE_ALL, + KEY_1, VALUE_1, VALUE_2, false, false, true, true}; + EXPECT_EQ(g_conflictData.front() == expectNotifyData, true); +} + +/** + * @tc.name: ConflictNotificationTest003 + * @tc.desc: Put a data then delete it. Expect native conflict being triggered. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest003, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Put a kv data into database with KEY_1, VALUE_1 and setup conflict notifier. + * @tc.expected: step1/2. setup success. + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_1); + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + EXPECT_TRUE(g_kvNbDelegatePtr->SetConflictNotifier(CONFLICT_ALL, NotifierCallback) == OK); + /** + * @tc.steps:step3. Delete KEY_1. + * @tc.expected: step3. Expect Delete action triggers a conflict. Return a SingleVerConflictData with { + * KvStoreNbConflictType::CONFLICT_NATIVE_ALL, KEY_1, VALUE_1, DEFT_VALUE, false, true, true, true, OK, ERROR}; + */ + g_kvNbDelegatePtr->Delete(KEY_1); + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + EXPECT_EQ(g_conflictData.empty(), false); + SingleVerConflictData expectNotifyData = {KvStoreNbConflictType::CONFLICT_NATIVE_ALL, + KEY_1, VALUE_1, DEFT_VALUE, false, true, true, true, OK, DB_ERROR}; + EXPECT_EQ(g_conflictData.front() == expectNotifyData, true); +} + +/** + * @tc.name: ConflictNotificationTest004 + * @tc.desc: Sync a data then put a data with the same key. Expect native conflict being triggered. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest004, TestSize.Level1) +{ + Timestamp time = TimeHelper::GetSysCurrentTime(); + /** + * @tc.steps:step1/2. Sync a kv data into database with KEY_1, VALUE_1 and setup conflict notifier. + * @tc.expected: step1/2. setup conflict notifier success. + */ + std::vector vect; + vect.push_back({KEY_1, VALUE_1, time, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + EXPECT_TRUE(g_kvNbDelegatePtr->SetConflictNotifier(CONFLICT_ALL, NotifierCallback) == OK); + /** + * @tc.steps:step3. Put a kv data with the same key but different value KEY_1, VALUE_2. + * @tc.expected: step3. Expect Put action triggers a conflict, which is of type SingleVerConflictData, + * {KvStoreNbConflictType::CONFLICT_NATIVE_ALL, KEY_1, VALUE_1, VALUE_2, false, false, true, true, OK, OK}; + */ + g_kvNbDelegatePtr->Put(KEY_1, VALUE_2); + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + EXPECT_EQ(g_conflictData.empty(), false); + SingleVerConflictData expectNotifyData = {KvStoreNbConflictType::CONFLICT_NATIVE_ALL, + KEY_1, VALUE_1, VALUE_2, false, false, true, true, OK, OK}; + EXPECT_EQ(g_conflictData.front() == expectNotifyData, true); +} + +/** + * @tc.name: ConflictNotificationTest005 + * @tc.desc: Get a Sync data then delete it. Expect to see native conflict. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest005, TestSize.Level1) +{ + Timestamp time = TimeHelper::GetSysCurrentTime(); + /** + * @tc.steps:step1/2. Sync a kv data into database with KEY_1, VALUE_1 and setup conflict notifier. + * @tc.expected: step1/2. setup conflict notifier success. + */ + std::vector vect; + vect.push_back({KEY_1, VALUE_1, time, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + /** + * @tc.steps:step3. Delete the synchronized data. + * @tc.expected: step3. Expect Delete action triggers a conflict, which is of type SingleVerConflictData, + * {KvStoreNbConflictType::CONFLICT_NATIVE_ALL, KEY_1, VALUE_1, DEFT_VALUE, false, true, true, true, OK, ERROR}; + */ + EXPECT_TRUE(g_kvNbDelegatePtr->SetConflictNotifier(CONFLICT_ALL, NotifierCallback) == OK); + g_kvNbDelegatePtr->Delete(KEY_1); + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + SingleVerConflictData expectNotifyData = {KvStoreNbConflictType::CONFLICT_NATIVE_ALL, + KEY_1, VALUE_1, DEFT_VALUE, false, true, true, true, OK, DB_ERROR}; + EXPECT_EQ(g_conflictData.front() == expectNotifyData, true); +} + +/** + * @tc.name: ConflictNotificationTest006 + * @tc.desc: Get a sync data without local key that conflicts with it. Expect to see no conflict. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest006, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Sync a kv data into database with KEY_1, VALUE_1 and setup conflict notifier. + * @tc.expected: step1/2. setup conflict notifier success and no conflict being triggered. + */ + EXPECT_TRUE(g_kvNbDelegatePtr->SetConflictNotifier(CONFLICT_ALL, NotifierCallback) == OK); + Timestamp time = TimeHelper::GetSysCurrentTime(); + std::vector vect; + vect.push_back({KEY_1, VALUE_1, time, 1, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + EXPECT_EQ(g_conflictData.empty(), true); +} + +/** + * @tc.name: ConflictNotificationTest007 + * @tc.desc: Sync-sync data conflict. Expect to see foreign conflict and the winner has larger time tag. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest007, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Sync a kv data into database, and sync another with the same key and same origin, with a time + * lag DATA_TIME_LAG us. + * @tc.expected: step1/2. Expect to see conflict with Foreign key only conflict: + * {CONFLICT_FOREIGN_KEY_ONLY, KEY_1.key, {VALUE_1, false, true}, {VALUE_2, false, false}} + */ + SyncPutConflictData(DATA_TIME_LAG); +} + +/** + * @tc.name: ConflictNotificationTest008 + * @tc.desc: Sync-sync data conflict. Expect to see foreign conflict and the winner has larger time tag. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest008, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Sync a kv data into database, and sync another with the same key and same origin, with + * time advanced DATA_TIME_LAG us. + * @tc.expected: step1/2. Expect to see conflict: + * {CONFLICT_FOREIGN_KEY_ONLY, KEY_1.key, {VALUE_1, false, true}, {VALUE_2, false, false}} + */ + SyncPutConflictData(-DATA_TIME_LAG); +} + +/** + * @tc.name: ConflictNotificationTest009 + * @tc.desc: Sync a data to the device. Sync another data with the same key and the time tag +DATA_TIME_LAG us. + * Expect to see native conflict and the first data being deleted. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest009, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Sync a kv data into database, and sync another deleted data (null data) with the same key + * and same origin, with a time lag DATA_TIME_LAG us. + * @tc.expected: step1/2. Expect the deleted data triggers conflict: + * {CONFLICT_FOREIGN_KEY_ONLY, KEY_1.key, {VALUE_1, false, true}, {DEFT_VALUE.value, true, false}} + */ + SyncDeleteConflictData(DATA_TIME_LAG); + ASSERT_FALSE(g_conflictDataConnect.empty()); + KvDBConflictEntry expectNotifyData = {KvStoreNbConflictType::CONFLICT_FOREIGN_KEY_ONLY, KEY_1, + {VALUE_1, false, true}, {DEFT_VALUE, true, false}}; + EXPECT_EQ(CheckOldConflictData(g_conflictDataConnect.front(), expectNotifyData), true); + EXPECT_EQ(CheckNewConflictData(g_conflictDataConnect.front(), expectNotifyData), true); +} + +/** + * @tc.name: ConflictNotificationTest010 + * @tc.desc: Sync a data to the device. Sync another data with the same key and the time tag -DATA_TIME_LAG us. + * Expect to see native conflict and the second data being deleted. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest010, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Sync a kv data into database, and sync another deleted data (null data) with the same key + * and same origin, with a time lag DATA_TIME_LAG us. + * @tc.expected: step1/2. Expect the deleted data triggers conflict: + * {CONFLICT_FOREIGN_KEY_ONLY, KEY_1.key, {DEFT_VALUE.value, true, false}, {VALUE_1, false, true}} + */ + SyncDeleteConflictData(-DATA_TIME_LAG); + EXPECT_EQ(g_conflictDataConnect.empty(), false); + KvDBConflictEntry expectNotifyData = {KvStoreNbConflictType::CONFLICT_FOREIGN_KEY_ONLY, KEY_1, + {DEFT_VALUE, true, false}, {VALUE_1, false, true}}; + EXPECT_EQ(CheckOldConflictData(g_conflictDataConnect.front(), expectNotifyData), true); + EXPECT_EQ(CheckNewConflictData(g_conflictDataConnect.front(), expectNotifyData), true); +} + +/** + * @tc.name: ConflictNotificationTest011 + * @tc.desc: Sync-sync multi-origin conflict. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest011, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Sync a kv data into database, and sync another with the same key + * but origin differs from the previous, with a time lag DATA_TIME_LAG us. + * @tc.expected: step1/2. Expect the Put Action triggers conflict: + * {CONFLICT_FOREIGN_KEY_ORIG, KEY_1.key, {VALUE_1, false, true}, {VALUE_2, false, false}} + */ + SyncPutFromDiffDevConflictData(DATA_TIME_LAG); + EXPECT_EQ(g_conflictDataConnect.empty(), false); + KvDBConflictEntry expectNotifyData = {KvStoreNbConflictType::CONFLICT_FOREIGN_KEY_ORIG, KEY_1, + {VALUE_1, false, true}, {VALUE_2, false, false}}; + EXPECT_EQ(CheckOldConflictData(g_conflictDataConnect.front(), expectNotifyData), true); + EXPECT_EQ(CheckNewConflictData(g_conflictDataConnect.front(), expectNotifyData), true); +} + +/** + * @tc.name: ConflictNotificationTest012 + * @tc.desc: Sync-sync multi-origin conflict. + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest012, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Sync a kv data into database, and sync another with the same key + * but origin differs from the previous, with a time advanced DATA_TIME_LAG us. + * @tc.expected: step1/2. Expect the Put Action triggers conflict: + * {CONFLICT_FOREIGN_KEY_ORIG, KEY_1.key, {VALUE_1, false, true}, {VALUE_2, false, false}} + */ + SyncPutFromDiffDevConflictData(-DATA_TIME_LAG); + KvDBConflictEntry expectNotifyData = {KvStoreNbConflictType::CONFLICT_FOREIGN_KEY_ORIG, KEY_1, + {VALUE_2, false, false}, {VALUE_1, false, true}}; + EXPECT_EQ(CheckOldConflictData(g_conflictDataConnect.front(), expectNotifyData), true); + EXPECT_EQ(CheckNewConflictData(g_conflictDataConnect.front(), expectNotifyData), true); +} + +/** + * @tc.name: ConflictNotificationTest013 + * @tc.desc: Sync-sync multi-origin conflict with deleted data + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest013, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Sync a kv data into database, and sync another with the same key + * but origin differs from the previous, with a time lag DATA_TIME_LAG us. + * @tc.expected: step1/2. Expect the deleted data triggers conflict: + * {CONFLICT_FOREIGN_KEY_ORIG, KEY_1.key, {VALUE_1, false, true}, {VALUE_2, false, false}} + */ + SyncDeleteFromDiffDevConflictData(DATA_TIME_LAG); + EXPECT_EQ(g_conflictDataConnect.empty(), false); + KvDBConflictEntry expectNotifyData = {KvStoreNbConflictType::CONFLICT_FOREIGN_KEY_ORIG, KEY_1, + {VALUE_1, false, true}, {DEFT_VALUE, true, false}}; + EXPECT_EQ(CheckOldConflictData(g_conflictDataConnect.front(), expectNotifyData), true); + EXPECT_EQ(CheckNewConflictData(g_conflictDataConnect.front(), expectNotifyData), true); +} + +/** + * @tc.name: ConflictNotificationTest014 + * @tc.desc: Sync-sync multi-origin conflict with deleted data + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: maokeheng + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest014, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Sync a kv data into database, and sync another with the same key + * but origin differs from the previous, with a time advanced DATA_TIME_LAG us. + * @tc.expected: step1/2. Expect the deleted data triggers conflict: + * {CONFLICT_FOREIGN_KEY_ORIG, KEY_1.key, {VALUE_1, false, true}, {VALUE_2, false, false}} + */ + SyncDeleteFromDiffDevConflictData(-DATA_TIME_LAG); + KvDBConflictEntry expectNotifyData = {KvStoreNbConflictType::CONFLICT_FOREIGN_KEY_ORIG, KEY_1, + {DEFT_VALUE, true, false}, {VALUE_1, false, true}}; + EXPECT_EQ(CheckOldConflictData(g_conflictDataConnect.front(), expectNotifyData), true); + EXPECT_EQ(CheckNewConflictData(g_conflictDataConnect.front(), expectNotifyData), true); +} + +/** + * @tc.name: ConflictNotificationTest015 + * @tc.desc: put same record for conflict notification function + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: wumin + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest015, TestSize.Level1) +{ + Timestamp timeEnd = TimeHelper::GetSysCurrentTime(); + + std::vector vect; + vect.push_back({KEY_1, VALUE_1, timeEnd, 0, "deviceB", 0, "deviceB"}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + ASSERT_EQ(g_connection->SetConflictNotifier(CONFLICT_ALL, NotifierConnectCallback), E_OK); + EXPECT_EQ(g_conflictDataConnect.empty(), true); + + vect.clear(); + vect.push_back({KEY_1, VALUE_1, timeEnd + DATA_TIME_LAG, 0, "deviceB", 0, "deviceB"}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + ASSERT_EQ(g_conflictDataConnect.empty(), true); +} + +/** + * @tc.name: ConflictNotificationTest016 + * @tc.desc: put record for conflict notification function + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: wumin + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest016, TestSize.Level1) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + Timestamp timeEnd = TimeHelper::GetSysCurrentTime(); + + std::vector vect; + vect.push_back({KEY_1, VALUE_1, timeEnd, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + ASSERT_EQ(g_connection->SetConflictNotifier(CONFLICT_ALL, NotifierConnectCallback), E_OK); + EXPECT_EQ(g_conflictDataConnect.empty(), true); + + vect.clear(); + vect.push_back({KEY_1, VALUE_2, timeEnd, 0, DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + + ASSERT_EQ(g_conflictDataConnect.empty(), false); + KvDBConflictEntry expectNotifyData = {KvStoreNbConflictType::CONFLICT_FOREIGN_KEY_ONLY, KEY_1, + {VALUE_1, false, true}, {VALUE_2, false, false}}; + + EXPECT_EQ(CheckOldConflictData(g_conflictDataConnect.front(), expectNotifyData), true); + EXPECT_EQ(CheckNewConflictData(g_conflictDataConnect.front(), expectNotifyData), true); +} + +namespace { + void GetNewConflictStore() + { + if (g_connection != nullptr) { + g_connection->Close(); + } + if (g_kvNbDelegatePtr != nullptr) { + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + EXPECT_TRUE(g_mgr.DeleteKvStore(STORE_ID) == OK); + } + KvDBProperties property; + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, STORE_ID); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, g_identifier); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + property.SetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, DENY_OTHER_DEV_AMEND_CUR_DEV_DATA); + g_store = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(g_store, nullptr); + ASSERT_EQ(g_store->Open(property), E_OK); + + int erroCode = E_OK; + g_connection = static_cast(g_store->GetDBConnection(erroCode)); + ASSERT_NE(g_connection, nullptr); + g_store->DecObjRef(g_store); + EXPECT_EQ(erroCode, E_OK); + } +} + +/** + * @tc.name: ConflictNotificationTest017 + * @tc.desc: put record for conflict notification function + * @tc.type: FUNC + * @tc.require: AR000CQS3U + * @tc.author: wumin + */ +HWTEST_F(DistributedDBStorageRegisterConflictTest, ConflictNotificationTest017, TestSize.Level1) +{ + GetNewConflictStore(); + IOption option; + option.dataType = IOption::SYNC_DATA; + std::vector vect; + g_connection->Put(option, KEY_1, VALUE_1); + ASSERT_EQ(g_connection->SetConflictNotifier(CONFLICT_ALL, NotifierConnectCallback), E_OK); + + Timestamp timeEnd = TimeHelper::GetSysCurrentTime(); + static const uint32_t addTimestamp = 10000; + vect.push_back({KEY_1, VALUE_2, timeEnd + addTimestamp, 0, "", timeEnd + addTimestamp, + DBCommon::TransferHashString("deviceB")}); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(g_store, vect, "deviceB"), E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(TIME_LAG)); + EXPECT_EQ(g_conflictDataConnect.empty(), true); + Value readValue; + EXPECT_EQ(g_connection->Get(option, KEY_1, readValue), E_OK); + EXPECT_EQ(VALUE_1, readValue); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_register_observer_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_register_observer_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4f8bdeaba0be3a28d487a11bf79ce1c12e4d4d9 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_register_observer_test.cpp @@ -0,0 +1,828 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "default_factory.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "ikvdb_factory.h" +#include "log_print.h" +#include "sqlite_single_ver_natural_store.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + + SQLiteSingleVerNaturalStore *g_singleVerNaturaStore = nullptr; + IKvDBConnection *g_singleVerNaturaStoreConnection = nullptr; + bool g_createFactory = false; + const int OBSERVER_SLEEP_TIME = 80; + + Key g_emptyKey; + string g_keyStr1 = "key_1"; + string g_keyStr2 = "key_2"; + string g_keyStr3 = "key_3"; + string g_keyStr4 = "key_4"; + string g_keyStr5 = "key_5"; + string g_keyStr6 = "key_6"; + + string g_valueStr1 = "value_1"; + string g_valueStr2 = "value_2"; + string g_valueStr3 = "value_3"; + string g_valueStr4 = "value_4"; + string g_valueStr5 = "value_5"; + string g_valueStr6 = "value_6"; + string g_oldValueStr3 = "old_value_3"; + string g_oldValueStr4 = "old_value_4"; + + list g_emptyEntries; + Entry g_entry0; + Entry g_entry1; + Entry g_entry2; + Entry g_entry3; + Entry g_entry4; + Entry g_entry5; + Entry g_entry6; + Entry g_oldEntry3; + Entry g_oldEntry4; + + bool g_testFuncCalled = false; + list g_insertedEntries; + list g_updatedEntries; + list g_deletedEntries; + + Entry TransferStrToKyEntry(const string &key, const string &value) + { + Entry entry; + entry.key.resize(key.size()); + entry.key.assign(key.begin(), key.end()); + entry.value.resize(value.size()); + entry.value.assign(value.begin(), value.end()); + return entry; + } + + void TestFunc(const KvDBCommitNotifyData &data) + { + g_testFuncCalled = true; + int errCode; + g_insertedEntries = data.GetInsertedEntries(errCode); + ASSERT_EQ(errCode, E_OK); + g_updatedEntries = data.GetUpdatedEntries(errCode); + ASSERT_EQ(errCode, E_OK); + g_deletedEntries = data.GetDeletedEntries(errCode); + ASSERT_EQ(errCode, E_OK); + LOGI("Insert:%zu, update:%zu, delete:%zu", g_insertedEntries.size(), g_updatedEntries.size(), + g_deletedEntries.size()); + return; + } + + bool CompairEntryList(const list &entryList1, const list &entryList2) + { + bool result = true; + EXPECT_EQ(entryList1.size(), entryList2.size()); + if (entryList1.size() != entryList2.size()) { + return false; + } + for (const auto &entry1 : entryList1) { + result = false; + for (const auto &entry2 : entryList2) { + if (entry1.key != entry2.key) { + continue; + } + if (entry1.value == entry2.value) { + result = true; + break; + } + cout << "entry1.key: "; + for (const auto &character : entry1.key) { + cout << character; + } + cout << endl; + cout << "entry2.key: "; + for (const auto &character : entry2.key) { + cout << character; + } + cout << endl; + cout << "entry1.value: "; + for (const auto &character : entry1.value) { + cout << character; + } + cout << endl; + cout << "entry2.value: "; + for (const auto &character : entry2.value) { + cout << character; + } + cout << endl; + break; + } + } + return result; + } + + void TestAndClearCallbackResult(bool isCallbackCalled, const list &expectedInsertedEntries, + const list &expectedUpdatedEntries, const list &expectedDeletedEntries) + { + EXPECT_EQ(g_testFuncCalled, isCallbackCalled); + if (g_testFuncCalled) { + EXPECT_EQ(CompairEntryList(g_insertedEntries, expectedInsertedEntries), true); + EXPECT_EQ(CompairEntryList(g_updatedEntries, expectedUpdatedEntries), true); + EXPECT_EQ(CompairEntryList(g_deletedEntries, expectedDeletedEntries), true); + } + // clear result + g_testFuncCalled = false; + g_insertedEntries.resize(0); + g_updatedEntries.resize(0); + g_deletedEntries.resize(0); + } + + void PreDataforOperation(const Entry &entry, bool isLocalPutRegisted, bool isPutRegisted, list &entries) + { + IOption opt; + entries.push_back(entry); + // test local insert + opt.dataType = IOption::LOCAL_DATA; + g_singleVerNaturaStoreConnection->Put(opt, entry.key, entry.value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + TestAndClearCallbackResult(isLocalPutRegisted, entries, g_emptyEntries, g_emptyEntries); + // test local update + g_singleVerNaturaStoreConnection->Put(opt, entry.key, entry.value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + TestAndClearCallbackResult(isLocalPutRegisted, g_emptyEntries, entries, g_emptyEntries); + // test local delete + g_singleVerNaturaStoreConnection->Delete(opt, entry.key); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + TestAndClearCallbackResult(isLocalPutRegisted, g_emptyEntries, g_emptyEntries, entries); + + // test insert + opt.dataType = IOption::SYNC_DATA; + g_singleVerNaturaStoreConnection->Put(opt, entry.key, entry.value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + TestAndClearCallbackResult(isPutRegisted, entries, g_emptyEntries, g_emptyEntries); + + // test update + g_singleVerNaturaStoreConnection->Put(opt, entry.key, entry.value); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + TestAndClearCallbackResult(isPutRegisted, g_emptyEntries, entries, g_emptyEntries); + // test delete + g_singleVerNaturaStoreConnection->Delete(opt, entry.key); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + TestAndClearCallbackResult(isPutRegisted, g_emptyEntries, g_emptyEntries, entries); + } + + void TestForOperation(const Entry &entry, bool isLocalPutRegisted, bool isPutRegisted, bool isSyncRegisted) + { + list entries; + entries.push_back(entry); + + // test sync insert + Timestamp time; + g_singleVerNaturaStore->GetMaxTimestamp(time); + + DataItem dataItem; + dataItem.key = entry.key; + dataItem.value = entry.value; + dataItem.timestamp = time + 1; + dataItem.writeTimestamp = dataItem.timestamp; + dataItem.flag = 0; + vector insertDataItems; + insertDataItems.push_back(dataItem); + int result = DistributedDBToolsUnitTest::PutSyncDataTest(g_singleVerNaturaStore, insertDataItems, "deviceB"); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + ASSERT_EQ(result, E_OK); + TestAndClearCallbackResult(isSyncRegisted, entries, g_emptyEntries, g_emptyEntries); + // test sync update + vector updateDataItems; + dataItem.timestamp++; + dataItem.writeTimestamp = dataItem.timestamp; + updateDataItems.push_back(dataItem); + result = DistributedDBToolsUnitTest::PutSyncDataTest(g_singleVerNaturaStore, updateDataItems, "deviceB"); + + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + ASSERT_EQ(result, E_OK); + TestAndClearCallbackResult(isSyncRegisted, g_emptyEntries, entries, g_emptyEntries); + // test sync delete + vector deleteDataItems; + DataItem dataItem1 = dataItem; + dataItem1.timestamp++; + dataItem1.writeTimestamp = dataItem1.timestamp; + dataItem1.flag = 1; + DistributedDBToolsUnitTest::CalcHash(dataItem.key, dataItem1.key); + deleteDataItems.push_back(dataItem1); + result = DistributedDBToolsUnitTest::PutSyncDataTest(g_singleVerNaturaStore, deleteDataItems, "deviceB"); + + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + ASSERT_EQ(result, E_OK); + TestAndClearCallbackResult(isSyncRegisted, g_emptyEntries, g_emptyEntries, entries); + } +} + +class DistributedDBStorageRegisterObserverTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageRegisterObserverTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + if (IKvDBFactory::GetCurrent() == nullptr) { + IKvDBFactory *factory = new (std::nothrow) DefaultFactory(); + ASSERT_NE(factory, nullptr); + if (factory == nullptr) { + LOGE("failed to new DefaultFactory!"); + return; + } + IKvDBFactory::Register(factory); + g_createFactory = true; + } + // prepare test entries + g_entry1 = TransferStrToKyEntry(g_keyStr1, g_valueStr1); + g_entry2 = TransferStrToKyEntry(g_keyStr2, g_valueStr2); + g_entry3 = TransferStrToKyEntry(g_keyStr3, g_valueStr3); + g_entry4 = TransferStrToKyEntry(g_keyStr4, g_valueStr4); + g_entry5 = TransferStrToKyEntry(g_keyStr5, g_valueStr5); + g_entry6 = TransferStrToKyEntry(g_keyStr6, g_valueStr6); + g_oldEntry3 = TransferStrToKyEntry(g_keyStr3, g_oldValueStr3); + g_oldEntry4 = TransferStrToKyEntry(g_keyStr4, g_oldValueStr4); +} + +void DistributedDBStorageRegisterObserverTest::TearDownTestCase(void) +{ + if (g_createFactory) { + if (IKvDBFactory::GetCurrent() != nullptr) { + delete IKvDBFactory::GetCurrent(); + IKvDBFactory::Register(nullptr); + } + } + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBStorageRegisterObserverTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + ASSERT_NE(factory, nullptr); + if (factory == nullptr) { + LOGE("failed to get DefaultFactory!"); + return; + } + + g_singleVerNaturaStore = new (std::nothrow) SQLiteSingleVerNaturalStore(); + ASSERT_NE(g_singleVerNaturaStore, nullptr); + if (g_singleVerNaturaStore == nullptr) { + return; + } + + KvDBProperties property; + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, "TestGeneralNB"); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, "TestGeneralNB"); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + int errCode = g_singleVerNaturaStore->Open(property); + ASSERT_EQ(errCode, E_OK); + if (errCode != E_OK) { + g_singleVerNaturaStore = nullptr; + return; + } + + g_singleVerNaturaStoreConnection = g_singleVerNaturaStore->GetDBConnection(errCode); + ASSERT_EQ(errCode, E_OK); + ASSERT_NE(g_singleVerNaturaStoreConnection, nullptr); +} + +void DistributedDBStorageRegisterObserverTest::TearDown(void) +{ + if (g_singleVerNaturaStoreConnection != nullptr) { + g_singleVerNaturaStoreConnection->Close(); + } + std::string identifierName; + g_singleVerNaturaStore->DecObjRef(g_singleVerNaturaStore); + identifierName = DBCommon::TransferStringToHex("TestGeneralNB"); + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/" + identifierName + "/" + DBConstant::SINGLE_SUB_DIR); +} + +/** + * @tc.name: RegisterObserver001 + * @tc.desc: Register a NULL pointer as an observer + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver001, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Register a null pointer to subscribe to the database. + * Check whether the registration is successful. + * @tc.expected: step1/2. Returns INVALID_ARGS. + */ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_PUT_EVENT), g_entry1.key, nullptr, result); + EXPECT_EQ(result, -E_INVALID_ARGS); + EXPECT_EQ(handle, nullptr); + + /** + * @tc.steps: step3/4. UnRegister a null pointer to subscribe to the database. + * Check whether the unregistration is successful. + * @tc.expected: step3/4. Returns INVALID_ARGS. + */ + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(nullptr); + EXPECT_EQ(result, -E_INVALID_ARGS); + return; +} + +/** + * @tc.name: RegisterObserver002 + * @tc.desc: Register an observer for the local database change of a specified key + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver002, TestSize.Level1) +{ + int result; + /** + * @tc.steps: step1/2. Register a null pointer to subscribe to the database. + * Check whether the registration is successful. + * @tc.expected: step1/2. Returns INVALID_ARGS. + */ + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT), g_entry1.key, TestFunc, result); + EXPECT_EQ(result, E_OK); + EXPECT_NE(handle, nullptr); + + /** + * @tc.steps: step3/4/5/6. Register an observer for the local database change of a specified key + */ + TestForOperation(g_entry1, true, false, false); + TestForOperation(g_entry2, false, false, false); + + /** + * @tc.steps: step7/8. UnRegister the subscribe to the database. + * Check whether the unregistration is successful. + * @tc.expected: step7/8. Returns E_OK. + */ + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + + /** + * @tc.steps: step9. Repeat step3/5 + * @tc.expected: step9. No callback. + */ + TestForOperation(g_entry1, false, false, false); + TestForOperation(g_entry2, false, false, false); + return; +} + +/** + * @tc.name: RegisterObserver003 + * @tc.desc: Register an observer for the local sync database change of a specified key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver003, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Register a null pointer to subscribe to the database. + * Check whether the registration is successful. + * @tc.expected: step1/2. Returns INVALID_ARGS. + */ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_PUT_EVENT), g_entry1.key, TestFunc, result); + EXPECT_EQ(result, E_OK); + EXPECT_NE(handle, nullptr); + + /** + * @tc.steps: step3/4/5/6. Register an observer for the local sync database change of a specified key. + */ + TestForOperation(g_entry1, false, true, false); + TestForOperation(g_entry2, false, false, false); + + /** + * @tc.steps: step7/8. UnRegister the subscribe to the database. + * Check whether the unregistration is successful. + * @tc.expected: step7/8. Returns E_OK. + */ + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + + /** + * @tc.steps: step9. Repeat step3/5 + * @tc.expected: step9. No callback. + */ + TestForOperation(g_entry1, false, false, false); + TestForOperation(g_entry2, false, false, false); + return; +} + +/** + * @tc.name: RegisterObserver004 + * @tc.desc: Register an observer for the remote sync database change of a specified key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver004, TestSize.Level1) +{ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_SYNC_EVENT), g_entry1.key, TestFunc, result); + EXPECT_EQ(result, E_OK); + EXPECT_NE(handle, nullptr); + TestForOperation(g_entry1, false, false, true); + list entries1; + PreDataforOperation(g_entry1, false, false, entries1); + TestForOperation(g_entry2, false, false, false); + list entries2; + PreDataforOperation(g_entry2, false, false, entries2); + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + TestForOperation(g_entry1, false, false, false); + TestForOperation(g_entry2, false, false, false); + return; +} + +/** + * @tc.name: RegisterObserver005 + * @tc.desc: Register an observer for the sync database change of a specified key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver005, TestSize.Level1) +{ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_PUT_EVENT) | + static_cast(SQLITE_GENERAL_NS_SYNC_EVENT), g_entry1.key, TestFunc, result); + EXPECT_EQ(result, E_OK); + EXPECT_NE(handle, nullptr); + TestForOperation(g_entry1, false, true, true); + TestForOperation(g_entry2, false, false, false); + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + TestForOperation(g_entry1, false, false, false); + TestForOperation(g_entry2, false, false, false); + return; +} + +/** + * @tc.name: RegisterObserver006 + * @tc.desc: Register an observer for the local database change of any key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver006, TestSize.Level1) +{ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT), g_emptyKey, TestFunc, result); + EXPECT_EQ(result, E_OK); + EXPECT_NE(handle, nullptr); + TestForOperation(g_entry1, true, false, false); + TestForOperation(g_entry2, true, false, false); + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + TestForOperation(g_entry1, false, false, false); + TestForOperation(g_entry2, false, false, false); + return; +} + +/** + * @tc.name: RegisterObserver007 + * @tc.desc: Register an observer for the local sync database change of any key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver007, TestSize.Level1) +{ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_PUT_EVENT), g_emptyKey, TestFunc, result); + EXPECT_EQ(result, E_OK); + EXPECT_NE(handle, nullptr); + TestForOperation(g_entry1, false, true, false); + TestForOperation(g_entry2, false, true, false); + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + TestForOperation(g_entry1, false, false, false); + TestForOperation(g_entry2, false, false, false); + return; +} + +/** + * @tc.name: RegisterObserver008 + * @tc.desc: Register an observer for the remote sync database change of any key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver008, TestSize.Level1) +{ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_SYNC_EVENT), g_emptyKey, TestFunc, result); + EXPECT_EQ(result, E_OK); + EXPECT_NE(handle, nullptr); + TestForOperation(g_entry1, false, false, true); + TestForOperation(g_entry2, false, false, true); + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + TestForOperation(g_entry1, false, false, false); + TestForOperation(g_entry2, false, false, false); + return; +} + +/** + * @tc.name: RegisterObserver009 + * @tc.desc: Register an observer for the sync database change of any key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver009, TestSize.Level1) +{ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_PUT_EVENT) | + static_cast(SQLITE_GENERAL_NS_SYNC_EVENT), g_emptyKey, TestFunc, result); + EXPECT_EQ(result, E_OK); + EXPECT_NE(handle, nullptr); + TestForOperation(g_entry1, false, true, true); + TestForOperation(g_entry2, false, true, true); + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + TestForOperation(g_entry1, false, false, false); + TestForOperation(g_entry2, false, false, false); + return; +} + +/** + * @tc.name: RegisterObserver010 + * @tc.desc: Register an observer for the local sync database change and the local database change of a specified key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver010, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Register an observer for the local sync database change + * and the local database change of a specified key. Check register result. + * @tc.expected: step1/2. Returns E_NOT_SUPPORT. + */ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_PUT_EVENT) | + static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT), g_entry1.key, TestFunc, result); + EXPECT_EQ(result, -E_NOT_SUPPORT); + EXPECT_EQ(handle, nullptr); + return; +} + +/** + * @tc.name: RegisterObserver011 + * @tc.desc: Register an observer for the remote sync database change and the local database change of a specified key + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver011, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Register an observer for the remote sync database change + * and the local database change of a specified key. Check register result. + * @tc.expected: step1/2. Returns E_NOT_SUPPORT. + */ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_SYNC_EVENT) | + static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT), g_entry1.key, TestFunc, result); + EXPECT_EQ(result, -E_NOT_SUPPORT); + EXPECT_EQ(handle, nullptr); + return; +} + +/** + * @tc.name: RegisterObserver012 + * @tc.desc: Register an observer for the local sync database change and the local database change of any key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver012, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Register an observer for the local sync database change + * and the local database change of any key. Check register result. + * @tc.expected: step1/2. Returns E_NOT_SUPPORT. + */ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_PUT_EVENT) | + static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT), g_emptyKey, TestFunc, result); + EXPECT_EQ(result, -E_NOT_SUPPORT); + EXPECT_EQ(handle, nullptr); + return; +} + +/** + * @tc.name: RegisterObserver013 + * @tc.desc: Register an observer for the remote sync database change and the local database change of any key. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver013, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Register an observer for the remote sync database change + * and the local database change of any key. Check register result. + * @tc.expected: step1/2. Returns E_NOT_SUPPORT. + */ + int result; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_SYNC_EVENT) | + static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT), g_emptyKey, TestFunc, result); + EXPECT_EQ(result, -E_NOT_SUPPORT); + EXPECT_EQ(handle, nullptr); + return; +} + +static void PreSyncDataForRegisterObserver014(Timestamp time, vector &dataItems) +{ + // sync data + DataItem dataItem = {g_entry1.key, g_entry1.value, .timestamp = ++time, .flag = 1}; + dataItem.writeTimestamp = dataItem.timestamp; + DistributedDBToolsUnitTest::CalcHash(g_entry1.key, dataItem.key); + dataItems.push_back(dataItem); + + DistributedDBToolsUnitTest::CalcHash(g_entry2.key, dataItem.key); + dataItem.value = g_entry2.value; + dataItems.push_back(dataItem); + + dataItem.key = g_entry3.key; + dataItem.value = g_entry3.value; + dataItem.flag = 0; + dataItems.push_back(dataItem); + + dataItem.key = g_entry4.key; + dataItem.value = g_entry4.value; + dataItems.push_back(dataItem); + + dataItem.key = g_entry5.key; + dataItem.value = g_entry5.value; + dataItems.push_back(dataItem); + + dataItem.key = g_entry6.key; + dataItem.value = g_entry6.value; + dataItems.push_back(dataItem); +} + +/** + * @tc.name: RegisterObserver014 + * @tc.desc: Sync multiple records to the sync database + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver014, TestSize.Level1) +{ + /** + * @tc.steps: step1. Write the per data records to the synchronization database by Put. + */ + IOption opt = {.dataType = IOption::SYNC_DATA}; + g_singleVerNaturaStoreConnection->Put(opt, g_entry1.key, g_entry1.value); + g_singleVerNaturaStoreConnection->Put(opt, g_entry2.key, g_entry2.value); + g_singleVerNaturaStoreConnection->Put(opt, g_oldEntry3.key, g_oldEntry3.value); + g_singleVerNaturaStoreConnection->Put(opt, g_oldEntry4.key, g_oldEntry4.value); + // get max time + Timestamp time; + g_singleVerNaturaStore->GetMaxTimestamp(time); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + /** + * @tc.steps: step2. Register the observer to the sync database + * from the remote end without specifying the key. + */ + int result = E_OK; + KvDBObserverHandle* handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_SYNC_EVENT), g_emptyKey, TestFunc, result); + EXPECT_EQ(result, E_OK); + EXPECT_NE(handle, nullptr); + + // sync data + vector dataItems; + PreSyncDataForRegisterObserver014(time, dataItems); + + /** + * @tc.steps: step3. A batch write operation by PutSyncData. + * The key1 and key2 records are deleted, and the key3 and key4 records are recorded. + */ + result = DistributedDBToolsUnitTest::PutSyncDataTest(g_singleVerNaturaStore, dataItems, "deviceB"); + + ASSERT_EQ(result, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME)); + + // test result + list deletedEntries; + deletedEntries.push_back(g_entry1); + deletedEntries.push_back(g_entry2); + list updatedEntries; + updatedEntries.push_back(g_entry3); + updatedEntries.push_back(g_entry4); + list insertedEntries; + insertedEntries.push_back(g_entry5); + insertedEntries.push_back(g_entry6); + + /** + * @tc.steps: step4. Callback is triggered, the Put and Delete data is obtained from the observer. + * @tc.expected: step4. The data is consistent with the data to be written. + */ + TestAndClearCallbackResult(true, insertedEntries, updatedEntries, deletedEntries); + + /** + * @tc.steps: step3. unregister observer + */ + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + return; +} +/** + * @tc.name: RegisterObserver015 + * @tc.desc: Sync multiple records to the sync database, and remove them. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: liujialei + */ +HWTEST_F(DistributedDBStorageRegisterObserverTest, RegisterObserver015, TestSize.Level1) +{ + /** + * @tc.steps: step1. Generate the random entry. + */ + vector dataItems; + static const unsigned long number = 2; // 2 entries + for (unsigned long i = 0; i < number; i++) { + DataItem item; + DistributedDBToolsUnitTest::GetRandomKeyValue(item.key, DBConstant::MAX_KEY_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(item.value, DBConstant::MAX_VALUE_SIZE); + dataItems.push_back(std::move(item)); + } + /** + * @tc.steps: step2. Put the entries through the syncer interface. + */ + int result = DistributedDBToolsUnitTest::PutSyncDataTest(g_singleVerNaturaStore, dataItems, "deviceB"); + dataItems.clear(); + dataItems.shrink_to_fit(); + ASSERT_EQ(result, E_OK); + /** + * @tc.steps: step3. Register the observer. + */ + KvDBObserverHandle *handle = g_singleVerNaturaStoreConnection->RegisterObserver( + static_cast(SQLITE_GENERAL_NS_SYNC_EVENT), g_emptyKey, TestFunc, result); + EXPECT_EQ(result, E_OK); + ASSERT_NE(handle, nullptr); + /** + * @tc.steps: step4. Remove the data from "deviceB". + * @tc.expected: step4. Return E_OK and the observer data has delete entries. + */ + g_deletedEntries.clear(); + result = g_singleVerNaturaStore->RemoveDeviceData("deviceB", true); + ASSERT_EQ(result, E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(OBSERVER_SLEEP_TIME * number)); + ASSERT_NE(g_deletedEntries.empty(), true); + /** + * @tc.steps: step5. unregister observer + */ + result = g_singleVerNaturaStoreConnection->UnRegisterObserver(handle); + EXPECT_EQ(result, E_OK); + return; +} diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_resultset_and_json_optimize.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_resultset_and_json_optimize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ee0ecbba178bb7dc948b6c16c392df51a9cd7e9 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_resultset_and_json_optimize.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OMIT_JSON +#include + +#include "db_common.h" +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "kvdb_manager.h" +#include "log_print.h" +#include "platform_specific.h" +#include "res_finalizer.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "sqlite_single_ver_result_set.h" +#include "sqlite_utils.h" +#include "store_types.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + const int INSERT_NUMBER = 10; + const Key EMPTY_KEY; + const SQLiteSingleVerResultSet::Option OPTION = {ResultSetCacheMode::CACHE_ENTRY_ID_ONLY, 1}; + + string g_testDir; + string g_identifier; + SQLiteSingleVerNaturalStore *g_store = nullptr; + SQLiteSingleVerNaturalStoreConnection *g_connection = nullptr; + KvDBProperties g_Property; + const string STORE_ID = STORE_ID_SYNC; +} +class DistributedDBStorageResultAndJsonOptimizeTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageResultAndJsonOptimizeTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + std::string origIdentifier = USER_ID + "-" + APP_ID + "-" + STORE_ID; + std::string identifier = DBCommon::TransferHashString(origIdentifier); + g_identifier = DBCommon::TransferStringToHex(identifier); + std::string dir = g_testDir + g_identifier + "/" + DBConstant::SINGLE_SUB_DIR; + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + g_Property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + g_Property.SetStringProp(KvDBProperties::STORE_ID, STORE_ID); + g_Property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, g_identifier); + g_Property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); +} + +void DistributedDBStorageResultAndJsonOptimizeTest::TearDownTestCase(void) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +void DistributedDBStorageResultAndJsonOptimizeTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: 1. Create a SQLiteSingleVerNaturalStore. + * 2. Set the ResultSet cache mode to CACHE_ENTRY_ID_ONLY. + * 3. Put 10 records. + */ + g_store = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(g_store, nullptr); + ASSERT_EQ(g_store->Open(g_Property), E_OK); + + int errCode = E_OK; + g_connection = static_cast(g_store->GetDBConnection(errCode)); + ASSERT_NE(g_connection, nullptr); + g_store->DecObjRef(g_store); + EXPECT_EQ(errCode, E_OK); + + IOption option; + option.dataType = IOption::SYNC_DATA; + g_connection->Clear(option); + Key insertKey; + ASSERT_EQ(g_connection->StartTransaction(), E_OK); + for (int i = 1; i < INSERT_NUMBER + 1; i++) { + insertKey.clear(); + insertKey.push_back(i); + ASSERT_EQ(g_connection->Put(option, insertKey, VALUE_1), OK); + } + ASSERT_EQ(g_connection->Commit(), E_OK); +} + +void DistributedDBStorageResultAndJsonOptimizeTest::TearDown(void) +{ + /** + * @tc.teardown: Release the SQLiteSingleVerNaturalStore. + */ + if (g_connection != nullptr) { + g_connection->Close(); + g_connection = nullptr; + } + + g_store = nullptr; + KvDBManager::RemoveDatabase(g_Property); +} + +/** + * @tc.name: ResultSetOpen001 + * @tc.desc: Test the SQLiteSingleVerResultSet Open function + * @tc.type: FUNC + * @tc.require: AR000F3OP0 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBStorageResultAndJsonOptimizeTest, ResultSetOpen001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a SQLiteSingleVerResultSet. + */ + std::unique_ptr resultSet1 = + std::make_unique(g_store, EMPTY_KEY, OPTION); + + /** + * @tc.steps: step2. Call SQLiteSingleVerResultSet.Open with parameter true. + * @tc.expected: step2. Expect return E_OK. + */ + EXPECT_EQ(resultSet1->Open(true), E_OK); + + /** + * @tc.steps: step3. Create a SQLiteSingleVerResultSet. + */ + std::unique_ptr resultSet2 = + std::make_unique(g_store, EMPTY_KEY, OPTION); + + /** + * @tc.steps: step4. Call SQLiteSingleVerResultSet.Open with parameter false. + * @tc.expected: step4. Expect return E_OK. + */ + EXPECT_EQ(resultSet2->Open(false), E_OK); + + /** + * @tc.steps: step5. Close all ResultSet. + */ + resultSet1->Close(); + resultSet2->Close(); +} + +/** + * @tc.name: ResultSetGetCount001 + * @tc.desc: Test the SQLiteSingleVerResultSet GetCount function. + * @tc.type: FUNC + * @tc.require: AR000F3OP0 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBStorageResultAndJsonOptimizeTest, ResultSetGetCount001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a SQLiteSingleVerResultSet. + */ + std::unique_ptr resultSet = + std::make_unique(g_store, EMPTY_KEY, OPTION); + + /** + * @tc.steps: step2. Call SQLiteSingleVerResultSet.Open + * @tc.expected: step2. Expect return E_OK.Gits + */ + EXPECT_EQ(resultSet->Open(false), E_OK); + + /** + * @tc.steps: step2. Call SQLiteSingleVerResultSet.GetCount + * @tc.expected: step2. Expect return INSERT_NUMBER. + */ + EXPECT_EQ(resultSet->GetCount(), INSERT_NUMBER); + + /** + * @tc.steps: step3. Close the ResultSet. + */ + resultSet->Close(); +} + +/** + * @tc.name: ResultSetMoveTo001 + * @tc.desc: Test the SQLiteSingleVerResultSet MoveTo And GetPosition function. + * @tc.type: FUNC + * @tc.require: AR000F3OP0 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBStorageResultAndJsonOptimizeTest, ResultSetMoveTo001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a SQLiteSingleVerResultSet. + */ + std::unique_ptr resultSet = + std::make_unique(g_store, EMPTY_KEY, OPTION); + + /** + * @tc.steps: step2. Call SQLiteSingleVerResultSet.Open. + * @tc.expected: step2. Expect return E_OK. + */ + EXPECT_EQ(resultSet->Open(false), E_OK); + + /** + * @tc.steps: step3. Call SQLiteSingleVerResultSet MoveTo INSERT_NUMBER - 1 + * @tc.expected: step3. Expect return E_OK. + */ + EXPECT_EQ(resultSet->MoveTo(INSERT_NUMBER - 1), E_OK); + + /** + * @tc.steps: step4. Call SQLiteSingleVerResultSet GetPosition + * @tc.expected: step5. Expect return INSERT_NUMBER - 1. + */ + EXPECT_EQ(resultSet->GetPosition(), INSERT_NUMBER - 1); + + /** + * @tc.steps: step5. Call SQLiteSingleVerResultSet MoveTo INSERT_NUMBER + * @tc.expected: step5. Expect return -E_INVALID_ARGS. + */ + EXPECT_EQ(resultSet->MoveTo(INSERT_NUMBER), -E_INVALID_ARGS); + + /** + * @tc.steps: step6. Call SQLiteSingleVerResultSet GetPosition + * @tc.expected: step6. Expect return INSERT_NUMBER. + */ + EXPECT_EQ(resultSet->GetPosition(), INSERT_NUMBER); + + /** + * @tc.steps: step7. Call SQLiteSingleVerResultSet MoveTo -1 + * @tc.expected: step7. Expect return E_INVALID_ARGS. + */ + EXPECT_EQ(resultSet->MoveTo(-1), -E_INVALID_ARGS); + + /** + * @tc.steps: step8. Call SQLiteSingleVerResultSet GetPosition + * @tc.expected: step8. Expect return 0. + */ + EXPECT_EQ(resultSet->GetPosition(), -1); + + /** + * @tc.steps: step9. Call SQLiteSingleVerResultSet MoveTo 0 + * @tc.expected: step9. Expect return E_OK. + */ + EXPECT_EQ(resultSet->MoveTo(0), E_OK); + + /** + * @tc.steps: step10. Call SQLiteSingleVerResultSet GetPosition + * @tc.expected: step10. Expect return 0. + */ + EXPECT_EQ(resultSet->GetPosition(), 0); + + /** + * @tc.steps: step11. Close the ResultSet. + */ + resultSet->Close(); +} + +/** + * @tc.name: ResultSetGetEntry001 + * @tc.desc: Test the SQLiteSingleVerResultSet GetEntry function. + * @tc.type: FUNC + * @tc.require: AR000F3OP0 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBStorageResultAndJsonOptimizeTest, ResultSetGetEntry001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a SQLiteSingleVerResultSet. + */ + std::unique_ptr resultSet = + std::make_unique(g_store, EMPTY_KEY, OPTION); + + /** + * @tc.steps: step2. Call SQLiteSingleVerResultSet.Open + * @tc.expected: step2. Expect return E_OK. + */ + EXPECT_EQ(resultSet->Open(false), E_OK); + + /** + * @tc.steps: step2. Call SQLiteSingleVerResultSet MoveTo 0 And GetEntry + * @tc.expected: step2. Expect return E_OK. + */ + Entry entry; + ASSERT_EQ(resultSet->MoveTo(0), E_OK); + EXPECT_EQ(resultSet->GetEntry(entry), E_OK); + + /** + * @tc.expected: step2. Expect return Key == { 1 }, value == VALUE_1. + */ + const Key key = { 1 }; + EXPECT_EQ(entry.key, key); + EXPECT_EQ(entry.value, VALUE_1); + + /** + * @tc.steps: step3. Close the ResultSet. + */ + resultSet->Close(); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ca1b1b460ce8e47de591ceba6cd5d8b68ebe348 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.cpp @@ -0,0 +1,1902 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "distributeddb_storage_single_ver_natural_store_testcase.h" + +#include "generic_single_ver_kv_entry.h" +#include "time_helper.h" + +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + const int MAX_TEST_KEY_SIZE = 1024; + const int MAX_TEST_VAL_SIZE = 4194304; + + // select result index for the item for sync database + const int SYNC_RES_KEY_INDEX = 0; + const int SYNC_RES_VAL_INDEX = 1; + const int SYNC_RES_TIME_INDEX = 2; + const int SYNC_RES_FLAG_INDEX = 3; + const int SYNC_RES_HASH_KEY_INDEX = 6; + + const std::string SYNC_DATA_DEFAULT_SQL = "select * from SYNC_DATA;"; +} + +/** + * @tc.name: GetSyncData001 + * @tc.desc: To test the function of querying the data in the time stamp range in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, C) interface of the NaturalStore, where AGetMaxTimestamp(timeBegin); + Key key1; + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + Timestamp timeEnd; + store->GetMaxTimestamp(timeEnd); + EXPECT_GT(timeEnd, timeBegin); + + std::vector vect; + ContinueToken token = nullptr; + SyncInputArg inputArg(timeBegin, timeEnd + 1, 1024); // no more than 1024 + EXPECT_EQ(DistributedDBToolsUnitTest::GetSyncDataTest(inputArg, store, vect, token), E_OK); + + EXPECT_EQ(token, nullptr); + DataItem item = {key1, value1, 0, 0}; + EXPECT_EQ(DistributedDBToolsUnitTest::IsItemValueExist(item, vect), true); +} + +/** + * @tc.name: GetSyncData002 + * @tc.desc: Test the function that the database does not query the data in the time stamp range. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData002(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, B) interface of the NaturalStore, + * where APut(option, key, value), E_OK); + Timestamp timestamp; + store->GetMaxTimestamp(timestamp); + + std::vector vect; + ContinueToken token = nullptr; + SyncInputArg inputArg(timestamp + 1, timestamp + 1000, 1024); // no more than 1024 + EXPECT_EQ(DistributedDBToolsUnitTest::GetSyncDataTest(inputArg, store, vect, token), E_OK); + + EXPECT_EQ(token, nullptr); + EXPECT_EQ(vect.size(), 0UL); +} + +/** + * @tc.name: GetSyncData003 + * @tc.desc: To test the function of querying data when the timestamp range + * in the data obtaining interface is invalid. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData003(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, B) interface of the NaturalStore, where A>B + * @tc.expected: step1. The value of GetSyncData is E_INVALID_ARG. + */ + IOption option; + option.dataType = IOption::SYNC_DATA; + Timestamp timeBegin = 1000; // random + Timestamp timeEnd = 700; // random + std::vector vect; + ContinueToken token = nullptr; + SyncInputArg inputArg1(timeBegin, timeEnd, MAX_TEST_VAL_SIZE); + EXPECT_EQ(DistributedDBToolsUnitTest::GetSyncDataTest(inputArg1, store, vect, token), -E_INVALID_ARGS); + + timeEnd = timeBegin; + SyncInputArg inputArg2(timeBegin, timeEnd, MAX_TEST_VAL_SIZE); + EXPECT_EQ(DistributedDBToolsUnitTest::GetSyncDataTest(inputArg2, store, vect, token), -E_INVALID_ARGS); + + store->GetMaxTimestamp(timeBegin); + Key key1; + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + store->GetMaxTimestamp(timeEnd); + + SyncInputArg inputArg3(timeEnd, timeBegin, MAX_TEST_VAL_SIZE); + EXPECT_EQ(DistributedDBToolsUnitTest::GetSyncDataTest(inputArg3, store, vect, token), -E_INVALID_ARGS); + + EXPECT_EQ(token, nullptr); +} + +/** + * @tc.name: GetSyncData004 + * @tc.desc: To the test database Subcon reading, a large number of data records exist in the time stamp range. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData004(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + Key key; + Value value; + IOption option; + option.dataType = IOption::SYNC_DATA; + // The test assumes that there are ten data records + for (int i = 0; i < 10; i++) { + DistributedDBToolsUnitTest::GetRandomKeyValue(key, 100 + i); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(value, 9900 + i); // random size + EXPECT_EQ(connection->Put(option, key, value), E_OK); + } + + Timestamp timestamp = 0; + store->GetMaxTimestamp(timestamp); + + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, B) interface of the NaturalStore. + * @tc.expected: step1. Return E_GET_UNFINISHED. + */ + ContinueToken token = nullptr; + std::vector dataItems; + SyncInputArg inputArg(0, timestamp + 1, 30 * 1024); // 30k per block + EXPECT_EQ(DistributedDBToolsUnitTest::GetSyncDataTest(inputArg, store, dataItems, token), -E_UNFINISHED); + + EXPECT_NE(token, nullptr); + std::size_t countNum = dataItems.size(); + int count = 1; + do { + /** + * @tc.steps:step2. Continue to obtain data through the GetSyncDataNext() interface + * of the NaturalStore until the E_GET_FINISHED message is returned. + * @tc.expected: step2. When the GetSyncDataNext returns E_GET_FINISHED, + * the total number of obtained data is the number of inserted data and the data is consistent. + */ + dataItems.clear(); + int errCode = DistributedDBToolsUnitTest::GetSyncDataNextTest(store, 30 * 1024, dataItems, token); // 30k block + + countNum += dataItems.size(); + count++; + if (errCode == -E_UNFINISHED) { + continue; + } else if (errCode == -E_FINISHED || errCode == E_OK) { + break; + } else { + count = 0; + break; + } + } while (true); + EXPECT_EQ(token, nullptr); + EXPECT_EQ(countNum, 10UL); // 10 entries + EXPECT_EQ(count, 4); // 4 blocks +} + +/** + * @tc.name: GetSyncData005 + * @tc.desc: In the test database, if a large number of data records exist + * in the time stamp range, a packet is read successfully. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData005(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + Key key; + Value value; + IOption option; + option.dataType = IOption::SYNC_DATA; + for (int i = 0; i < 10; i++) { // 10 entries + DistributedDBToolsUnitTest::GetRandomKeyValue(key, 100 + i); // about 100 byte + DistributedDBToolsUnitTest::GetRandomKeyValue(value, 9900 + i); // about 9900 byte + EXPECT_EQ(connection->Put(option, key, value), E_OK); + } + + Timestamp timestamp = 0; + store->GetMaxTimestamp(timestamp); + + ContinueToken token = nullptr; + std::vector dataItems; + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, B) interface of the NaturalStore. + * @tc.expected: step1. The total size of all data in OK, dataItems is 99K. + */ + SyncInputArg inputArg(0, timestamp + 1, 100 * 1024); // for 100k + EXPECT_EQ(DistributedDBToolsUnitTest::GetSyncDataTest(inputArg, store, dataItems, token), E_OK); + + EXPECT_EQ(token, nullptr); + EXPECT_EQ(dataItems.size(), 10UL); +} + +/** + * @tc.name: GetSyncData006 + * @tc.desc: To test the function of reading data when the time stamp range in the database + * is greater than the value of blockSize. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData006(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key, MAX_TEST_KEY_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(value, MAX_TEST_VAL_SIZE); + + IOption option; + option.dataType = IOption::SYNC_DATA; + EXPECT_EQ(connection->Put(option, key, value), E_OK); + Timestamp timestamp = 0; + store->GetMaxTimestamp(timestamp); + + ContinueToken token = nullptr; + std::vector dataItems; + + /** + * @tc.steps:step1. Use the GetSyncData(A, B) interface of the NaturalStore + * and set blockSize to 50 kb to obtain the data within the time stamp range. + * @tc.expected: step1. The system returns E_GET_FINISHED. The size of the obtained data is 1 kb. + */ + SyncInputArg inputArg(0, timestamp + 1, 1000); // get size for 1k + EXPECT_EQ(DistributedDBToolsUnitTest::GetSyncDataTest(inputArg, store, dataItems, token), E_OK); + + EXPECT_EQ(token, nullptr); + DataItem item = {key, value, 0, 0}; + EXPECT_EQ(DistributedDBToolsUnitTest::IsItemValueExist(item, dataItems), true); +} + +/** + * @tc.name: PutSyncData001 + * @tc.desc: To test the function of synchronizing the new data of the remote device that synchronizes the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::PutSyncData001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + Timestamp timeBegin; + store->GetMaxTimestamp(timeBegin); + Key key1; + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, 13); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, 20); // random size + + /** + * @tc.steps:step1/2. Set Ioption to synchronous data and insert a (key1, value1) data record by put interface. + */ + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + Timestamp timeEnd; + store->GetMaxTimestamp(timeEnd); + EXPECT_GT(timeEnd, timeBegin); + + DataItem item1; + std::vector vect; + item1.key = key1; + DistributedDBToolsUnitTest::GetRandomKeyValue(item1.value, 18); // random size + item1.timestamp = timeBegin; + item1.writeTimestamp = item1.timestamp; + item1.flag = 0; + vect.push_back(item1); + + /** + * @tc.steps:step3. Insert a (key1, value2!=value1, timestamp, false) data record + * through the PutSyncData interface. The value of timestamp is less than or equal + * to the value of timestamp. For Compare the timestamp to determine whether to synchronization data. + * @tc.expected: step3. Return OK. + */ + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps:step4. The Ioption is set to synchronize data + * through the Get interface to obtain the value data of the key1. + * @tc.expected: step4. Return OK.The obtained value is value1. + */ + Value valueRead; + EXPECT_EQ(connection->Get(option, key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value1), true); + + item1.timestamp = timeEnd + 1; + item1.writeTimestamp = item1.timestamp; + vect.clear(); + vect.push_back(item1); + + /** + * @tc.steps:step5. Insert a (key1, value3!=value1, timestamp, false) data record + * through the PutSyncData interface of the NaturalStore. The value of timestamp + * is greater than that of timestamp inserted in 2. + * @tc.expected: step5. Return OK. + */ + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps:step6. The Ioption is set to synchronize data through the Get interface + * to obtain the value data of the key1. + * @tc.expected: step6. Return OK. + */ + EXPECT_EQ(connection->Get(option, key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(item1.value, valueRead), true); + + DistributedDBToolsUnitTest::GetRandomKeyValue(item1.key, 35); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(item1.value, 47); // random size + vect.clear(); + vect.push_back(item1); + + /** + * @tc.steps:step7. Insert a (key2, value4) data record through the PutSyncData interface. + * @tc.expected: step7. Return OK. + */ + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps:step8. The Ioption is set to synchronize data + * through the Get interface to obtain the value data of the key2. + * @tc.expected: step8. Returns OK, and the obtained data is value4. + */ + EXPECT_EQ(connection->Get(option, item1.key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(item1.value, valueRead), true); +} + +/** + * @tc.name: PutSyncData002 + * @tc.desc: To test the function of synchronizing data from the remote device + * to the local device after the data is deleted from the remote device. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::PutSyncData002(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + Timestamp timeBegin; + store->GetMaxTimestamp(timeBegin); + Key key1; + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, 37); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, 19); // random size + + /** + * @tc.steps:step1/2. Set Ioption to synchronous data and insert a (key1, value1) data record by put interface. + */ + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + Timestamp timeEnd; + store->GetMaxTimestamp(timeEnd); + EXPECT_GT(timeEnd, timeBegin); + + DataItem item1; + std::vector vect; + item1.key = key1; + DistributedDBToolsUnitTest::GetRandomKeyValue(item1.value, 18); // random size + item1.timestamp = timeBegin; + item1.writeTimestamp = item1.timestamp; + item1.flag = 1; + DistributedDBToolsUnitTest::CalcHash(key1, item1.key); + vect.push_back(item1); + /** + * @tc.steps:step3. Insert a (key1, value2!=value1, timestamp, false) data record + * through the PutSyncData interface. The value of timestamp is less than or equal + * to the value of timestamp. For Compare the timestamp to determine whether delete data. + * @tc.expected: step3. Return OK. + */ + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps:step4. The Ioption is set to synchronize data + * through the Get interface to obtain the value data of the key1. + * @tc.expected: step4. Return OK.The obtained value is value1. + */ + Value valueRead; + EXPECT_EQ(connection->Get(option, key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value1), true); + + item1.timestamp = timeEnd + 1; + item1.writeTimestamp = item1.timestamp; + vect.clear(); + vect.push_back(item1); + + /** + * @tc.steps:step5. Insert a (key1, value3!=value1, timestamp, false) data record + * through the PutSyncData interfac. The value of timestamp + * is greater than that of timestamp inserted in step2. + * @tc.expected: step5. Return OK. + */ + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps:step6. The Ioption is set to synchronize data through the Get interface + * to obtain the value data of the key1. + * @tc.expected: step6. Return E_NOT_FOUND. + */ + EXPECT_EQ(connection->Get(option, key1, valueRead), -E_NOT_FOUND); + + // put remote deleted data which not existed locally. + DistributedDBToolsUnitTest::GetRandomKeyValue(item1.key, 35); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(item1.value, 47); // random size + vect.clear(); + vect.push_back(item1); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + EXPECT_EQ(connection->Get(option, item1.key, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: PutSyncData003 + * @tc.desc: To test the function of synchronizing the mixed data of the added + * and deleted data from the remote device to the local device. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::PutSyncData003(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + Timestamp timeBegin; + store->GetMaxTimestamp(timeBegin); + DataItem dataItem1; + DataItem dataItem2; + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem1.key, 23); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem2.key, 15); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem1.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem2.value); + dataItem1.timestamp = timeBegin + 1; // ensure bigger timestamp + dataItem1.writeTimestamp = dataItem1.timestamp; + dataItem2.timestamp = timeBegin + 2; // ensure bigger timestamp + dataItem2.writeTimestamp = dataItem2.timestamp; + dataItem1.flag = dataItem2.flag = 0; + + /** + * @tc.steps:step1. Insert a data record (key1,value1 is not null) and (key2, value2 is not null) + * through the PutSyncData interface. + * @tc.expected: step1. Return OK. + */ + std::vector vect = {dataItem1, dataItem2}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps:step2. Set Ioption as the synchronization data to obtain the data of key1 and key2. + * @tc.expected: step2. The Get interface returns OK. The value of key1 is value1, + * and the value of key2 is value2. + */ + Value valueRead1, valueRead2; + EXPECT_EQ(connection->Get(option, dataItem1.key, valueRead1), E_OK); + EXPECT_EQ(connection->Get(option, dataItem2.key, valueRead2), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead1, dataItem1.value), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead2, dataItem2.value), true); + + /** + * @tc.steps:step3. Insert a (key3, value3) and delete the data of the (key1, value1). + * @tc.expected: step3. The PutSyncData returns OK. + */ + DataItem dataItem3 = dataItem1; + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem3.key, 38); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem3.value, 27); // random size + + DataItem dataItem4 = dataItem1; + dataItem4.flag = 1; + dataItem4.timestamp += 1; + dataItem4.writeTimestamp = dataItem4.timestamp; + DistributedDBToolsUnitTest::CalcHash(dataItem1.key, dataItem4.key); + vect = {dataItem4, dataItem3}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps:step4. Set Ioption to the synchronization data and obtain the data of key1, key2, and key3. + * @tc.expected: step4. Get key1 returns E_NOT_FOUND,Get key2. + * The value of OK,value is value2, the value of Get key3 is OK, + * and the value of value is value3. + */ + valueRead1.clear(); + valueRead2.clear(); + Value valueRead3; + EXPECT_EQ(connection->Get(option, dataItem1.key, valueRead1), -E_NOT_FOUND); + EXPECT_EQ(connection->Get(option, dataItem2.key, valueRead2), E_OK); + EXPECT_EQ(connection->Get(option, dataItem3.key, valueRead3), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead2, dataItem2.value), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead3, dataItem3.value), true); +} + +/** + * @tc.name: PutMetaData001 + * @tc.desc: Test metadata insertion and modification. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::PutMetaData001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + TestMetaDataPutAndGet(store, connection); +} + +/** + * @tc.name: GetMetaData001 + * @tc.desc: To test the function of reading the metadata of a key in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::GetMetaData001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + /** + * @tc.steps:step1. Use GetMetaData in NaturalStore to obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step1. Return OK, and the value is the same as the value of value1. + */ + TestMetaDataPutAndGet(store, connection); +} + +/** + * @tc.name: DeleteMetaData001 + * @tc.desc: To test the function of deleting the metadata with prefix key in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteMetaData001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + /** + * @tc.steps:step1. Put 2 mete data with prefix key 'a', 2 meta data with prefix key 'b'. + And delete meta data with prefix key 'b'. + * @tc.expected: step1. Get all meta data and will get 2 data with prefix key 'a'. + */ + TestMetaDataDeleteByPrefixKey(store, connection); +} + +/** + * @tc.name: GetCurrentMaxTimestamp001 + * @tc.desc: To test the function of obtaining the maximum timestamp when a record exists in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::GetCurrentMaxTimestamp001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + Key key1; + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + Timestamp timeBegin = 0; + Timestamp timeMiddle = 0; + Timestamp timeEnd = 0; + + /** + * @tc.steps:step1/2. Insert a data record into the synchronization database. + */ + store->GetMaxTimestamp(timeBegin); + IOption option; + option.dataType = IOption::SYNC_DATA; + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + + /** + * @tc.steps:step3. The current maximum timestamp is A. + */ + store->GetMaxTimestamp(timeMiddle); + EXPECT_GT(timeMiddle, timeBegin); + + /** + * @tc.steps:step4. Insert a data record into the synchronization database. + */ + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + + /** + * @tc.steps:step5. Obtain the maximum timestamp B and check whether B>=A exists. + * @tc.expected: step5. The obtained timestamp is B>=A. + */ + store->GetMaxTimestamp(timeEnd); + EXPECT_GT(timeEnd, timeMiddle); +} + +/** + * @tc.name: GetCurrentMaxTimestamp002 + * @tc.desc: Obtain the maximum timestamp when no record exists in the test record library. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::GetCurrentMaxTimestamp002(SQLiteSingleVerNaturalStore *&store) +{ + /** + * @tc.steps:step1. Obtains the maximum timestamp in the current database record. + * @tc.expected: step1. Return timestamp is 0. + */ + Timestamp timestamp = 10; // non-zero + store->GetMaxTimestamp(timestamp); + EXPECT_EQ(timestamp, 0UL); +} + +/** + * @tc.name: LocalDatabaseOperate001 + * @tc.desc: Test the function of inserting data in the local database of the NaturalStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::LocalDatabaseOperate001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::LOCAL_DATA; + DataBaseCommonPutOperate(store, connection, option); +} + +/** + * @tc.name: LocalDatabaseOperate002 + * @tc.desc: Test the function of deleting data from the local database of the NaturalStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::LocalDatabaseOperate002(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::LOCAL_DATA; + DataBaseCommonDeleteOperate(store, connection, option); +} + +/** + * @tc.name: LocalDatabaseOperate003 + * @tc.desc: To test the function of reading data from the local database of the NaturalStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::LocalDatabaseOperate003(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::LOCAL_DATA; + DataBaseCommonGetOperate(store, connection, option); +} + +/** + * @tc.name: SyncDatabaseOperate001 + * @tc.desc: To test the function of inserting data of the local device in the synchronization database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + DataBaseCommonPutOperate(store, connection, option); +} + +/** + * @tc.name: SyncDatabaseOperate002 + * @tc.desc: test the put operation after data synced from other devices. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate002(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + DataItem dataItem1; + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem1.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem1.value); + dataItem1.timestamp = 1001; // 1001 as random timestamp + dataItem1.writeTimestamp = dataItem1.timestamp; + dataItem1.flag = 0; + + /** + * @tc.steps: step1/2. Add a remote synchronization data record. (key1, value1). + */ + std::vector vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps: step3. Ioption is set to synchronous data. Obtains the value data of the key1. + * @tc.expected: step3. Return OK. The value is the same as the value of value1. + */ + Value valueRead; + EXPECT_EQ(connection->Get(option, dataItem1.key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, dataItem1.value), true); + + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, dataItem1.value.size() + 1); + + /** + * @tc.steps: step4. Ioption Set the data to be synchronized and insert the data of key1,value2. + * @tc.expected: step4. Return OK. + */ + EXPECT_EQ(connection->Put(option, dataItem1.key, value2), E_OK); + EXPECT_EQ(connection->Get(option, dataItem1.key, valueRead), E_OK); + + /** + * @tc.steps: step3. Ioption is set to synchronous data. Obtains the value data of the key1. + * @tc.expected: step3. Return OK. The value is the same as the value of value2. + */ + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value2), true); +} + +/** + * @tc.name: SyncDatabaseOperate003 + * @tc.desc: test the delete operation in sync database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate003(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + DataBaseCommonDeleteOperate(store, connection, option); +} + +/** + * @tc.name: SyncDatabaseOperate004 + * @tc.desc: test the delete for the data from other devices in sync database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate004(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + DataItem dataItem1; + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem1.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem1.value); + dataItem1.timestamp = 1997; // 1997 as random timestamp + dataItem1.writeTimestamp = dataItem1.timestamp; + dataItem1.flag = 0; + + std::vector vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps: step2. The Ioption parameter is set to synchronize data to obtain the value data of the key1. + * @tc.expected: step2. Return OK. The value is the same as the value of value1. + */ + Value valueRead; + EXPECT_EQ(connection->Get(option, dataItem1.key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, dataItem1.value), true); + + Key key2; + DistributedDBToolsUnitTest::GetRandomKeyValue(key2); + EXPECT_EQ(connection->Delete(option, key2), E_OK); + + /** + * @tc.steps: step3. The Ioption parameter is set to synchronize data, and the key1 data is deleted. + * @tc.expected: step3. Return OK. + */ + EXPECT_EQ(connection->Delete(option, dataItem1.key), E_OK); + + /** + * @tc.steps: step4. The Ioption parameter is set to synchronize data to obtain the value data of the key1. + * @tc.expected: step4. Return E_NOT_FOUND. + */ + EXPECT_EQ(connection->Get(option, dataItem1.key, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: SyncDatabaseOperate005 + * @tc.desc: test the reading for sync database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate005(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + DataBaseCommonGetOperate(store, connection, option); +} + +/** + * @tc.name: SyncDatabaseOperate006 + * @tc.desc: test the get entries for sync database + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate006(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + Key key1, key2, key3; + Value value1, value2, value3; + + /** + * @tc.steps: step2/3/4. Set Ioption to synchronous data. + * Insert the data of key=keyPrefix + 'a', value1. + * Insert the data of key=keyPrefix + 'c', value2. + * Insert the data of key length=keyPrefix length - 1, value3. + * @tc.expected: step2/3/4. Return E_NOT_FOUND. + */ + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, 30); // 30 as random size + key3 = key2 = key1; + key2.push_back('C'); + key3.pop_back(); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, 84); // 84 as random size + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, 101); // 101 as random size + DistributedDBToolsUnitTest::GetRandomKeyValue(value3, 37); // 37 as random size + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + EXPECT_EQ(connection->Put(option, key2, value2), E_OK); + EXPECT_EQ(connection->Put(option, key3, value3), E_OK); + + /** + * @tc.steps: step5. Obtain all data whose prefixKey is keyPrefix. + * @tc.expected: step5. Return OK. The number of obtained data records is 2. + */ + std::vector entriesRead; + EXPECT_EQ(connection->GetEntries(option, key1, entriesRead), E_OK); + EXPECT_EQ(entriesRead.size(), 2UL); + + /** + * @tc.steps: step6. Obtain all data whose prefixKey is empty. + * @tc.expected: step6. Return OK. The number of obtained data records is 3. + */ + entriesRead.clear(); + Key emptyKey; + EXPECT_EQ(connection->GetEntries(option, emptyKey, entriesRead), E_OK); + EXPECT_EQ(entriesRead.size(), 3UL); + + /** + * @tc.steps: step7. Obtain all data whose prefixKey is keyPrefix. + * @tc.expected: step7. Return E_NOT_SUPPORT. + */ + option.dataType = IOption::LOCAL_DATA; + EXPECT_EQ(connection->GetEntries(option, emptyKey, entriesRead), -E_NOT_FOUND); +} + +/** + * @tc.name: ClearRemoteData001 + * @tc.desc: test the clear data synced from the remote by device. + * @tc.type: FUNC + * @tc.require: AR000CIFDA AR000CQS3T + * @tc.author: wangbingquan + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::ClearRemoteData001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + DataItem dataItem1; + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem1.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem1.value); + dataItem1.timestamp = 1997; // 1997 as random timestamp + dataItem1.writeTimestamp = dataItem1.timestamp; + dataItem1.flag = 0; + + DataItem dataItem2; + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem2.key, dataItem1.key.size() + 1); + DistributedDBToolsUnitTest::GetRandomKeyValue(dataItem2.value); + dataItem2.timestamp = 2019; // 2019 as random timestamp + dataItem2.writeTimestamp = dataItem2.timestamp; + dataItem2.flag = 0; + + /** + * @tc.steps: step1. New data is inserted to the B end of the device. [keyB, valueB]. + */ + std::vector vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceA"), E_OK); + + /** + * @tc.steps: step2. The device pulls the data of the device B, and the device inserts the [keyA, valueA]. + */ + vect.clear(); + vect.push_back(dataItem2); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps: step3. The device obtains the data of keyA and valueB. + * @tc.expected: step3. Obtain [keyA, valueA] and [keyB, valueB]. + */ + Value valueRead; + EXPECT_EQ(connection->Get(option, dataItem1.key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, dataItem1.value), true); + EXPECT_EQ(connection->Get(option, dataItem2.key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, dataItem2.value), true); + + /** + * @tc.steps: step4.Invoke the interface for clearing the synchronization data of the B device. + */ + EXPECT_EQ(store->RemoveDeviceData("deviceA", false), E_OK); + + /** + * @tc.steps: step5. The device obtains the data of keyA and valueB. + * @tc.expected: step5. The value of [keyA, valueA] is obtained, + * and the value of NOT_FOUND is obtained by querying keyB. + */ + EXPECT_EQ(connection->Get(option, dataItem1.key, valueRead), -E_NOT_FOUND); + EXPECT_EQ(connection->Get(option, dataItem2.key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, dataItem2.value), true); + + EXPECT_EQ(store->RemoveDeviceData("deviceB", false), E_OK); + EXPECT_EQ(connection->Get(option, dataItem2.key, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: DeleteUserKeyValue001 + * @tc.desc: When a user deletes a data record, the system clears the user record. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue001(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &url) +{ + // StoreID::TestGeneralNB + IOption option; + option.dataType = IOption::SYNC_DATA; + + // per-set data + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(connection->Put(option, KEY_2, VALUE_2), E_OK); + + /** + * @tc.steps: step1. delete K1. + * @tc.expected: step1. delete K1 successfully. + */ + EXPECT_EQ(connection->Delete(option, KEY_1), E_OK); + + // Close database + connection->Close(); + connection = nullptr; + store = nullptr; + + /** + * @tc.steps: step2. Real query by sqlite3. + * @tc.expected: step2. Find KEY_1, not find K2. + */ + std::vector vecSyncData; + int numSelect = GetRawSyncData(url, SYNC_DATA_DEFAULT_SQL, vecSyncData); + + bool isFound = false; + EXPECT_EQ(numSelect, 2); // 2 as entry size + isFound = IsSqlinteExistKey(vecSyncData, KEY_1); + EXPECT_EQ(isFound, false); + isFound = IsSqlinteExistKey(vecSyncData, KEY_2); + EXPECT_EQ(isFound, true); +} + +/** + * @tc.name: MemoryDbDeleteUserKeyValue001 + * @tc.desc: When a user deletes a data record, the system clears the user record. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::MemoryDbDeleteUserKeyValue001( + SQLiteSingleVerNaturalStore *&store, SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &url) +{ + // StoreID::TestGeneralNB + IOption option; + option.dataType = IOption::SYNC_DATA; + + // per-set data + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(connection->Put(option, KEY_2, VALUE_2), E_OK); + + /** + * @tc.steps: step1. delete K1. + * @tc.expected: step1. delete K1 successfully. + */ + EXPECT_EQ(connection->Delete(option, KEY_1), E_OK); + + /** + * @tc.steps: step3. Real query by sqlite3. + * @tc.expected: step3. Find KEY_1, not find K2. + */ + std::vector vecSyncData; + int numSelect = GetRawSyncData(url, SYNC_DATA_DEFAULT_SQL, vecSyncData); + + bool isFound = false; + EXPECT_EQ(numSelect, 2); // 2 as entry size + isFound = IsSqlinteExistKey(vecSyncData, KEY_1); + EXPECT_EQ(isFound, false); + isFound = IsSqlinteExistKey(vecSyncData, KEY_2); + EXPECT_EQ(isFound, true); + + // Close database + connection->Close(); + connection = nullptr; + store = nullptr; +} + +/** + * @tc.name: DeleteUserKeyValue002 + * @tc.desc: After the synchronization library data is deleted locally, add the same key data locally. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue002(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &url) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + + // pre-set data + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(connection->Put(option, KEY_2, VALUE_2), E_OK); + + /** + * @tc.steps: step1. Delete key1 data via Delete interface. + * @tc.expected: step1. Delete successfully. + */ + EXPECT_EQ(connection->Delete(option, KEY_1), E_OK); + + /** + * @tc.steps: step2. New data from key1, value3 via Put interface. + * @tc.expected: step2. New data from key1, value3 via Put interface successfully. + */ + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_3), E_OK); + + /** + * @tc.steps: step3. Query key1 data via Get interface. + * @tc.expected: step3. Query key1 data via Get interface successfully, get value3 by key1. + */ + Value valueRead; + EXPECT_EQ(connection->Get(option, KEY_1, valueRead), E_OK); + EXPECT_EQ(valueRead, VALUE_3); + + /** + * @tc.steps: step4. Query key1 real data by sqlite3. + * @tc.expected: step4. Two records were found. + */ + std::vector vecSyncData; + int numSelect = GetRawSyncData(url, SYNC_DATA_DEFAULT_SQL, vecSyncData); + + EXPECT_EQ(numSelect, 2); // 2 as entry size +} + +/** + * @tc.name: DeleteUserKeyValue003 + * @tc.desc: After the synchronization database data is deleted locally, the same key data is added from the remote end. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue003(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &url) +{ + IOption option; + option.dataType = IOption::SYNC_DATA; + + // ready data + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + + /** + * @tc.steps: step1. Delete data by key1. + * @tc.expected: step1. Delete successfully. + */ + EXPECT_EQ(connection->Delete(option, KEY_1), E_OK); + + /** + * @tc.steps: step2. Get data by key1. + * @tc.expected: step1. Key1 not exist in database. + */ + Value valueRead; + EXPECT_NE(connection->Get(option, KEY_1, valueRead), E_OK); + + Timestamp timestamp = 0; + store->GetMaxTimestamp(timestamp); + + DataItem dataItem1; + dataItem1.key = KV_ENTRY_1.key; + dataItem1.value = KV_ENTRY_3.value; + dataItem1.timestamp = timestamp - 100UL; // less than current timestamp + dataItem1.writeTimestamp = dataItem1.timestamp; + dataItem1.flag = 0; + + DataItem dataItem2; + dataItem2.key = KV_ENTRY_1.key; + dataItem2.value = KV_ENTRY_4.value; + dataItem2.timestamp = timestamp + 100UL; // bigger than current timestamp + dataItem2.writeTimestamp = dataItem2.timestamp; + dataItem2.flag = 0; + std::vector vect = {dataItem1}; + + /** + * @tc.steps: step3. Get a new data from remote device B , key1, value3, + * with a smaller timestamp than the current timestamp. + */ + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps: step4. Get data by key1. + * @tc.expected: step4. Key1 not exist in database. + */ + EXPECT_NE(connection->Get(option, KEY_1, valueRead), E_OK); + + /** + * @tc.steps: step5. Get a new data from remote device C , key1, value4, + * and the timestamp is larger than the current timestamp. + */ + vect.clear(); + vect.push_back(dataItem2); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceC"), E_OK); + + /** + * @tc.steps: step6. Get data by key1. + * @tc.expected: step6. Key1 not exist in database. + */ + EXPECT_EQ(connection->Get(option, KEY_1, valueRead), E_OK); + + /** + * @tc.steps: step7. Get real data by key1. + * @tc.expected: step7. Get 1 record. + */ + std::vector vecSyncData; + int numSelect = GetRawSyncData(url, SYNC_DATA_DEFAULT_SQL, vecSyncData); + + EXPECT_EQ(numSelect, 1); +} + +/** + * @tc.name: DeleteUserKeyValue004 + * @tc.desc: Changes in key after remote delete data syncs to local + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue004(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &url) +{ + // pre-set data + IOption option; + option.dataType = IOption::SYNC_DATA; + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + + DataItem dataItem1; + dataItem1.key = KV_ENTRY_1.key; + dataItem1.value = KV_ENTRY_1.value; + store->GetMaxTimestamp(dataItem1.timestamp); + dataItem1.flag = 1; + dataItem1.timestamp += 1; + dataItem1.writeTimestamp = dataItem1.timestamp; + /** + * @tc.steps: step1 2 3. Synchronize data to another device B; delete key1 data from device B; + * pull the action of key1 to local. + */ + DistributedDBToolsUnitTest::CalcHash(KV_ENTRY_1.key, dataItem1.key); + std::vector vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps: step4. Close database. + */ + connection->Close(); + connection = nullptr; + store = nullptr; + + /** + * @tc.steps: step5 6. Get real data by key1;and get the number of records. + * @tc.expected: step5 6. Not exist key1 real data in database;Get 1 record. + */ + std::vector vecSyncData; + int numSelect = GetRawSyncData(url, SYNC_DATA_DEFAULT_SQL, vecSyncData); + + EXPECT_EQ(numSelect, 1); + bool isFound = IsSqlinteExistKey(vecSyncData, KEY_1); + EXPECT_EQ(isFound, false); +} + +/** + * @tc.name: MemoryDbDeleteUserKeyValue004 + * @tc.desc: Changes in key after remote delete data syncs to local + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::MemoryDbDeleteUserKeyValue004( + SQLiteSingleVerNaturalStore *&store, SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &url) +{ + // pre-set data + IOption option; + option.dataType = IOption::SYNC_DATA; + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + + DataItem dataItem1; + dataItem1.key = KV_ENTRY_1.key; + dataItem1.value = KV_ENTRY_1.value; + store->GetMaxTimestamp(dataItem1.timestamp); + dataItem1.flag = 1; + dataItem1.timestamp += 1; + dataItem1.writeTimestamp = dataItem1.timestamp; + /** + * @tc.steps: step1 2 3. Synchronize data to another device B; delete key1 data from device B; + * pull the action of key1 to local. + */ + DistributedDBToolsUnitTest::CalcHash(KV_ENTRY_1.key, dataItem1.key); + std::vector vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps: step4 5. Get real data by key1;and get the number of records. + * @tc.expected: step 4 5. Not exist key1 real data in database;Get 1 record. + */ + std::vector vecSyncData; + int numSelect = GetRawSyncData(url, SYNC_DATA_DEFAULT_SQL, vecSyncData); + + EXPECT_EQ(numSelect, 1); + bool isFound = IsSqlinteExistKey(vecSyncData, KEY_1); + EXPECT_EQ(isFound, false); + + connection->Close(); + connection = nullptr; + store = nullptr; +} + +/** + * @tc.name: DeleteUserKeyValue005 + * @tc.desc: New unified key data locally after remote delete data syncs to local + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue005(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &url) +{ + // pre-set data + IOption option; + option.dataType = IOption::SYNC_DATA; + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(connection->Put(option, KEY_2, VALUE_2), E_OK); + + DataItem dataItem1; + dataItem1.key = KV_ENTRY_1.key; + dataItem1.value = KV_ENTRY_1.value; + store->GetMaxTimestamp(dataItem1.timestamp); + dataItem1.timestamp = dataItem1.timestamp + 10UL; + dataItem1.writeTimestamp = dataItem1.timestamp; + dataItem1.flag = 1; + + /** + * @tc.steps: step1 2 3. Synchronize data to another device B; delete key1 data from device B; + * pull the action of key1 to local. + */ + DistributedDBToolsUnitTest::CalcHash(KV_ENTRY_1.key, dataItem1.key); + std::vector vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps: step4. Put K1 V1 to database. + * @tc.expected: step4. Put successfully. + */ + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + + /** + * @tc.steps: step5. Close database. + */ + connection->Close(); + connection = nullptr; + store = nullptr; + + /** + * @tc.steps: step6 7. Get real data by key1;and get the number of records. + * @tc.expected: step6 7. Not exist key1 real data in database;Get 2 record. + */ + std::vector vecSyncData; + int numSelect = GetRawSyncData(url, SYNC_DATA_DEFAULT_SQL, vecSyncData); + + EXPECT_EQ(numSelect, 2); // 2 as entry size + bool isFound = IsSqlinteExistKey(vecSyncData, KEY_1); + EXPECT_EQ(isFound, true); +} + +/** + * @tc.name: MemoryDbDeleteUserKeyValue005 + * @tc.desc: New unified key data locally after remote delete data syncs to local + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::MemoryDbDeleteUserKeyValue005( + SQLiteSingleVerNaturalStore *&store, SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &url) +{ + // pre-set data + IOption option; + option.dataType = IOption::SYNC_DATA; + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(connection->Put(option, KEY_2, VALUE_2), E_OK); + + DataItem dataItem1; + dataItem1.key = KV_ENTRY_1.key; + dataItem1.value = KV_ENTRY_1.value; + store->GetMaxTimestamp(dataItem1.timestamp); + dataItem1.timestamp = TimeHelper::GetSysCurrentTime(); + dataItem1.writeTimestamp = dataItem1.timestamp; + dataItem1.flag = 1; + + /** + * @tc.steps: step1 2 3. Synchronize data to another device B; delete key1 data from device B; + * pull the action of key1 to local. + */ + DistributedDBToolsUnitTest::CalcHash(KV_ENTRY_1.key, dataItem1.key); + std::vector vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps: step4. Put K1 V1 to database. + * @tc.expected: step4. Put successfully. + */ + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + + /** + * @tc.steps: step5 6. Get real data by key1;and get the number of records. + * @tc.expected: step5 6. Not exist key1 real data in database;Get 2 record. + */ + std::vector vecSyncData; + int numSelect = GetRawSyncData(url, SYNC_DATA_DEFAULT_SQL, vecSyncData); + + EXPECT_EQ(numSelect, 2); // 2 as entry size + bool isFound = IsSqlinteExistKey(vecSyncData, KEY_1); + EXPECT_EQ(isFound, true); + + connection->Close(); + connection = nullptr; + store = nullptr; +} + +/** + * @tc.name: DeleteUserKeyValue006 + * @tc.desc: After the remote delete data is synced to the local, + * the same key data is added from the remote other devices + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +void DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue006(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &url) +{ + // pre-set data + IOption option; + option.dataType = IOption::SYNC_DATA; + EXPECT_EQ(connection->Put(option, KEY_1, VALUE_1), E_OK); + + DataItem dataItem1; + dataItem1.key = KV_ENTRY_1.key; + dataItem1.value = KV_ENTRY_1.value; + store->GetMaxTimestamp(dataItem1.timestamp); + dataItem1.timestamp = dataItem1.timestamp + 10UL; + dataItem1.writeTimestamp = dataItem1.timestamp; + dataItem1.flag = 1; + + /** + * @tc.steps: step1. Remote device B sync deletes data key1 and pushes to local. + */ + DistributedDBToolsUnitTest::CalcHash(KV_ENTRY_1.key, dataItem1.key); + std::vector vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceB"), E_OK); + + /** + * @tc.steps: step2. Get key1 from database. + * @tc.expected: step2. Not exist key1. + */ + Value valueRead; + EXPECT_NE(connection->Get(option, KEY_1, valueRead), E_OK); + + dataItem1.key = KV_ENTRY_1.key; + dataItem1.flag = 0; + dataItem1.value = KV_ENTRY_2.value; + dataItem1.timestamp = dataItem1.timestamp - 100UL; // less than current timestamp + dataItem1.writeTimestamp = dataItem1.timestamp; + /** + * @tc.steps: step3. Remote device C syncs new data (key1, value2), + * timestamp is less than delete timestamp, to local. + */ + vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceC"), E_OK); + + /** + * @tc.steps: step4. Get key1 from database. + * @tc.expected: step4. Not exist key1. + */ + EXPECT_NE(connection->Get(option, KEY_1, valueRead), E_OK); + + dataItem1.value = KV_ENTRY_3.value; + dataItem1.timestamp = dataItem1.timestamp + 200UL; // bigger than current timestamp + dataItem1.writeTimestamp = dataItem1.timestamp; + /** + * @tc.steps: step5. Remote device C syncs new data (key1, value2), + * timestamp is bigger than delete timestamp, to local. + */ + vect = {dataItem1}; + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, vect, "deviceD"), E_OK); + + /** + * @tc.steps: step6. Get key1 from database. + * @tc.expected: step6. Exist key1. + */ + EXPECT_EQ(connection->Get(option, KEY_1, valueRead), E_OK); + EXPECT_EQ(valueRead, VALUE_3); + + /** + * @tc.steps: step7. Get real data from database. + * @tc.expected: step7. Get 1 record. + */ + std::vector vecSyncData; + int numSelect = GetRawSyncData(url, SYNC_DATA_DEFAULT_SQL, vecSyncData); + + EXPECT_EQ(numSelect, 1); +} + +// private Begin +void DistributedDBStorageSingleVerNaturalStoreTestCase::CreateMemDb(SQLiteSingleVerNaturalStoreConnection *&connection, + int &errCode) +{ + // pre-Set close other db + if (connection != nullptr) { + connection->Close(); + connection = nullptr; + } + + KvDBProperties property; + property.SetStringProp(KvDBProperties::STORE_ID, "TestGeneralNB"); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, "TestGeneralNB"); + property.SetBoolProp(KvDBProperties::MEMORY_MODE, true); + + SQLiteSingleVerNaturalStore *memoryStore = nullptr; + memoryStore = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(memoryStore, nullptr); + + errCode = memoryStore->Open(property); + if (errCode != E_OK) { + return; + } + + connection = static_cast(memoryStore->GetDBConnection(errCode)); + ASSERT_NE(connection, nullptr); + memoryStore->DecObjRef(memoryStore); +} + +// param [in] dbName:Database name,strSql: The sql statement executed,[out],vecSyncData:SYNC_DATA table data +// Real query sync-DATA table data via sqlite. return query data row number +int DistributedDBStorageSingleVerNaturalStoreTestCase::GetRawSyncData(const std::string &dbName, + const std::string &strSql, std::vector &vecSyncData) +{ + uint64_t flag = SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE; + flag |= SQLITE_OPEN_CREATE; + + sqlite3* db = nullptr; + int nResult = sqlite3_open_v2(dbName.c_str(), &db, flag, nullptr); + if (nResult != SQLITE_OK) { + return -nResult; + } + + sqlite3_stmt *statement = nullptr; + + nResult = sqlite3_prepare(db, strSql.c_str(), -1, &statement, NULL); + if (nResult != SQLITE_OK) { + (void)sqlite3_close_v2(db); + return -1; + } + + while (sqlite3_step(statement) == SQLITE_ROW) { + SyncData stuSyncData; + const uint8_t *blobValue = static_cast(sqlite3_column_blob(statement, SYNC_RES_KEY_INDEX)); + int valueLength = sqlite3_column_bytes(statement, SYNC_RES_KEY_INDEX); + if (blobValue == nullptr) { + stuSyncData.key.clear(); + } else { + stuSyncData.key.resize(valueLength); + stuSyncData.key.assign(blobValue, blobValue + valueLength); + } + + blobValue = static_cast(sqlite3_column_blob(statement, SYNC_RES_HASH_KEY_INDEX)); + valueLength = sqlite3_column_bytes(statement, SYNC_RES_HASH_KEY_INDEX); + stuSyncData.hashKey.resize(valueLength); + stuSyncData.hashKey.assign(blobValue, blobValue + valueLength); + + blobValue = static_cast(sqlite3_column_blob(statement, SYNC_RES_VAL_INDEX)); + valueLength = sqlite3_column_bytes(statement, SYNC_RES_VAL_INDEX); + if (blobValue == nullptr) { + stuSyncData.value.clear(); + } else { + stuSyncData.value.resize(valueLength); + stuSyncData.value.assign(blobValue, blobValue + valueLength); + } + + stuSyncData.timestamp = static_cast(sqlite3_column_int64(statement, SYNC_RES_TIME_INDEX)); + stuSyncData.flag = sqlite3_column_int64(statement, SYNC_RES_FLAG_INDEX); + vecSyncData.push_back(stuSyncData); + } + + sqlite3_finalize(statement); + statement = nullptr; + (void)sqlite3_close_v2(db); + return static_cast(vecSyncData.size()); +} + +// @Real query sync-DATA table by key, judge is exist. +bool DistributedDBStorageSingleVerNaturalStoreTestCase::IsSqlinteExistKey(const std::vector &vecSyncData, + const std::vector &key) +{ + for (const auto &iter : vecSyncData) { + if (key == iter.key) { + return true; + } + } + return false; +} + +void DistributedDBStorageSingleVerNaturalStoreTestCase::TestMetaDataPutAndGet(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection) +{ + Key key1; + Value value1; + Key emptyKey; + Value emptyValue; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + + /** + * @tc.steps:step1. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step1. Return OK. + */ + EXPECT_EQ(store->PutMetaData(key1, value1), E_OK); + + /** + * @tc.steps:step2. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step2. The obtained value is the same as the value of value1. + */ + Value valueRead; + EXPECT_EQ(store->GetMetaData(key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value1), true); + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, static_cast(value1.size() + 3)); // 3 as random size + + /** + * @tc.steps:step3. The key value is key1, the value is not empty, + * and the value of value2 is different from the value of value1 through the PutMetaData interface. + * @tc.expected: step3. Return OK. + */ + EXPECT_EQ(store->PutMetaData(key1, value2), E_OK); + + /** + * @tc.steps:step4. Run the GetMetaData command to obtain the value of key1 + * and check whether the value is the same as the value of value2. + * @tc.expected: step4. The obtained value is the same as the value of value2. + */ + EXPECT_EQ(store->GetMetaData(key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value2), true); + + /** + * @tc.steps:step5. Use PutMetaData to insert a record whose key is empty and value is not empty. + * @tc.expected: step5. Return E_INVALID_ARGS. + */ + EXPECT_EQ(store->PutMetaData(emptyKey, value1), -E_INVALID_ARGS); + + /** + * @tc.steps:step6. Use PutMetaData in NaturalStore to insert data whose key2(!=key1) + * is not empty and value is empty. + * @tc.expected: step6. Return OK. + */ + Key key2; + DistributedDBToolsUnitTest::GetRandomKeyValue(key2, static_cast(key1.size() + 1)); + EXPECT_EQ(store->PutMetaData(key2, emptyValue), E_OK); + + /** + * @tc.steps:step7. Obtain the value of key2 and check whether the value is empty. + * @tc.expected: step7. The obtained value is empty. + */ + EXPECT_EQ(store->GetMetaData(key2, valueRead), E_OK); + EXPECT_EQ(valueRead.empty(), true); + + /** + * @tc.steps:step8. Insert the data whose key size is 1024 and value size is 4Mb + * through PutMetaData of NaturalStore. + * @tc.expected: step8. Return OK. + */ + Key sizeKey; + Value sizeValue; + DistributedDBToolsUnitTest::GetRandomKeyValue(sizeKey, MAX_TEST_KEY_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(sizeValue, MAX_TEST_VAL_SIZE); + EXPECT_EQ(store->PutMetaData(sizeKey, sizeValue), E_OK); + EXPECT_EQ(store->GetMetaData(sizeKey, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, sizeValue), true); + + /** + * @tc.steps:step9/10. Insert data items whose key size is greater than 1 kb + * or value size greater than 4Mb through PutMetaData of NaturalStore. + * @tc.expected: step9/10. Return E_INVALID_ARGS. + */ + sizeKey.push_back(249); // 249 as random size + EXPECT_EQ(store->PutMetaData(sizeKey, sizeValue), -E_INVALID_ARGS); + sizeKey.pop_back(); + sizeValue.push_back(174); // 174 as random size + EXPECT_EQ(store->PutMetaData(sizeKey, sizeValue), -E_INVALID_ARGS); + + /** + * @tc.steps:step11. Delete key1 and key2 successfully. + * @tc.expected: step11. Cannot find key1 and key2 in DB anymore. + */ + EXPECT_EQ(store->DeleteMetaData(std::vector {key1, key2}), E_OK); + EXPECT_EQ(store->GetMetaData(key1, valueRead), -E_NOT_FOUND); + EXPECT_EQ(store->GetMetaData(key2, valueRead), -E_NOT_FOUND); +} + +void DistributedDBStorageSingleVerNaturalStoreTestCase::DataBaseCommonPutOperate(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection, IOption option) +{ + Key key1; + Value value1; + + /** + * @tc.steps: step1/2. Set Ioption to the local data and insert a record of key1 and value1. + * @tc.expected: step1/2. Return OK. + */ + DistributedDBToolsUnitTest::GetRandomKeyValue(key1); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + + /** + * @tc.steps: step3. Set Ioption to the local data and obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step3. The obtained value and value2 are the same. + */ + Value valueRead; + EXPECT_EQ(connection->Get(option, key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value1), true); + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, static_cast(value1.size() + 3)); // 3 more for diff + + /** + * @tc.steps: step4. Ioption Set this parameter to the local data. Insert key1. + * The value cannot be empty. value2(!=value1) + * @tc.expected: step4. Return OK. + */ + EXPECT_EQ(connection->Put(option, key1, value2), E_OK); + + /** + * @tc.steps: step5. Set Ioption to the local data, GetMetaData to obtain the value of key1, + * and check whether the value is the same as the value of value2. + * @tc.expected: step5. The obtained value and value2 are the same. + */ + EXPECT_EQ(connection->Get(option, key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value2), true); + + /** + * @tc.steps: step6. The Ioption parameter is set to the local data. + * The data record whose key is empty and value is not empty is inserted. + * @tc.expected: step6. Return E_INVALID_DATA. + */ + Key emptyKey; + Value emptyValue; + EXPECT_EQ(connection->Put(option, emptyKey, value1), -E_INVALID_ARGS); + + /** + * @tc.steps: step7. Set Ioption to the local data, insert data + * whose key2(!=key1) is not empty, and value is empty. + * @tc.expected: step7. Return OK. + */ + Key key2; + DistributedDBToolsUnitTest::GetRandomKeyValue(key2, static_cast(key1.size() + 1)); + EXPECT_EQ(connection->Put(option, key2, emptyValue), E_OK); + + /** + * @tc.steps: step8. Set option to local data, obtain the value of key2, + * and check whether the value is empty. + * @tc.expected: step8. Return OK, value is empty. + */ + EXPECT_EQ(connection->Get(option, key2, valueRead), E_OK); + EXPECT_EQ(valueRead.empty(), true); + + /** + * @tc.steps: step9. Ioption Set the local data. + * Insert the data whose key size is 1024 and value size is 4Mb. + * @tc.expected: step9. Return OK. + */ + Key sizeKey; + Value sizeValue; + DistributedDBToolsUnitTest::GetRandomKeyValue(sizeKey, MAX_TEST_KEY_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(sizeValue, MAX_TEST_VAL_SIZE); + EXPECT_EQ(connection->Put(option, sizeKey, sizeValue), E_OK); + EXPECT_EQ(connection->Get(option, sizeKey, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, sizeValue), true); + + /** + * @tc.steps: step10/11. Set Ioption to the local data and insert data items + * whose value is greater than 4Mb or key is bigger than 1Kb + * @tc.expected: step10/11. Return E_INVALID_ARGS. + */ + sizeKey.push_back(std::rand()); // random size + EXPECT_EQ(connection->Put(option, sizeKey, sizeValue), -E_INVALID_ARGS); + sizeKey.pop_back(); + sizeValue.push_back(174); // 174 as random size + EXPECT_EQ(connection->Put(option, sizeKey, sizeValue), -E_INVALID_ARGS); +} + +void DistributedDBStorageSingleVerNaturalStoreTestCase::DataBaseCommonDeleteOperate(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection, IOption option) +{ + /** + * @tc.steps: step2. Set Ioption to the local data and delete the data whose key is key1 (empty). + * @tc.expected: step2. Return E_INVALID_ARGS. + */ + Key key1; + EXPECT_EQ(connection->Delete(option, key1), -E_INVALID_ARGS); + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, MAX_TEST_KEY_SIZE + 1); + EXPECT_EQ(connection->Delete(option, key1), -E_INVALID_ARGS); + DistributedDBToolsUnitTest::GetRandomKeyValue(key1); + EXPECT_EQ(connection->Delete(option, key1), E_OK); + + /** + * @tc.steps: step3. Set Ioption to the local data, insert non-null key1, and non-null value1 data. + * @tc.expected: step3. Return E_OK. + */ + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + + /** + * @tc.steps: step4. Set Ioption to the local data, obtain the value of key1, + * and check whether the value is the same as that of value1. + * @tc.expected: step4. Return E_OK. The obtained value is the same as the value of value1. + */ + Value valueRead; + EXPECT_EQ(connection->Get(option, key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value1), true); + + /** + * @tc.steps: step5. Set Ioption to the local data and delete the data whose key is key1. + * @tc.expected: step5. Return E_OK. + */ + EXPECT_EQ(connection->Delete(option, key1), E_OK); + + /** + * @tc.steps: step5. Set Ioption to the local data and obtain the value of Key1. + * @tc.expected: step5. Return E_NOT_FOUND. + */ + EXPECT_EQ(connection->Get(option, key1, valueRead), -E_NOT_FOUND); +} + +void DistributedDBStorageSingleVerNaturalStoreTestCase::DataBaseCommonGetOperate(SQLiteSingleVerNaturalStore *&store, + SQLiteSingleVerNaturalStoreConnection *&connection, IOption option) +{ + /** + * @tc.steps: step2. Set Ioption to the local data and delete the data whose key is key1 (empty). + * @tc.expected: step2. Return E_INVALID_ARGS. + */ + Key key1; + Value valueRead; + // empty key + EXPECT_EQ(connection->Get(option, key1, valueRead), -E_INVALID_ARGS); + + // invalid key + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, MAX_TEST_KEY_SIZE + 1); + EXPECT_EQ(connection->Get(option, key1, valueRead), -E_INVALID_ARGS); + + // non-exist key + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, MAX_TEST_KEY_SIZE); + EXPECT_EQ(connection->Get(option, key1, valueRead), -E_NOT_FOUND); + + /** + * @tc.steps: step3. Set Ioption to the local data, insert non-null key1, and non-null value1 data. + * @tc.expected: step3. Return E_OK. + */ + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + EXPECT_EQ(connection->Put(option, key1, value1), E_OK); + + /** + * @tc.steps: step4. Set Ioption to the local data, obtain the value of key1, + * and check whether the value is the same as that of value1. + * @tc.expected: step4. Return E_OK. The obtained value is the same as the value of value1. + */ + EXPECT_EQ(connection->Get(option, key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value1), true); + + Key key2; + DistributedDBToolsUnitTest::GetRandomKeyValue(key2); + EXPECT_EQ(connection->Get(option, key2, valueRead), -E_NOT_FOUND); + + /** + * @tc.steps: step5. Set Ioption to the local data and obtain the value data of Key1. + * Check whether the value is the same as the value of value2. + * @tc.expected: step4. Return E_OK, and the value is the same as the value of value2. + */ + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, value1.size() + 1); + EXPECT_EQ(connection->Put(option, key1, value2), E_OK); + + /** + * @tc.steps: step5. The Ioption is set to the local. + * The data of the key1 and value2(!=value1) is inserted. + * @tc.expected: step4. Return E_OK. + */ + EXPECT_EQ(connection->Get(option, key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value2), true); +} + +void DistributedDBStorageSingleVerNaturalStoreTestCase::TestMetaDataDeleteByPrefixKey( + SQLiteSingleVerNaturalStore *&store, SQLiteSingleVerNaturalStoreConnection *&connection) +{ + /** + * @tc.steps:step1. Put a1, b1, a2, b2. + * @tc.expected: step1. Return OK. + */ + ASSERT_EQ(store->PutMetaData(Key {'a', '1'}, Value {'a', '1'}), E_OK); + ASSERT_EQ(store->PutMetaData(Key {'b', '1'}, Value {'b', '1'}), E_OK); + ASSERT_EQ(store->PutMetaData(Key {'a', '2'}, Value {'a', '2'}), E_OK); + ASSERT_EQ(store->PutMetaData(Key {'b', '2'}, Value {'b', '2'}), E_OK); + + /** + * @tc.steps:step2. Delete meta data with prefix key 'b'. + * @tc.expected: step2. Return OK. + */ + ASSERT_EQ(store->DeleteMetaDataByPrefixKey(Key {'b'}), E_OK); + ASSERT_EQ(store->DeleteMetaDataByPrefixKey(Key {'c'}), E_OK); + + /** + * @tc.steps:step3. Get a1, b1, a2, b2. + * @tc.expected: step3. Get a1, a2 successfully, and get b1, b2 failed. + */ + Value value; + EXPECT_EQ(store->GetMetaData(Key {'a', '1'}, value), E_OK); + EXPECT_EQ(store->GetMetaData(Key {'a', '2'}, value), E_OK); + EXPECT_EQ(store->GetMetaData(Key {'b', '1'}, value), -E_NOT_FOUND); + EXPECT_EQ(store->GetMetaData(Key {'b', '2'}, value), -E_NOT_FOUND); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.h b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.h new file mode 100644 index 0000000000000000000000000000000000000000..148fd14dc2d647ac2916b456421f5ab66ce6a5aa --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "db_errno.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "multi_ver_natural_store.h" +#include "sqlite_local_kvdb.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "sqlite_utils.h" + +#ifndef DISTRIBUTEDDB_STORAGE_SINGLE_VER_NATURAL_STORE_TESTCASE_H +#define DISTRIBUTEDDB_STORAGE_SINGLE_VER_NATURAL_STORE_TESTCASE_H +struct SyncData { + std::vector hashKey; + std::vector key; + std::vector value; + uint64_t timestamp; + uint64_t flag; + std::string deviceInfo; +}; + +class DistributedDBStorageSingleVerNaturalStoreTestCase final { +public: + DistributedDBStorageSingleVerNaturalStoreTestCase() {}; + ~DistributedDBStorageSingleVerNaturalStoreTestCase() {}; + + static void GetSyncData001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void GetSyncData002(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void GetSyncData003(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void GetSyncData004(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void GetSyncData005(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void GetSyncData006(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void PutSyncData001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void PutSyncData002(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void PutSyncData003(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void PutMetaData001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void GetMetaData001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void DeleteMetaData001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void GetCurrentMaxTimestamp001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void GetCurrentMaxTimestamp002(DistributedDB::SQLiteSingleVerNaturalStore *&store); + + static void LocalDatabaseOperate001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void LocalDatabaseOperate002(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void LocalDatabaseOperate003(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void SyncDatabaseOperate001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void SyncDatabaseOperate002(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void SyncDatabaseOperate003(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void SyncDatabaseOperate004(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void SyncDatabaseOperate005(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void SyncDatabaseOperate006(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void ClearRemoteData001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void DeleteUserKeyValue001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &dbName); + + static void MemoryDbDeleteUserKeyValue001(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &dbName); + + static void DeleteUserKeyValue002(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &dbName); + + static void DeleteUserKeyValue003(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &dbName); + + static void DeleteUserKeyValue004(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &dbName); + + static void MemoryDbDeleteUserKeyValue004(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &dbName); + + static void DeleteUserKeyValue005(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &dbName); + + static void MemoryDbDeleteUserKeyValue005(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &dbName); + + static void DeleteUserKeyValue006(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, const std::string &dbName); + static int GetRawSyncData(const std::string &dbName, const std::string &strSql, std::vector &vecSyncData); + +private: + static void CreateMemDb(DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, int &errCode); + + static bool IsSqlinteExistKey(const std::vector &vecSyncData, const std::vector &key); + + static void TestMetaDataPutAndGet(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void TestMetaDataDeleteByPrefixKey(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection); + + static void DataBaseCommonPutOperate(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, DistributedDB::IOption option); + + static void DataBaseCommonDeleteOperate(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, DistributedDB::IOption option); + + static void DataBaseCommonGetOperate(DistributedDB::SQLiteSingleVerNaturalStore *&store, + DistributedDB::SQLiteSingleVerNaturalStoreConnection *&connection, DistributedDB::IOption option); +}; +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_upgrade_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_upgrade_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b5b311cef001c416e6bb94fbaa266bddbb06ddb3 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_upgrade_test.cpp @@ -0,0 +1,588 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "distributeddb_tools_unit_test.h" +#include "iprocess_system_api_adapter.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.h" +#include "log_print.h" +#include "platform_specific.h" +#include "process_system_api_adapter_impl.h" +#include "runtime_context.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_utils.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + // define some variables to init a KvStoreDelegateManager object. + KvStoreDelegateManager g_mgr("app0", "user0"); + enum ForkConcurrentStatus : int { + NOT_RUN = 0, + RUNNING, + FINISHED, + }; + string g_testDir; + KvStoreConfig g_config; + string g_identifier; + string g_databaseName; + string g_newDatabaseName; + string g_localdatabaseName; + Value g_origValue = {'c', 'e'}; + string g_flag = "2"; + CipherPassword g_passwd; + int g_forkconcurrent = ForkConcurrentStatus::NOT_RUN; + static std::shared_ptr g_adapter; + string g_maindbPath; + string g_metadbPath; + string g_cachedbPath; + DBStatus g_valueStatus = INVALID_ARGS; + Value g_value; + auto g_valueCallback = bind(&DistributedDBToolsUnitTest::ValueCallback, + placeholders::_1, placeholders::_2, std::ref(g_valueStatus), std::ref(g_value)); + + // define the g_kvNbDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; + const std::vector ORIG_DATABASE_V1 = { + "CREATE TABLE IF NOT EXISTS local_data(key BLOB PRIMARY KEY NOT NULL, value BLOB);", + "CREATE TABLE IF NOT EXISTS sync_data(key BLOB NOT NULL, value BLOB, timestamp INT NOT NULL," \ + "flag INT NOT NULL, device BLOB, ori_device BLOB, hash_key BLOB PRIMARY KEY NOT NULL);", + "CREATE TABLE IF NOT EXISTS meta_data(key BLOB PRIMARY KEY NOT NULL, value BLOB);", + "CREATE INDEX IF NOT EXISTS key_index ON sync_data (key);", + "CREATE INDEX IF NOT EXISTS time_index ON sync_data (timestamp);", + "CREATE INDEX IF NOT EXISTS dev_index ON sync_data (device);", + "PRAGMA user_version=101;" + }; + + const std::vector ORIG_DATABASE_V2 = { + "CREATE TABLE IF NOT EXISTS local_data(key BLOB PRIMARY KEY NOT NULL," \ + "value BLOB, timestamp INT, hash_key BLOB);", + "CREATE TABLE IF NOT EXISTS sync_data(key BLOB NOT NULL, value BLOB, timestamp INT NOT NULL," \ + "flag INT NOT NULL, device BLOB, ori_device BLOB, hash_key BLOB PRIMARY KEY NOT NULL, w_timestamp INT);", + "CREATE TABLE IF NOT EXISTS meta_data(key BLOB PRIMARY KEY NOT NULL, value BLOB);", + "CREATE INDEX IF NOT EXISTS key_index ON sync_data (key, flag);", + "CREATE INDEX IF NOT EXISTS time_index ON sync_data (timestamp);", + "CREATE INDEX IF NOT EXISTS dev_index ON sync_data (device);", + "CREATE INDEX IF NOT EXISTS local_hashkey_index ON local_data (hash_key);", + "PRAGMA user_version=102;" + }; + + const std::vector ORIG_DATABASE_V3 = { + "CREATE TABLE IF NOT EXISTS local_data(key BLOB PRIMARY KEY NOT NULL, value BLOB, " \ + "timestamp INT, hash_key BLOB);", + "CREATE TABLE IF NOT EXISTS sync_data(key BLOB NOT NULL, value BLOB, timestamp INT NOT NULL," \ + "flag INT NOT NULL, device BLOB, ori_device BLOB, hash_key BLOB PRIMARY KEY NOT NULL, w_timestamp INT);", + "CREATE INDEX IF NOT EXISTS key_index ON sync_data (key, flag);", + "CREATE INDEX IF NOT EXISTS time_index ON sync_data (timestamp);", + "CREATE INDEX IF NOT EXISTS dev_index ON sync_data (device);", + "CREATE INDEX IF NOT EXISTS local_hashkey_index ON local_data (hash_key);", + "PRAGMA user_version=103;" + }; + + const std::vector INSERT_DATA_V1 = { + "INSERT INTO sync_data VALUES('ab', 'cd', 100, 2, '', '', 'efdef');" \ + }; + + const std::string INSERT_LOCAL_DATA_V1 = { + "INSERT INTO local_data VALUES(?, 'ce');" + }; + + const std::vector INSERT_DATA_V2 = { + "INSERT INTO sync_data VALUES('ab', 'cd', 100, " + g_flag + ", '', '', 'efdef', 100);" + }; + + const std::string INSERT_LOCAL_DATA_V2 = { + "INSERT INTO local_data VALUES(?, 'ce',3169633545069981070,'efdef');" + }; + + const std::string INSERT_META_DATA_V2 = { + "INSERT INTO meta_data VALUES('ab', 'ce');" + }; + + const std::string CHECK_V1_SYNC_UPGRADE = + "SELECT w_timestamp, timestamp FROM sync_data;"; + + const std::string CHECK_V2_SYNC_UPGRADE = + "SELECT flag FROM sync_data;"; + + const std::string CHECK_V1_LOCAL_UPGRADE = + "SELECT timestamp, hash_key FROM local_data;"; + + void KvStoreNbDelegateCallback( + DBStatus statusSrc, KvStoreNbDelegate *kvStoreSrc, DBStatus &statusDst, KvStoreNbDelegate *&kvStoreDst) + { + statusDst = statusSrc; + kvStoreDst = kvStoreSrc; + } + + auto g_kvNbDelegateCallback = bind(&KvStoreNbDelegateCallback, placeholders::_1, + placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + + void CreateDatabase(const std::vector &insertSqls, const std::string &insertLocalDataSql, + const OpenDbProperties &property) + { + sqlite3 *db = nullptr; + EXPECT_EQ(SQLiteUtils::OpenDatabase(property, db), E_OK); + ASSERT_NE(db, nullptr); + + for (const auto &item : insertSqls) { + ASSERT_EQ(SQLiteUtils::ExecuteRawSQL(db, item), E_OK); + } + sqlite3_stmt *statement = nullptr; + ASSERT_EQ(SQLiteUtils::GetStatement(db, insertLocalDataSql, statement), E_OK); + ASSERT_NE(statement, nullptr); + EXPECT_EQ(SQLiteUtils::BindBlobToStatement(statement, 1, g_origValue, false), E_OK); + EXPECT_EQ(SQLiteUtils::StepWithRetry(statement, false), -SQLITE_DONE); + EXPECT_EQ(sqlite3_finalize(statement), SQLITE_OK); + + (void)sqlite3_close_v2(db); + } + + void CheckSyncDataV1ToV2(sqlite3 *db) + { + sqlite3_stmt *statement = nullptr; + ASSERT_EQ(SQLiteUtils::GetStatement(db, CHECK_V1_SYNC_UPGRADE, statement), E_OK); + ASSERT_NE(statement, nullptr); + ASSERT_EQ(SQLiteUtils::StepWithRetry(statement), SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)); + ASSERT_EQ(sqlite3_column_int64(statement, 0), sqlite3_column_int64(statement, 1)); + ASSERT_EQ(sqlite3_finalize(statement), SQLITE_OK); + } + + void CheckSyncDataV2ToV3(sqlite3 *db) + { + sqlite3_stmt *statement = nullptr; + ASSERT_EQ(SQLiteUtils::GetStatement(db, CHECK_V2_SYNC_UPGRADE, statement), E_OK); + ASSERT_NE(statement, nullptr); + ASSERT_EQ(SQLiteUtils::StepWithRetry(statement), SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)); + long int targetFlagValue = sqlite3_column_int64(statement, 0); + ASSERT_EQ(targetFlagValue, stol(g_flag)); + ASSERT_EQ(sqlite3_finalize(statement), SQLITE_OK); + } + + void CheckLocalDataV1ToV2(sqlite3 *db) + { + sqlite3_stmt *statement = nullptr; + ASSERT_EQ(SQLiteUtils::GetStatement(db, CHECK_V1_LOCAL_UPGRADE, statement), E_OK); + ASSERT_NE(statement, nullptr); + ASSERT_EQ(SQLiteUtils::StepWithRetry(statement), SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)); + Timestamp stamp = static_cast(sqlite3_column_int64(statement, 0)); + EXPECT_NE(stamp, 0UL); + + Value readHashValue; + Value calcValue; + EXPECT_EQ(DBCommon::CalcValueHash(g_origValue, calcValue), E_OK); + ASSERT_EQ(SQLiteUtils::GetColumnBlobValue(statement, 1, readHashValue), E_OK); + EXPECT_EQ(readHashValue, calcValue); + ASSERT_EQ(sqlite3_finalize(statement), SQLITE_OK); + } + + void CheckDirectoryV2ToV3(bool expectedValue, bool expecteMetaDbExist) + { + std::string identifier = DBCommon::TransferStringToHex(g_identifier); + std::string newDatabaseName = g_testDir + "/" + identifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::MAINDB_DIR + "/" + DBConstant::SINGLE_VER_DATA_STORE + ".db"; + std::string newMetadatabaseName = g_testDir + "/" + identifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::METADB_DIR + "/" + DBConstant::SINGLE_VER_META_STORE + ".db"; + std::string newCacheDirectory = g_testDir + "/" + identifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::CACHEDB_DIR + "/"; + EXPECT_EQ(OS::CheckPathExistence(newDatabaseName), expectedValue); + EXPECT_EQ(OS::CheckPathExistence(newMetadatabaseName), expecteMetaDbExist); + EXPECT_EQ(OS::CheckPathExistence(newCacheDirectory), expectedValue); + } + + void CheckVersionV3(sqlite3 *db) + { + int version = SOFTWARE_VERSION_BASE; + SQLiteUtils::GetVersion(db, version); + EXPECT_EQ(version, SINGLE_VER_STORE_VERSION_CURRENT); + } + + void CheckSecOpt(const SecurityOption ¤tSecOpt) + { + SecurityOption checkSecOpt; + SecurityOption currentMetaSecOpt {SecurityLabel::S2, SecurityFlag::ECE}; + int errCode = RuntimeContext::GetInstance()->GetSecurityOption(g_maindbPath, checkSecOpt); + EXPECT_TRUE(currentSecOpt == checkSecOpt); + EXPECT_TRUE(errCode == E_OK); + if (OS::CheckPathExistence(g_cachedbPath)) { + errCode = RuntimeContext::GetInstance()->GetSecurityOption(g_cachedbPath, checkSecOpt); + EXPECT_TRUE(currentSecOpt == checkSecOpt); + EXPECT_TRUE(errCode == E_OK); + } + if (OS::CheckPathExistence(g_metadbPath)) { + errCode = RuntimeContext::GetInstance()->GetSecurityOption(g_metadbPath, checkSecOpt); + EXPECT_TRUE(currentMetaSecOpt == checkSecOpt); + EXPECT_TRUE(errCode == E_OK); + } + } + + void GetKvStoreProcess(const KvStoreNbDelegate::Option &option, bool putCheck, bool secOptCheck, + const SecurityOption &secopt) + { + Key keyTmp = {'1'}; + Value valueRead; + Value value = {'7'}; + g_mgr.GetKvStore("TestUpgradeNb", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + if (secOptCheck) { + CheckSecOpt(secopt); + } + if (putCheck) { + EXPECT_TRUE(g_kvNbDelegatePtr->Put(keyTmp, value) == OK); + EXPECT_TRUE(g_kvNbDelegatePtr->Get(keyTmp, valueRead) == OK); + } + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + } +} + +class DistributedDBStorageSingleVerUpgradeTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageSingleVerUpgradeTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + std::string oriIdentifier = "user0-app0-TestUpgradeNb"; + g_identifier = DBCommon::TransferHashString(oriIdentifier); + std::string identifier = DBCommon::TransferStringToHex(g_identifier); + g_databaseName = "/" + identifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + g_newDatabaseName = "/" + identifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + DBConstant::MAINDB_DIR + "/" + + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + g_localdatabaseName = "/" + identifier + "/" + DBConstant::LOCAL_SUB_DIR + "/" + + DBConstant::LOCAL_DATABASE_NAME + DBConstant::SQLITE_DB_EXTENSION; + const int passwdLen = 5; + const int passwdVal = 1; + vector passwdBuffer1(passwdLen, passwdVal); + int errCode = g_passwd.SetValue(passwdBuffer1.data(), passwdBuffer1.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + g_adapter = std::make_shared(); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); + g_maindbPath = g_testDir + "/" + identifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + DBConstant::MAINDB_DIR + + "/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + g_metadbPath = g_testDir + "/" + identifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + DBConstant::METADB_DIR + + "/" + DBConstant::SINGLE_VER_META_STORE + DBConstant::SQLITE_DB_EXTENSION; + g_cachedbPath = g_testDir + "/" + identifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + DBConstant::CACHEDB_DIR + + "/" + DBConstant::SINGLE_VER_CACHE_STORE + DBConstant::SQLITE_DB_EXTENSION; +} + +void DistributedDBStorageSingleVerUpgradeTest::TearDownTestCase(void) +{ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); +} + +void DistributedDBStorageSingleVerUpgradeTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + std::string identifier = DBCommon::TransferStringToHex(g_identifier); + DBCommon::CreateDirectory(g_testDir + "/" + identifier); + DBCommon::CreateDirectory(g_testDir + "/" + identifier + "/" + DBConstant::SINGLE_SUB_DIR); + DBCommon::CreateDirectory(g_testDir + "/" + identifier + "/" + DBConstant::LOCAL_SUB_DIR); +} + +void DistributedDBStorageSingleVerUpgradeTest::TearDown(void) +{ + while (g_forkconcurrent == ForkConcurrentStatus::RUNNING) { + sleep(1); + } + g_adapter->ResetSecOptDic(); + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } +} + +/** + * @tc.name: UpgradeTest001 + * @tc.desc: Test the NbDelegate upgrade from the old version V1. + * @tc.type: FUNC + * @tc.require: AR000DPTQ7 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSingleVerUpgradeTest, UpgradeTest001, TestSize.Level2) +{ + /** + * @tc.steps:step1. create old version V1 db. + */ + std::string dbPath = g_testDir + g_databaseName; + OpenDbProperties property = {dbPath, true, false, ORIG_DATABASE_V1}; + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); + SecurityOption secopt{SecurityLabel::S3, SecurityFlag::SECE}; + CreateDatabase(INSERT_DATA_V1, INSERT_LOCAL_DATA_V1, property); + bool isDatabaseExists = OS::CheckPathExistence(dbPath); + EXPECT_EQ(isDatabaseExists, true); + /** + * @tc.steps:step2. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + option.secOption = secopt; + GetKvStoreProcess(option, true, true, SecurityOption()); + + sqlite3 *db = nullptr; + dbPath = g_testDir + g_newDatabaseName; + property = {dbPath, true, false}; + EXPECT_EQ(SQLiteUtils::OpenDatabase(property, db), E_OK); + ASSERT_NE(db, nullptr); + /** + * @tc.steps:step3. check result is ok. + * @tc.expected: dir is ok,version is ok. + */ + CheckLocalDataV1ToV2(db); + CheckSyncDataV1ToV2(db); + CheckDirectoryV2ToV3(true, false); + CheckVersionV3(db); + (void)sqlite3_close_v2(db); + EXPECT_EQ(g_mgr.DeleteKvStore("TestUpgradeNb"), OK); +} + +/** + * @tc.name: UpgradeTest002 + * @tc.desc: Test the NbDelegate upgrade from the old version V2. + * @tc.type: FUNC + * @tc.require: AR000DPTQ7 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBStorageSingleVerUpgradeTest, UpgradeTest002, TestSize.Level2) +{ + /** + * @tc.steps:step1. create old version V2 db. + */ + std::string dbPath = g_testDir + g_databaseName; + OpenDbProperties property = {dbPath, true, false, ORIG_DATABASE_V2}; + CreateDatabase(INSERT_DATA_V2, INSERT_LOCAL_DATA_V2, property); + SecurityOption secopt{SecurityLabel::S3, SecurityFlag::SECE}; + bool isDatabaseExists = OS::CheckPathExistence(dbPath); + EXPECT_EQ(isDatabaseExists, true); + /** + * @tc.steps:step2. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + option.secOption = secopt; + GetKvStoreProcess(option, true, true, SecurityOption()); + + sqlite3 *db = nullptr; + dbPath = g_testDir + g_newDatabaseName; + property = {dbPath, true, false}; + EXPECT_EQ(SQLiteUtils::OpenDatabase(property, db), E_OK); + ASSERT_NE(db, nullptr); + /** + * @tc.steps:step3. check result is ok. + * @tc.expected: dir is ok,version is ok. + */ + CheckDirectoryV2ToV3(true, false); + CheckVersionV3(db); + (void)sqlite3_close_v2(db); + GetKvStoreProcess(option, false, true, SecurityOption()); + EXPECT_EQ(g_mgr.DeleteKvStore("TestUpgradeNb"), OK); +} +#ifndef OMIT_JSON +/** + * @tc.name: UpgradeTest003 + * @tc.desc: Test the NbDelegate upgrade from the old version V2 with schema. + * @tc.type: FUNC + * @tc.require: AR000DPTQ7 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBStorageSingleVerUpgradeTest, UpgradeTest003, TestSize.Level2) +{ + /** + * @tc.steps:step1. create old version V2 db. + */ + std::string dbPath = g_testDir + g_databaseName; + OpenDbProperties property = {dbPath, true, false, ORIG_DATABASE_V2}; + std::string val = "{\"field_name1\":true, \"field_name2\":{\"field_name3\":1, \"field_name4\":1, \"field_name5\":1,\ + \"field_name6\":\"1\", \"field_name7\":null, \"field_name8\":null}}"; + std::string insertValueSql = "INSERT INTO sync_data VALUES('ab', '"; + insertValueSql += val; + insertValueSql += "', 100, " + g_flag + ", '', '', 'efdef', 100);"; + CreateDatabase(std::vector {insertValueSql}, INSERT_LOCAL_DATA_V2, property); + SecurityOption secopt{SecurityLabel::S3, SecurityFlag::SECE}; + bool isDatabaseExists = OS::CheckPathExistence(dbPath); + EXPECT_EQ(isDatabaseExists, true); + /** + * @tc.steps:step2. Get the nb delegate. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + option.secOption = secopt; + option.schema = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":{" + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, NOT NULL, DEFAULT '3.1415'\"," + "\"field_name7\":[]," + "\"field_name8\":{}" + "}" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2.field_name6\"]}"; + GetKvStoreProcess(option, false, true, SecurityOption()); + + sqlite3 *db = nullptr; + dbPath = g_testDir + g_newDatabaseName; + property = {dbPath, true, false}; + EXPECT_EQ(SQLiteUtils::OpenDatabase(property, db), E_OK); + ASSERT_NE(db, nullptr); + /** + * @tc.steps:step3. check result is ok. + * @tc.expected: dir is ok,version is ok. + */ + CheckDirectoryV2ToV3(true, false); + CheckVersionV3(db); + (void)sqlite3_close_v2(db); + + GetKvStoreProcess(option, false, true, SecurityOption()); + EXPECT_EQ(g_mgr.DeleteKvStore("TestUpgradeNb"), OK); +} +#endif +/** + * @tc.name: UpgradeTest004 + * @tc.desc: Test the NbDelegate upgrade from the old version V2 while secOption from NOT_SET to S3SECE. + * @tc.type: FUNC + * @tc.require: AR000DPTQ7 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBStorageSingleVerUpgradeTest, UpgradeTest004, TestSize.Level2) +{ + /** + * @tc.steps:step1. create old version V2 db. + */ + std::string dbPath = g_testDir + g_databaseName; + OpenDbProperties property = {dbPath, true, false, ORIG_DATABASE_V2}; + CreateDatabase(INSERT_DATA_V2, INSERT_LOCAL_DATA_V2, property); + SecurityOption secopt{SecurityLabel::S3, SecurityFlag::SECE}; + SecurityOption checkSecOpt; + bool isDatabaseExists = OS::CheckPathExistence(dbPath); + EXPECT_EQ(isDatabaseExists, true); + /** + * @tc.steps:step2. Get the nb delegate without secoption and Get the nb delegate again with secoption. + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + GetKvStoreProcess(option, false, true, SecurityOption()); + RuntimeContext::GetInstance()->GetSecurityOption(g_maindbPath, checkSecOpt); + EXPECT_TRUE(checkSecOpt.securityLabel == NOT_SET); + + option.secOption = secopt; + GetKvStoreProcess(option, true, true, SecurityOption()); + + sqlite3 *db = nullptr; + dbPath = g_testDir + g_newDatabaseName; + property = {dbPath, true, false}; + EXPECT_EQ(SQLiteUtils::OpenDatabase(property, db), E_OK); + ASSERT_NE(db, nullptr); + /** + * @tc.steps:step3. check result is ok. + * @tc.expected: dir is ok,version is ok. + */ + CheckDirectoryV2ToV3(true, false); + CheckVersionV3(db); + (void)sqlite3_close_v2(db); + + GetKvStoreProcess(option, false, false, secopt); + EXPECT_EQ(g_mgr.DeleteKvStore("TestUpgradeNb"), OK); +} + +HWTEST_F(DistributedDBStorageSingleVerUpgradeTest, UpgradeTest005, TestSize.Level2) +{ + /** + * @tc.steps:step1. create old version V2 db. + */ + std::string dbPath = g_testDir + g_databaseName; + OpenDbProperties property = {dbPath, true, false, ORIG_DATABASE_V2}; + CreateDatabase(INSERT_DATA_V2, INSERT_LOCAL_DATA_V2, property); + SecurityOption secopt = {SecurityLabel::S2, SecurityFlag::ECE}; + bool isDatabaseExists = OS::CheckPathExistence(dbPath); + EXPECT_EQ(isDatabaseExists, true); + /** + * @tc.steps:step2. Get the nb delegate while not sprite meta_db scene + * @tc.expected: step1. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + option.secOption = secopt; + GetKvStoreProcess(option, true, true, SecurityOption()); + + sqlite3 *db = nullptr; + dbPath = g_testDir + g_newDatabaseName; + property = {dbPath, true, false}; + EXPECT_EQ(SQLiteUtils::OpenDatabase(property, db), E_OK); + ASSERT_NE(db, nullptr); + /** + * @tc.steps:step3. check result is ok. + * @tc.expected: dir is ok,version is ok. + */ + CheckSyncDataV2ToV3(db); + CheckDirectoryV2ToV3(true, false); + CheckVersionV3(db); + (void)sqlite3_close_v2(db); + EXPECT_EQ(g_mgr.DeleteKvStore("TestUpgradeNb"), OK); +} + +HWTEST_F(DistributedDBStorageSingleVerUpgradeTest, UpgradeTest006, TestSize.Level2) +{ + /** + * @tc.steps:step1. create old version V2 db. + */ + std::string dbPath = g_testDir + g_databaseName; + OpenDbProperties property = {dbPath, true, false, ORIG_DATABASE_V2}; + CreateDatabase(INSERT_DATA_V2, INSERT_LOCAL_DATA_V2, property); + SecurityOption secopt = {SecurityLabel::S3, SecurityFlag::ECE}; + bool isDatabaseExists = OS::CheckPathExistence(dbPath); + EXPECT_EQ(isDatabaseExists, true); + /** + * @tc.steps:step2. Get the nb delegate while not sprite meta_db scene + * @tc.expected: step2. Get results OK and non-null delegate. + */ + KvStoreNbDelegate::Option option = {true, false, false}; + option.secOption = secopt; + GetKvStoreProcess(option, true, true, SecurityOption()); + + sqlite3 *db = nullptr; + dbPath = g_testDir + g_newDatabaseName; + property = {dbPath, true, false}; + EXPECT_EQ(SQLiteUtils::OpenDatabase(property, db), E_OK); + ASSERT_NE(db, nullptr); + /** + * @tc.steps:step3. check result is ok. + * @tc.expected: dir is ok,version is ok. + */ + CheckSyncDataV2ToV3(db); + CheckDirectoryV2ToV3(true, false); + CheckVersionV3(db); + (void)sqlite3_close_v2(db); + EXPECT_EQ(g_mgr.DeleteKvStore("TestUpgradeNb"), OK); +} diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_sqlite_single_ver_natural_store_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_sqlite_single_ver_natural_store_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51150eab7eb29c4bb010fa016dfff306c14f6d70 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_sqlite_single_ver_natural_store_test.cpp @@ -0,0 +1,1081 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_constant.h" +#include "db_common.h" +#include "distributeddb_storage_single_ver_natural_store_testcase.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + DistributedDB::KvStoreConfig g_config; + + std::string g_testDir; + std::string g_databaseName; + std::string g_identifier; + + DistributedDB::SQLiteSingleVerNaturalStore *g_store = nullptr; + DistributedDB::SQLiteSingleVerNaturalStoreConnection *g_connection = nullptr; +} + +class DistributedDBStorageSQLiteSingleVerNaturalStoreTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageSQLiteSingleVerNaturalStoreTest::SetUpTestCase(void) +{} + +void DistributedDBStorageSQLiteSingleVerNaturalStoreTest::TearDownTestCase(void) {} + +void DistributedDBStorageSQLiteSingleVerNaturalStoreTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + LOGD("DistributedDBStorageSQLiteSingleVerNaturalStoreTest dir is %s", g_testDir.c_str()); + std::string oriIdentifier = APP_ID + "-" + USER_ID + "-" + "TestGeneralNB"; + std::string identifier = DBCommon::TransferHashString(oriIdentifier); + std::string g_identifier = DBCommon::TransferStringToHex(identifier); + + g_databaseName = "/" + g_identifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + DBConstant::MAINDB_DIR + "/" + + DBConstant::SINGLE_VER_DATA_STORE + ".db"; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/" + g_identifier + "/" + DBConstant::SINGLE_SUB_DIR); + KvDBProperties property; + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, "TestGeneralNB"); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, g_identifier); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + + g_store = new (std::nothrow) SQLiteSingleVerNaturalStore; + ASSERT_NE(g_store, nullptr); + ASSERT_EQ(g_store->Open(property), E_OK); + + int erroCode = E_OK; + g_connection = static_cast(g_store->GetDBConnection(erroCode)); + ASSERT_NE(g_connection, nullptr); + g_store->DecObjRef(g_store); + EXPECT_EQ(erroCode, E_OK); +} + +void DistributedDBStorageSQLiteSingleVerNaturalStoreTest::TearDown(void) +{ + if (g_connection != nullptr) { + g_connection->Close(); + } + + g_store = nullptr; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/" + g_identifier + "/" + + DBConstant::SINGLE_SUB_DIR); +} + +/** + * @tc.name: GetSyncData001 + * @tc.desc: To test the function of querying the data in the time stamp range in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, GetSyncData001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, C) interface of the NaturalStore, where AB + * @tc.expected: step1. The value of GetSyncData is E_INVALID_ARG. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData003(g_store, g_connection); +} + +/** + * @tc.name: GetSyncData004 + * @tc.desc: To the test database Subcon reading, a large number of data records exist in the time stamp range. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, GetSyncData004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, B) interface of the NaturalStore. + * @tc.expected: step1. Return E_GET_UNFINISHED. + */ + /** + * @tc.steps:step2. Continue to obtain data through the GetSyncDataNext() interface + * of the NaturalStore until the E_GET_FINISHED message is returned. + * @tc.expected: step2. When the GetSyncDataNext returns E_GET_FINISHED, + * the total number of obtained data is the number of inserted data and the data is consistent. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData004(g_store, g_connection); +} + +/** + * @tc.name: GetSyncData005 + * @tc.desc: In the test database, if a large number of data records exist + * in the time stamp range, a packet is read successfully. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, GetSyncData005, TestSize.Level1) +{ + /** + * @tc.steps:step1. Obtain the data within the time stamp range + * through the GetSyncData(A, B) interface of the NaturalStore. + * @tc.expected: step1. The total size of all data in OK, dataItems is 99K. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData005(g_store, g_connection); +} + +/** + * @tc.name: GetSyncData006 + * @tc.desc: To test the function of reading data when the time stamp range in the database + * is greater than the value of blockSize. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, GetSyncData006, TestSize.Level1) +{ + /** + * @tc.steps:step1. Use the GetSyncData(A, B) interface of the NaturalStore + * and set blockSize to 50 kb to obtain the data within the time stamp range. + * @tc.expected: step1. The system returns E_GET_FINISHED. The size of the obtained data is 1 kb. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetSyncData006(g_store, g_connection); +} + +/** + * @tc.name: PutSyncData001 + * @tc.desc: To test the function of synchronizing the new data of the remote device that synchronizes the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, PutSyncData001, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Set Ioption to synchronous data and insert a (key1, value1) data record by put interface. + */ + /** + * @tc.steps:step3. Insert a (key1, value2!=value1, timestamp, false) data record + * through the PutSyncData interface. The value of timestamp is less than or equal + * to the value of timestamp. For Compare the timestamp to determine whether to synchronization data. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. The Ioption is set to synchronize data + * through the Get interface to obtain the value data of the key1. + * @tc.expected: step4. Return OK.The obtained value is value1. + */ + /** + * @tc.steps:step5. Insert a (key1, value3!=value1, timestamp, false) data record + * through the PutSyncData interface of the NaturalStore. The value of timestamp + * is greater than that of timestamp inserted in 2. + * @tc.expected: step5. Return OK. + */ + /** + * @tc.steps:step6. The Ioption is set to synchronize data through the Get interface + * to obtain the value data of the key1. + * @tc.expected: step6. Return OK. + */ + /** + * @tc.steps:step7. Insert a (key2, value4) data record through the PutSyncData interface. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps:step8. The Ioption is set to synchronize data + * through the Get interface to obtain the value data of the key2. + * @tc.expected: step8. Returns OK, and the obtained data is value4. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::PutSyncData001(g_store, g_connection); +} + +/** + * @tc.name: PutSyncData002 + * @tc.desc: To test the function of synchronizing data from the remote device + * to the local device after the data is deleted from the remote device. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, PutSyncData002, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Set Ioption to synchronous data and insert a (key1, value1) data record by put interface. + */ + /** + * @tc.steps:step3. Insert a (key1, value2!=value1, timestamp, false) data record + * through the PutSyncData interface. The value of timestamp is less than or equal + * to the value of timestamp. For Compare the timestamp to determine whether delete data. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. The Ioption is set to synchronize data + * through the Get interface to obtain the value data of the key1. + * @tc.expected: step4. Return OK.The obtained value is value1. + */ + /** + * @tc.steps:step5. Insert a (key1, value3!=value1, timestamp, false) data record + * through the PutSyncData interfac. The value of timestamp + * is greater than that of timestamp inserted in step2. + * @tc.expected: step5. Return OK. + */ + /** + * @tc.steps:step6. The Ioption is set to synchronize data through the Get interface + * to obtain the value data of the key1. + * @tc.expected: step6. Return E_NOT_FOUND. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::PutSyncData002(g_store, g_connection); +} + +/** + * @tc.name: PutSyncData003 + * @tc.desc: To test the function of synchronizing the mixed data of the added + * and deleted data from the remote device to the local device. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, PutSyncData003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Insert a data record (key1,value1 is not null) and (key2, value2 is not null) + * through the PutSyncData interface. + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. Set Ioption as the synchronization data to obtain the data of key1 and key2. + * @tc.expected: step2. The Get interface returns OK. The value of key1 is value1, + * and the value of key2 is value2. + */ + /** + * @tc.steps:step3. Insert a (key3, value3) and delete the data of the (key1, value1). + * @tc.expected: step3. The PutSyncData returns OK. + */ + /** + * @tc.steps:step4. Set Ioption to the synchronization data and obtain the data of key1, key2, and key3. + * @tc.expected: step4. Get key1 returns E_NOT_FOUND,Get key2. + * The value of OK,value is value2, the value of Get key3 is OK, + * and the value of value is value3. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::PutSyncData003(g_store, g_connection); +} + +/** + * @tc.name: PutMetaData001 + * @tc.desc: Test metadata insertion and modification. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, PutMetaData001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step2. The obtained value is the same as the value of value1. + */ + /** + * @tc.steps:step3. The key value is key1, the value is not empty, + * and the value of value2 is different from the value of value1 through the PutMetaData interface. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. Run the GetMetaData command to obtain the value of key1 + * and check whether the value is the same as the value of value2. + * @tc.expected: step4. The obtained value is the same as the value of value2. + */ + /** + * @tc.steps:step5. Use PutMetaData to insert a record whose key is empty and value is not empty. + * @tc.expected: step5. Return E_INVALID_ARGS. + */ + /** + * @tc.steps:step6. Use PutMetaData in NaturalStore to insert data whose key2(!=key1) + * is not empty and value is empty. + * @tc.expected: step6. Return OK. + */ + /** + * @tc.steps:step7. Obtain the value of key2 and check whether the value is empty. + * @tc.expected: step7. The obtained value is empty. + */ + /** + * @tc.steps:step8. Insert the data whose key size is 1024 and value size is 4Mb + * through PutMetaData of NaturalStore. + * @tc.expected: step8. Return OK. + */ + /** + * @tc.steps:step9/10. Insert data items whose key size is greater than 1 kb + * or value size greater than 4Mb through PutMetaData of NaturalStore. + * @tc.expected: step9/10. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::PutMetaData001(g_store, g_connection); +} + +/** + * @tc.name: GetMetaData001 + * @tc.desc: To test the function of reading the metadata of a key in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, GetMetaData001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. Run the PutMetaData command to insert a non-empty key1 non-empty value1 data record. + * @tc.expected: step2. The obtained value is the same as the value of value1. + */ + /** + * @tc.steps:step3. The key value is key1, the value is not empty, + * and the value of value2 is different from the value of value1 through the PutMetaData interface. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps:step4. Run the GetMetaData command to obtain the value of key1 + * and check whether the value is the same as the value of value2. + * @tc.expected: step4. The obtained value is the same as the value of value2. + */ + /** + * @tc.steps:step5. Use PutMetaData to insert a record whose key is empty and value is not empty. + * @tc.expected: step5. Return E_INVALID_ARGS. + */ + /** + * @tc.steps:step6. Use PutMetaData in NaturalStore to insert data whose key2(!=key1) + * is not empty and value is empty. + * @tc.expected: step6. Return OK. + */ + /** + * @tc.steps:step7. Obtain the value of key2 and check whether the value is empty. + * @tc.expected: step7. The obtained value is empty. + */ + /** + * @tc.steps:step8. Insert the data whose key size is 1024 and value size is 4Mb + * through PutMetaData of NaturalStore. + * @tc.expected: step8. Return OK. + */ + /** + * @tc.steps:step9/10. Insert data items whose key size is greater than 1 kb + * or value size greater than 4Mb through PutMetaData of NaturalStore. + * @tc.expected: step9/10. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetMetaData001(g_store, g_connection); +} + +/** + * @tc.name: DeleteMetaData001 + * @tc.desc: * @tc.name: To test the function of deleting the metadata with prefix key in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, DeleteMetaData001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Put a1, b1, a2, b2. + * @tc.expected: step1. Return OK. + */ + /** + * @tc.steps:step2. Delete meta data with prefix key 'b'. + * @tc.expected: step2. Return OK. + */ + /** + * @tc.steps:step3. Get a1, b1, a2, b2. + * @tc.expected: step3. Get a1, a2 successfully, and get b1, b2 failed. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteMetaData001(g_store, g_connection); +} + + +/** + * @tc.name: GetCurrentMaxTimestamp001 + * @tc.desc: To test the function of obtaining the maximum timestamp when a record exists in the database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, GetCurrentMaxTimestamp001, TestSize.Level1) +{ + /** + * @tc.steps:step1/2. Insert a data record into the synchronization database. + */ + /** + * @tc.steps:step3. The current maximum timestamp is A. + */ + /** + * @tc.steps:step4. Insert a data record into the synchronization database. + */ + /** + * @tc.steps:step5. Obtain the maximum timestamp B and check whether B>=A exists. + * @tc.expected: step5. The obtained timestamp is B>=A. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetCurrentMaxTimestamp001(g_store, g_connection); +} + +/** + * @tc.name: GetCurrentMaxTimestamp002 + * @tc.desc: Obtain the maximum timestamp when no record exists in the test record library. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, GetCurrentMaxTimestamp002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Obtains the maximum timestamp in the current database record. + * @tc.expected: step1. Return timestamp is 0. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::GetCurrentMaxTimestamp002(g_store); +} + +/** + * @tc.name: LocalDatabaseOperate001 + * @tc.desc: Test the function of inserting data in the local database of the NaturalStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, LocalDatabaseOperate001, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Set Ioption to the local data and insert a record of key1 and value1. + * @tc.expected: step1/2. Return OK. + */ + /** + * @tc.steps: step3. Set Ioption to the local data and obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step3. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step4. Ioption Set this parameter to the local data. Insert key1. + * The value cannot be empty. value2(!=value1) + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data, GetMetaData to obtain the value of key1, + * and check whether the value is the same as the value of value2. + * @tc.expected: step5. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step6. The Ioption parameter is set to the local data. + * The data record whose key is empty and value is not empty is inserted. + * @tc.expected: step6. Return E_INVALID_DATA. + */ + /** + * @tc.steps: step7. Set Ioption to the local data, insert data + * whose key2(!=key1) is not empty, and value is empty. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps: step8. Set option to local data, obtain the value of key2, + * and check whether the value is empty. + * @tc.expected: step8. Return OK, value is empty. + */ + /** + * @tc.steps: step9. Ioption Set the local data. + * Insert the data whose key size is 1024 and value size is 4Mb. + * @tc.expected: step9. Return OK. + */ + /** + * @tc.steps: step10/11. Set Ioption to the local data and insert data items + * whose value is greater than 4Mb or key is bigger than 1Kb + * @tc.expected: step10/11. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::LocalDatabaseOperate001(g_store, g_connection); +} + +/** + * @tc.name: LocalDatabaseOperate002 + * @tc.desc: Test the function of deleting data from the local database of the NaturalStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, LocalDatabaseOperate002, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Set Ioption to the local data and insert a record of key1 and value1. + * @tc.expected: step1/2. Return OK. + */ + /** + * @tc.steps: step3. Set Ioption to the local data and obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step3. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step4. Ioption Set this parameter to the local data. Insert key1. + * The value cannot be empty. value2(!=value1) + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data, GetMetaData to obtain the value of key1, + * and check whether the value is the same as the value of value2. + * @tc.expected: step5. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step6. The Ioption parameter is set to the local data. + * The data record whose key is empty and value is not empty is inserted. + * @tc.expected: step6. Return E_INVALID_DATA. + */ + /** + * @tc.steps: step7. Set Ioption to the local data, insert data + * whose key2(!=key1) is not empty, and value is empty. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps: step8. Set option to local data, obtain the value of key2, + * and check whether the value is empty. + * @tc.expected: step8. Return OK, value is empty. + */ + /** + * @tc.steps: step9. Ioption Set the local data. + * Insert the data whose key size is 1024 and value size is 4Mb. + * @tc.expected: step9. Return OK. + */ + /** + * @tc.steps: step10/11. Set Ioption to the local data and insert data items + * whose value is greater than 4Mb or key is bigger than 1Kb + * @tc.expected: step10/11. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::LocalDatabaseOperate002(g_store, g_connection); +} + +/** + * @tc.name: LocalDatabaseOperate003 + * @tc.desc: To test the function of reading data from the local database of the NaturalStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, LocalDatabaseOperate003, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Set Ioption to the local data and insert a record of key1 and value1. + * @tc.expected: step1/2. Return OK. + */ + /** + * @tc.steps: step3. Set Ioption to the local data and obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step3. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step4. Ioption Set this parameter to the local data. Insert key1. + * The value cannot be empty. value2(!=value1) + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data, GetMetaData to obtain the value of key1, + * and check whether the value is the same as the value of value2. + * @tc.expected: step5. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step6. The Ioption parameter is set to the local data. + * The data record whose key is empty and value is not empty is inserted. + * @tc.expected: step6. Return E_INVALID_DATA. + */ + /** + * @tc.steps: step7. Set Ioption to the local data, insert data + * whose key2(!=key1) is not empty, and value is empty. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps: step8. Set option to local data, obtain the value of key2, + * and check whether the value is empty. + * @tc.expected: step8. Return OK, value is empty. + */ + /** + * @tc.steps: step9. Ioption Set the local data. + * Insert the data whose key size is 1024 and value size is 4Mb. + * @tc.expected: step9. Return OK. + */ + /** + * @tc.steps: step10/11. Set Ioption to the local data and insert data items + * whose value is greater than 4Mb or key is bigger than 1Kb + * @tc.expected: step10/11. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::LocalDatabaseOperate003(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate001 + * @tc.desc: To test the function of inserting data of the local device in the synchronization database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, SyncDatabaseOperate001, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Set Ioption to the local data and insert a record of key1 and value1. + * @tc.expected: step1/2. Return OK. + */ + /** + * @tc.steps: step3. Set Ioption to the local data and obtain the value of key1. + * Check whether the value is the same as the value of value1. + * @tc.expected: step3. The obtained value and value2 are the same. + */ + /** + * @tc.steps: step4. Ioption Set this parameter to the local data. Insert key1. + * The value cannot be empty. value2(!=value1) + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data, GetMetaData to obtain the value of key1, + * and check whether the value is the same as the value of value2. + * @tc.expected: step5. The obtained and value2 are the same. + */ + /** + * @tc.steps: step6. The Ioption parameter is set to the local data. + * The data record whose key is empty and value is not empty is inserted. + * @tc.expected: step6. Return E_INVALID_DATA. + */ + /** + * @tc.steps: step7. Set Ioption to the local data, insert data + * whose key2(!=key1) is not empty, and value is empty. + * @tc.expected: step7. Return OK. + */ + /** + * @tc.steps: step8. Set option to local data, obtain the value of key2, + * and check whether the value is empty. + * @tc.expected: step8. Return OK, value is empty. + */ + /** + * @tc.steps: step9. Ioption Set the local data. + * Insert the data whose key size is 1024 and value size is 4Mb. + * @tc.expected: step9. Return OK. + */ + /** + * @tc.steps: step10/11. Set Ioption to the local data and insert data items + * whose value is greater than 4Mb or key is bigger than 1Kb + * @tc.expected: step10/11. Return E_INVALID_ARGS. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate001(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate002 + * @tc.desc: test the put operation after data synced from other devices. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, SyncDatabaseOperate002, TestSize.Level1) +{ + /** + * @tc.steps: step1/2. Add a remote synchronization data record. (key1, value1). + */ + /** + * @tc.steps: step3. Ioption is set to synchronous data. Obtains the value data of the key1. + * @tc.expected: step3. Return OK. The value is the same as the value of value1. + */ + /** + * @tc.steps: step4. Ioption Set the data to be synchronized and insert the data of key1,value2. + * @tc.expected: step4. Return OK. + */ + /** + * @tc.steps: step3. Ioption is set to synchronous data. Obtains the value data of the key1. + * @tc.expected: step3. Return OK. The value is the same as the value of value2. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate002(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate003 + * @tc.desc: test the delete operation in sync database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, SyncDatabaseOperate003, TestSize.Level1) +{ + /** + * @tc.steps: step2. Set Ioption to the local data and delete the data whose key is key1 (empty). + * @tc.expected: step2. Return E_INVALID_ARGS. + */ + /** + * @tc.steps: step3. Set Ioption to the local data, insert non-null key1, and non-null value1 data. + * @tc.expected: step3. Return E_OK. + */ + /** + * @tc.steps: step4. Set Ioption to the local data, obtain the value of key1, + * and check whether the value is the same as that of value1. + * @tc.expected: step4. Return E_OK. The obtained value is the same as the value of value1. + */ + /** + * @tc.steps: step5. Set Ioption to the local data and delete the data whose key is key1. + * @tc.expected: step5. Return E_OK. + */ + /** + * @tc.steps: step5. Set Ioption to the local data and obtain the value of Key1. + * @tc.expected: step5. Return E_NOT_FOUND. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate003(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate004 + * @tc.desc: test the delete for the data from other devices in sync database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, SyncDatabaseOperate004, TestSize.Level1) +{ + /** + * @tc.steps: step2. The Ioption parameter is set to synchronize data to obtain the value data of the key1. + * @tc.expected: step2. Return OK. The value is the same as the value of value1. + */ + /** + * @tc.steps: step3. The Ioption parameter is set to synchronize data, and the key1 data is deleted. + * @tc.expected: step3. Return OK. + */ + /** + * @tc.steps: step4. The Ioption parameter is set to synchronize data to obtain the value data of the key1. + * @tc.expected: step4. Return E_NOT_FOUND. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate004(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate005 + * @tc.desc: test the reading for sync database. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, SyncDatabaseOperate005, TestSize.Level1) +{ + /** + * @tc.steps: step2. Set Ioption to the local data and delete the data whose key is key1 (empty). + * @tc.expected: step2. Return E_INVALID_ARGS. + */ + /** + * @tc.steps: step3. Set Ioption to the local data, insert non-null key1, and non-null value1 data. + * @tc.expected: step3. Return E_OK. + */ + /** + * @tc.steps: step4. Set Ioption to the local data, obtain the value of key1, + * and check whether the value is the same as that of value1. + * @tc.expected: step4. Return E_OK. The obtained value is the same as the value of value1. + */ + /** + * @tc.steps: step5. Set Ioption to the local data and obtain the value data of Key1. + * Check whether the value is the same as the value of value2. + * @tc.expected: step4. Return E_OK, and the value is the same as the value of value2. + */ + /** + * @tc.steps: step5. The Ioption is set to the local. + * The data of the key1 and value2(!=value1) is inserted. + * @tc.expected: step4. Return E_OK. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate005(g_store, g_connection); +} + +/** + * @tc.name: SyncDatabaseOperate006 + * @tc.desc: test the get entries for sync database + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, SyncDatabaseOperate006, TestSize.Level1) +{ + /** + * @tc.steps: step2/3/4. Set Ioption to synchronous data. + * Insert the data of key=keyPrefix + 'a', value1. + * Insert the data of key=keyPrefix + 'c', value2. + * Insert the data of key length=keyPrefix length - 1, value3. + * @tc.expected: step2/3/4. Return E_NOT_FOUND. + */ + /** + * @tc.steps: step5. Obtain all data whose prefixKey is keyPrefix. + * @tc.expected: step5. Return OK. The number of obtained data records is 2. + */ + /** + * @tc.steps: step6. Obtain all data whose prefixKey is empty. + * @tc.expected: step6. Return OK. The number of obtained data records is 3. + */ + /** + * @tc.steps: step7. Obtain all data whose prefixKey is keyPrefix. + * @tc.expected: step7. Return E_NOT_SUPPORT. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::SyncDatabaseOperate006(g_store, g_connection); +} + +/** + * @tc.name: ClearRemoteData001 + * @tc.desc: test the clear data synced from the remote by device. + * @tc.type: FUNC + * @tc.require: AR000CIFDA AR000CQS3T + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, ClearRemoteData001, TestSize.Level1) +{ + /** + * @tc.steps: step1. New data is inserted to the B end of the device. [keyB, valueB]. + */ + /** + * @tc.steps: step2. The device pulls the data of the device B, and the device inserts the [keyA, valueA]. + */ + /** + * @tc.steps: step3. The device obtains the data of keyA and valueB. + * @tc.expected: step3. Obtain [keyA, valueA] and [keyB, valueB]. + */ + /** + * @tc.steps: step4.Invoke the interface for clearing the synchronization data of the B device. + */ + /** + * @tc.steps: step5. The device obtains the data of keyA and valueB. + * @tc.expected: step5. The value of [keyA, valueA] is obtained, + * and the value of NOT_FOUND is obtained by querying keyB. + */ + DistributedDBStorageSingleVerNaturalStoreTestCase::ClearRemoteData001(g_store, g_connection); +} + +/** + * @tc.name: DeleteUserKeyValue001 + * @tc.desc: When a user deletes a data record, the system clears the user record. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, DeleteUserKeyValue001, TestSize.Level1) +{ + /** + * @tc.steps: step1. delete K1. + * @tc.expected: step1. delete K1 successfully. + */ + /** + * @tc.steps: step2. Real query by sqlite3. + * @tc.expected: step2. Find KEY_1, not find K2. + */ + const std::string url = g_testDir + g_databaseName; + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue001(g_store, g_connection, url); +} + +/** + * @tc.name: DeleteUserKeyValue002 + * @tc.desc: After the synchronization library data is deleted locally, add the same key data locally. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, DeleteUserKeyValue002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Delete key1 data via Delete interface. + * @tc.expected: step1. Delete successfully. + */ + /** + * @tc.steps: step2. New data from key1, value3 via Put interface. + * @tc.expected: step2. New data from key1, value3 via Put interface successfully. + */ + /** + * @tc.steps: step3. Query key1 data via Get interface. + * @tc.expected: step3. Query key1 data via Get interface successfully, get value3 by key1. + */ + /** + * @tc.steps: step4. Query key1 real data by sqlite3. + * @tc.expected: step4. Two records were found. + */ + const std::string url = g_testDir + g_databaseName; + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue002(g_store, g_connection, url); +} + +/** + * @tc.name: DeleteUserKeyValue003 + * @tc.desc: After the synchronization database data is deleted locally, the same key data is added from the remote end. + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, DeleteUserKeyValue003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Delete data by key1. + * @tc.expected: step1. Delete successfully. + */ + /** + * @tc.steps: step2. Get data by key1. + * @tc.expected: step1. Key1 not exist in database. + */ + /** + * @tc.steps: step3. Get a new data from remote device B , key1, value3, + * with a smaller timestamp than the current timestamp. + */ + /** + * @tc.steps: step4. Get data by key1. + * @tc.expected: step4. Key1 not exist in database. + */ + /** + * @tc.steps: step5. Get a new data from remote device C , key1, value4, + * and the timestamp is larger than the current timestamp. + */ + /** + * @tc.steps: step6. Get data by key1. + * @tc.expected: step6. Key1 not exist in database. + */ + /** + * @tc.steps: step7. Get real data by key1. + * @tc.expected: step7. Get 1 record. + */ + const std::string url = g_testDir + g_databaseName; + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue003(g_store, g_connection, url); +} + +/** + * @tc.name: DeleteUserKeyValue004 + * @tc.desc: Changes in key after remote delete data syncs to local + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, DeleteUserKeyValue004, TestSize.Level1) +{ + /** + * @tc.steps: step1 2 3. Synchronize data to another device B; delete key1 data from device B; + * pull the action of key1 to local. + */ + /** + * @tc.steps: step4. Close database. + */ + /** + * @tc.steps: step5 6. Get real data by key1;and get the number of records. + * @tc.expected: step5 6. Not exist key1 real data in database;Get 1 record. + */ + const std::string url = g_testDir + g_databaseName; + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue004(g_store, g_connection, url); +} + +/** + * @tc.name: DeleteUserKeyValue005 + * @tc.desc: New unified key data locally after remote delete data syncs to local + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, DeleteUserKeyValue005, TestSize.Level1) +{ + /** + * @tc.steps: step1 2 3. Synchronize data to another device B; delete key1 data from device B; + * pull the action of key1 to local. + */ + /** + * @tc.steps: step4. Put K1 V1 to database. + * @tc.expected: step4. Put successfully. + */ + /** + * @tc.steps: step5. Close database. + */ + /** + * @tc.steps: step6 7. Get real data by key1;and get the number of records. + * @tc.expected: step6 7. Not exist key1 real data in database;Get 2 record. + */ + const std::string url = g_testDir + g_databaseName; + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue005(g_store, g_connection, url); +} + +/** + * @tc.name: DeleteUserKeyValue006 + * @tc.desc: After the remote delete data is synced to the local, + * the same key data is added from the remote other devices + * @tc.type: FUNC + * @tc.require: AR000CKRTC AR000CQE0D + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBStorageSQLiteSingleVerNaturalStoreTest, DeleteUserKeyValue006, TestSize.Level1) +{ + /** + * @tc.steps: step1. Remote device B sync deletes data key1 and pushes to local. + */ + /** + * @tc.steps: step2. Get key1 from database. + * @tc.expected: step2. Not exist key1. + */ + /** + * @tc.steps: step3. Remote device C syncs new data (key1, value2), + * timestamp is less than delete timestamp, to local. + */ + /** + * @tc.steps: step4. Get key1 from database. + * @tc.expected: step4. Not exist key1. + */ + /** + * @tc.steps: step5. Remote device C syncs new data (key1, value2), + * timestamp is bigger than delete timestamp, to local. + */ + /** + * @tc.steps: step6. Get key1 from database. + * @tc.expected: step6. Exist key1. + */ + /** + * @tc.steps: step7. Get real data from database. + * @tc.expected: step7. Get 1 record. + */ + const std::string url = g_testDir + g_databaseName; + DistributedDBStorageSingleVerNaturalStoreTestCase::DeleteUserKeyValue006(g_store, g_connection, url); +} + diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_subscribe_query_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_subscribe_query_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d7fc916285e55aba0ee2e19f6fe2fd033e72046 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_subscribe_query_test.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "db_common.h" +#include "db_errno.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "generic_single_ver_kv_entry.h" +#include "kvdb_manager.h" +#include "process_communicator_test_stub.h" +#include "process_system_api_adapter_impl.h" +#include "query_sync_object.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { +DistributedDB::KvStoreConfig g_config; +std::string g_testDir; +string g_resourceDir; + +KvStoreDelegateManager g_mgr(APP_ID, USER_ID); +// define the g_kvDelegateCallback, used to get some information when open a kv store. +DBStatus g_kvDelegateStatus = INVALID_ARGS; +KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr; +auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvNbDelegatePtr)); + +const uint8_t PRESET_DATA_SIZE = 2; +const std::string SUBSCRIBE_ID = "680A20600517073AE306B11FEA8306C57DC5102CD33E322F7C513176AA707F0C"; + +const std::string REMOTE_DEVICE_ID = "remote_device_id"; +const std::string REMOTE_DEVICE_A = "remote_device_A"; +const std::string REMOTE_DEVICE_B = "remote_device_B"; +const Key PREFIX_KEY = { 'k' }; +const Key KEY1 = { 'k', '1' }; +const Key KEY2 = { 'k', '2' }; +const Key KEY3 = { 'k', '3' }; +const Value VALUE1 = { 'v', '1' }; +const Value VALUE2 = { 'v', '2' }; +const Value VALUE3 = { 'v', '3' }; + +const std::string NORMAL_FBS_FILE_NAME = "normal_fbs.bfbs"; +const string SCHEMA_STRING = + "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"BOOL\"," + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, DEFAULT '3.1415'\"," + "\"field_name7\":\"LONG, DEFAULT 100\"," + "\"field_name8\":\"LONG, DEFAULT 100\"," + "\"field_name9\":\"LONG, DEFAULT 100\"," + "\"field_name10\":\"LONG, DEFAULT 100\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2\"]}"; + +void PreSetData(uint8_t dataNum) +{ + EXPECT_GE(dataNum, 0); // 0 No preset data + EXPECT_LT(dataNum, 128); // 128 Max preset data size + for (uint8_t i = 0; i < dataNum; i++) { + Key keyA = {'K', i}; + Value value; + std::string validJsonData; + if (i % 2 == 0) { // 2 : for data construct + validJsonData = R"({"field_name1":false,"field_name2":true,"field_name3":100})"; + } else { + validJsonData = R"({"field_name1":false,"field_name2":false,"field_name3":100})"; + } + value.assign(validJsonData.begin(), validJsonData.end()); + EXPECT_EQ(g_kvNbDelegatePtr->Put(keyA, value), E_OK); + } +} + +void CreateAndGetStore(const std::string &storeId, const std::string &schemaString, + SQLiteSingleVerNaturalStoreConnection *&conn, SQLiteSingleVerNaturalStore *&store, uint8_t preSetDataNum = 0) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = schemaString; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + PreSetData(preSetDataNum); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + + std::string oriIdentifier = USER_ID + "-" + APP_ID + "-" + storeId; + std::string identifier = DBCommon::TransferHashString(oriIdentifier); + KvDBProperties property; + property.SetStringProp(KvDBProperties::IDENTIFIER_DATA, identifier); + std::string identifierHex = DBCommon::TransferStringToHex(identifier); + property.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + property.SetStringProp(KvDBProperties::STORE_ID, storeId); + property.SetBoolProp(KvDBProperties::MEMORY_MODE, false); + property.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + property.SetStringProp(KvDBProperties::IDENTIFIER_DIR, identifierHex); + property.SetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, ConflictResolvePolicy::LAST_WIN); + + if (!schemaString.empty()) { + SchemaObject schemaObj; + schemaObj.ParseFromSchemaString(schemaString); + EXPECT_EQ(schemaObj.IsSchemaValid(), true); + property.SetSchema(schemaObj); + } + + int errCode = E_OK; + conn = static_cast(KvDBManager::GetDatabaseConnection(property, errCode)); + EXPECT_EQ(errCode, E_OK); + ASSERT_NE(conn, nullptr); + store = static_cast(KvDBManager::OpenDatabase(property, errCode)); + EXPECT_EQ(errCode, E_OK); + ASSERT_NE(store, nullptr); +} + +#ifndef OMIT_FLATBUFFER +std::string FbfFileToSchemaString(const std::string &fileName) +{ + std::string filePath = g_resourceDir + "fbs_files_for_ut/" + fileName; + std::ifstream is(filePath, std::ios::binary | std::ios::ate); + if (!is.is_open()) { + LOGE("[FbfFileToSchemaString] open file failed name : %s", filePath.c_str()); + return ""; + } + + auto size = is.tellg(); + LOGE("file size %u", static_cast(size)); + std::string schema(size, '\0'); + is.seekg(0); + if (is.read(&schema[0], size)) { + return schema; + } + LOGE("[FbfFileToSchemaString] read file failed path : %s", filePath.c_str()); + return ""; +} +#endif + +void CheckDataNumByKey(const std::string &storeId, const Key& key, size_t expSize) +{ + KvStoreNbDelegate::Option option = {true, false, false}; + option.schema = SCHEMA_STRING; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + EXPECT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + std::vector entries; + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(key, entries), E_OK); + EXPECT_TRUE(entries.size() == expSize); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); +} +} + +class DistributedDBStorageSubscribeQueryTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp() override; + void TearDown() override; +}; + +static std::shared_ptr g_adapter; +void DistributedDBStorageSubscribeQueryTest::SetUpTestCase(void) +{ + g_mgr.SetProcessLabel("DistributedDBStorageSubscribeQueryTest", "test"); + g_mgr.SetProcessCommunicator(std::make_shared()); // export and import get devID + + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + ASSERT_EQ(DistributedDBToolsUnitTest::GetResourceDir(g_resourceDir), E_OK); + LOGD("Test dir is %s", g_testDir.c_str()); + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/TestQuerySync/" + DBConstant::SINGLE_SUB_DIR); + + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + g_adapter = std::make_shared(); + EXPECT_TRUE(g_adapter != nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); +} + +void DistributedDBStorageSubscribeQueryTest::TearDownTestCase(void) +{ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); +} + +void DistributedDBStorageSubscribeQueryTest::SetUp() +{ + Test::SetUp(); + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBStorageSubscribeQueryTest::TearDown() +{ + Test::TearDown(); + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +/** + * @tc.name: CheckAndInitQueryCondition001 + * @tc.desc: Check the condition is legal or not with json schema + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageSubscribeQueryTest, CheckAndInitQueryCondition001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create a json schema db, get the natural store instance. + * @tc.expected: step1. Get results OK and non-null store. + */ + SQLiteSingleVerNaturalStoreConnection *conn = nullptr; + SQLiteSingleVerNaturalStore *store = nullptr; + CreateAndGetStore("SchemaCondition01", SCHEMA_STRING, conn, store); + + /** + * @tc.steps:step2. Create a query with prefixKey only, check it as condition. + * @tc.expected: step2. Check condition return E_OK. + */ + Query query1 = Query::Select().PrefixKey({}); + QueryObject queryObject1(query1); + int errCode = store->CheckAndInitQueryCondition(queryObject1); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps:step3. Create a query with predicate, check it as condition. + * @tc.expected: step3. Check condition return E_OK. + */ + Query query2 = Query::Select().GreaterThan("field_name3", 10); + QueryObject queryObject2(query2); + errCode = store->CheckAndInitQueryCondition(queryObject2); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps:step4. Create a query with invalid field, check it as condition. + * @tc.expected: step4. Check condition return E_INVALID_QUERY_FIELD. + */ + Query query3 = Query::Select().GreaterThan("field_name11", 10); + QueryObject queryObject3(query3); + errCode = store->CheckAndInitQueryCondition(queryObject3); + EXPECT_EQ(errCode, -E_INVALID_QUERY_FIELD); + + /** + * @tc.steps:step5. Create a query with invalid format, check it as condition. + * @tc.expected: step5. Check condition return E_INVALID_QUERY_FORMAT. + */ + Query query4 = Query::Select().GreaterThan("field_name3", 10).And().BeginGroup(). + LessThan("field_name3", 100).OrderBy("field_name3").EndGroup(); + QueryObject queryObject4(query4); + errCode = store->CheckAndInitQueryCondition(queryObject4); + EXPECT_EQ(errCode, -E_INVALID_QUERY_FORMAT); + + /** + * @tc.steps:step6. Close natural store + * @tc.expected: step6. Close ok + */ + RefObject::KillAndDecObjRef(store); + KvDBManager::ReleaseDatabaseConnection(conn); +} + +#ifndef OMIT_FLATBUFFER +/** + * @tc.name: CheckAndInitQueryCondition002 + * @tc.desc: Check the condition always illegal with flatbuffer schema + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageSubscribeQueryTest, CheckAndInitQueryCondition002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create a flatbuffer schema db, get the natural store instance. + * @tc.expected: step1. Get results OK and non-null store. + */ + std::string fbSchema = FbfFileToSchemaString(NORMAL_FBS_FILE_NAME); + EXPECT_FALSE(fbSchema.empty()); + SQLiteSingleVerNaturalStoreConnection *conn = nullptr; + SQLiteSingleVerNaturalStore *store = nullptr; + CreateAndGetStore("SchemaCondition02", fbSchema, conn, store); + + /** + * @tc.steps:step2. Create a query, check it as condition. + * flatbuffer schema is not support with querySync and subscribe. + * @tc.expected: step2. Check condition return E_NOT_SUPPORT. + */ + Query query1 = Query::Select().PrefixKey({}); + QueryObject queryObject1(query1); + int errCode = store->CheckAndInitQueryCondition(queryObject1); + EXPECT_EQ(errCode, -E_NOT_SUPPORT); + + /** + * @tc.steps:step3. Close natural store + * @tc.expected: step3. Close ok + */ + RefObject::KillAndDecObjRef(store); + KvDBManager::ReleaseDatabaseConnection(conn); +} +#endif + +/** + * @tc.name: CheckAndInitQueryCondition003 + * @tc.desc: Check the condition always illegal with flatbuffer schema + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageSubscribeQueryTest, CheckAndInitQueryCondition003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create a kv db, get the natural store instance. + * @tc.expected: step1. Get results OK and non-null store. + */ + SQLiteSingleVerNaturalStoreConnection *conn = nullptr; + SQLiteSingleVerNaturalStore *store = nullptr; + CreateAndGetStore("SchemaCondition03", "", conn, store); + + /** + * @tc.steps:step2. Create a prefixKey query, check it as condition. + * @tc.expected: step2. Check condition return E_OK. + */ + Query query1 = Query::Select().PrefixKey({}); + QueryObject queryObject1(query1); + int errCode = store->CheckAndInitQueryCondition(queryObject1); + EXPECT_EQ(errCode, E_OK); + + /** + * @tc.steps:step2. Create a predicate query, check it as condition. + * @tc.expected: step2. Check condition return E_NOT_SUPPORT. + */ + Query query2 = Query::Select().GreaterThan("field_name3", 10); + QueryObject queryObject2(query2); + errCode = store->CheckAndInitQueryCondition(queryObject2); + EXPECT_EQ(errCode, -E_NOT_SUPPORT); + + /** + * @tc.steps:step3. Close natural store + * @tc.expected: step3. Close ok + */ + RefObject::KillAndDecObjRef(store); + KvDBManager::ReleaseDatabaseConnection(conn); +} + +/** + * @tc.name: PutSyncDataTestWithQuery + * @tc.desc: put remote devices sync data(get by query sync or subscribe) with query. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageSubscribeQueryTest, PutSyncDataTestWithQuery, TestSize.Level1) +{ + /** + * @tc.steps:step1. create and open a schema store, preset some data; + * @tc.expected: step1. open success + */ + const std::string storeId = "PutSyncData01"; + SQLiteSingleVerNaturalStoreConnection *conn = nullptr; + SQLiteSingleVerNaturalStore *store = nullptr; + CreateAndGetStore(storeId, SCHEMA_STRING, conn, store, PRESET_DATA_SIZE); + + /** + * @tc.steps:step2. Construct sync data + * @tc.expected: OK + */ + Key key; + Value value; + Timestamp now = store->GetCurrentTimestamp(); + LOGD("now time is : %ld", now); + std::vector data; + for (uint8_t i = 0; i < PRESET_DATA_SIZE; i++) { + DataItem item{key, value, now, DataItem::REMOTE_DEVICE_DATA_MISS_QUERY, REMOTE_DEVICE_ID, now}; + item.key.clear(); + DBCommon::CalcValueHash({'K', i}, item.key); + EXPECT_EQ(item.key.empty(), false); + data.push_back(item); + } + + /** + * @tc.steps:step3. put sync data with query + * @tc.expected: step3. data put success + */ + Query query = Query::Select().EqualTo("field_name2", true); + QueryObject queryObj(query); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, data, REMOTE_DEVICE_ID, queryObj), E_OK); + + RefObject::KillAndDecObjRef(store); + KvDBManager::ReleaseDatabaseConnection(conn); + + /** + * @tc.steps:step4. Check sync data + * @tc.expected: step4. check data ok + */ + CheckDataNumByKey(storeId, {'K'}, PRESET_DATA_SIZE / 2); +} + +/** + * @tc.name: PutSyncDataTestWithQuery002 + * @tc.desc: put remote devices sync data(timestamp is smaller then DB data) with query. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageSubscribeQueryTest, PutSyncDataTestWithQuery002, TestSize.Level1) +{ + /** + * @tc.steps:step1. create and open a schema store, preset some data; + * @tc.expected: step1. open success + */ + const std::string storeId = "PutSyncData02"; + + SQLiteSingleVerNaturalStoreConnection *conn = nullptr; + SQLiteSingleVerNaturalStore *store = nullptr; + CreateAndGetStore(storeId, SCHEMA_STRING, conn, store); + + Key key({'K', 'e', 'y'}); + Value value; + Timestamp now = store->GetCurrentTimestamp(); + /** + * @tc.steps:step2. put sync data + * @tc.expected: OK + */ + std::string validJsonData(R"({"field_name1":false,"field_name2":true,"field_name3":100})"); + value.assign(validJsonData.begin(), validJsonData.end()); + std::vector data; + DataItem item{key, value, now, DataItem::LOCAL_FLAG, REMOTE_DEVICE_ID, now}; + data.push_back(item); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, data, REMOTE_DEVICE_ID), E_OK); + + /** + * @tc.steps:step3. put sync miss query data with smaller timestamp + * @tc.expected: OK + */ + data.clear(); + value.clear(); + DataItem itemMiss{key, value, now - 1, DataItem::REMOTE_DEVICE_DATA_MISS_QUERY, REMOTE_DEVICE_ID, now - 1}; + itemMiss.key.clear(); + DBCommon::CalcValueHash({'K', 'e', 'y'}, itemMiss.key); + EXPECT_EQ(itemMiss.key.empty(), false); + data.push_back(itemMiss); + Query query = Query::Select().EqualTo("field_name2", true); + QueryObject queryObj(query); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, data, REMOTE_DEVICE_ID, queryObj), E_OK); + + RefObject::KillAndDecObjRef(store); + KvDBManager::ReleaseDatabaseConnection(conn); + + /** + * @tc.steps:step4. Check sync data + * @tc.expected: check data ok, data {key} is not erased. + */ + CheckDataNumByKey(storeId, {'K', 'e', 'y'}, 1); +} + +/** + * @tc.name: PutSyncDataTestWithQuery003 + * @tc.desc: put remote devices sync data(with same timestamp in DB data but different devices) with query. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageSubscribeQueryTest, PutSyncDataTestWithQuery003, TestSize.Level1) +{ + /** + * @tc.steps:step1. create and open a schema store, preset some data; + * @tc.expected: step1. open success + */ + const std::string storeId = "PutSyncData03"; + + SQLiteSingleVerNaturalStoreConnection *conn = nullptr; + SQLiteSingleVerNaturalStore *store = nullptr; + CreateAndGetStore(storeId, SCHEMA_STRING, conn, store); + + Key key({'K', 'e', 'y'}); + Value value; + Timestamp now = store->GetCurrentTimestamp(); + /** + * @tc.steps:step2. put sync data + * @tc.expected: OK + */ + std::string validJsonData(R"({"field_name1":false,"field_name2":true,"field_name3":100})"); + value.assign(validJsonData.begin(), validJsonData.end()); + std::vector data; + DataItem item{key, value, now, DataItem::LOCAL_FLAG, REMOTE_DEVICE_ID, now}; + data.push_back(item); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, data, REMOTE_DEVICE_ID), E_OK); + + /** + * @tc.steps:step3. put sync miss query data with same timestamp + * @tc.expected: OK + */ + data.clear(); + DataItem itemMiss{key, {}, now, DataItem::REMOTE_DEVICE_DATA_MISS_QUERY, REMOTE_DEVICE_ID, now}; + itemMiss.key.clear(); + DBCommon::CalcValueHash({'K', 'e', 'y'}, itemMiss.key); + EXPECT_EQ(itemMiss.key.empty(), false); + data.push_back(itemMiss); + Query query = Query::Select().EqualTo("field_name2", true); + QueryObject queryObj(query); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, data, REMOTE_DEVICE_ID, queryObj), E_OK); + + RefObject::KillAndDecObjRef(store); + KvDBManager::ReleaseDatabaseConnection(conn); + + /** + * @tc.steps:step4. Check sync data + * @tc.expected: check data ok, data {key} is not erased. + */ + CheckDataNumByKey(storeId, {'K', 'e', 'y'}, 1); +} + +/** + * @tc.name: PutSyncDataTestWithQuery004 + * @tc.desc: put remote devices sync data(with same timestamp in DB data but different devices) with query. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageSubscribeQueryTest, PutSyncDataTestWithQuery004, TestSize.Level1) +{ + /** + * @tc.steps:step1. create and open a schema store, preset some data; + * @tc.expected: step1. open success + */ + const std::string storeId = "PutSyncData04"; + SQLiteSingleVerNaturalStoreConnection *conn = nullptr; + SQLiteSingleVerNaturalStore *store = nullptr; + CreateAndGetStore(storeId, SCHEMA_STRING, conn, store); + + Key key({'K', 'e', 'y'}); + Value value; + Timestamp now = store->GetCurrentTimestamp(); + /** + * @tc.steps:step2. put sync data + * @tc.expected: OK + */ + std::string validJsonData(R"({"field_name1":false,"field_name2":true,"field_name3":100})"); + value.assign(validJsonData.begin(), validJsonData.end()); + std::vector data; + DataItem item{key, value, now, DataItem::LOCAL_FLAG, REMOTE_DEVICE_A, now}; + data.push_back(item); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, data, REMOTE_DEVICE_ID), E_OK); + + /** + * @tc.steps:step3. put sync miss query data with same timestamp + * @tc.expected: OK + */ + data.clear(); + DataItem itemMiss{key, {}, now, DataItem::REMOTE_DEVICE_DATA_MISS_QUERY, REMOTE_DEVICE_B, now}; + itemMiss.key.clear(); + DBCommon::CalcValueHash({'K', 'e', 'y'}, itemMiss.key); + EXPECT_EQ(itemMiss.key.empty(), false); + data.push_back(itemMiss); + Query query = Query::Select().EqualTo("field_name2", true); + QueryObject queryObj(query); + EXPECT_EQ(DistributedDBToolsUnitTest::PutSyncDataTest(store, data, REMOTE_DEVICE_B, queryObj), E_OK); + + RefObject::KillAndDecObjRef(store); + KvDBManager::ReleaseDatabaseConnection(conn); + + /** + * @tc.steps:step4. Check sync data + * @tc.expected: check data ok, data {key} is not erased. + */ + CheckDataNumByKey(storeId, {'K', 'e', 'y'}, 1); +} + +/** + * @tc.name: AddSubscribeTest001 + * @tc.desc: Add subscribe with query + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageSubscribeQueryTest, AddSubscribeTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create a json schema db, get the natural store instance. + * @tc.expected: Get results OK and non-null store. + */ + SQLiteSingleVerNaturalStoreConnection *conn = nullptr; + SQLiteSingleVerNaturalStore *store = nullptr; + CreateAndGetStore("SubscribeTest01", SCHEMA_STRING, conn, store); + + std::vector queryList; + queryList.push_back(Query::Select().PrefixKey({10, 20})); + queryList.push_back(Query::Select().EqualTo("field_name3", 30)); + queryList.push_back(Query::Select().NotEqualTo("field_name3", 30)); + queryList.push_back(Query::Select().GreaterThan("field_name3", 10)); + queryList.push_back(Query::Select().LessThan("field_name3", 30)); + queryList.push_back(Query::Select().GreaterThanOrEqualTo("field_name3", 30)); + queryList.push_back(Query::Select().LessThanOrEqualTo("field_name3", 30)); + queryList.push_back(Query::Select().Like("field_name6", "Abc%")); + queryList.push_back(Query::Select().NotLike("field_name6", "Asd%")); + std::vector set = {1, 2, 3, 4}; + queryList.push_back(Query::Select().In("field_name3", set)); + queryList.push_back(Query::Select().NotIn("field_name3", set)); + queryList.push_back(Query::Select().IsNull("field_name4")); + queryList.push_back(Query::Select().IsNotNull("field_name5")); + queryList.push_back(Query::Select().EqualTo("field_name3", 30).And().EqualTo("field_name1", true)); + queryList.push_back(Query::Select().EqualTo("field_name3", 30).Or().EqualTo("field_name1", true)); + queryList.push_back(Query::Select().EqualTo("field_name2", false).Or(). + BeginGroup().EqualTo("field_name3", 30).Or().EqualTo("field_name1", true).EndGroup()); + + /** + * @tc.steps:step2. Add subscribe with query, remove subscribe. + * @tc.expected: success. + */ + for (const auto &query : queryList) { + QueryObject queryObj(query); + EXPECT_EQ(store->AddSubscribe(SUBSCRIBE_ID, queryObj, false), E_OK); + EXPECT_EQ(store->RemoveSubscribe(SUBSCRIBE_ID), E_OK); + } + + /** + * @tc.steps:step6. Close natural store + * @tc.expected: step6. Close ok + */ + RefObject::KillAndDecObjRef(store); + KvDBManager::ReleaseDatabaseConnection(conn); +} + +/** + * @tc.name: AddSubscribeTest002 + * @tc.desc: Add subscribe with same query not failed + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xulianhui + */ +HWTEST_F(DistributedDBStorageSubscribeQueryTest, AddSubscribeTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create a json schema db, get the natural store instance. + * @tc.expected: Get results OK and non-null store. + */ + SQLiteSingleVerNaturalStoreConnection *conn = nullptr; + SQLiteSingleVerNaturalStore *store = nullptr; + CreateAndGetStore("SubscribeTest02", SCHEMA_STRING, conn, store); + + Query query = Query::Select().EqualTo("field_name2", false).Or(). + BeginGroup().EqualTo("field_name3", 30).Or().EqualTo("field_name1", true).EndGroup(); + /** + * @tc.steps:step2. Add subscribe with same query + * @tc.expected: step2. add success + */ + QueryObject queryObj(query); + int errCode = store->AddSubscribe(SUBSCRIBE_ID, queryObj, false); + EXPECT_EQ(errCode, E_OK); + errCode = store->AddSubscribe(SUBSCRIBE_ID, queryObj, false); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(store->RemoveSubscribe(SUBSCRIBE_ID), E_OK); + /** + * @tc.steps:step3. Close natural store + * @tc.expected: step3. Close ok + */ + RefObject::KillAndDecObjRef(store); + KvDBManager::ReleaseDatabaseConnection(conn); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_data_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_data_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff08d251bb392e7c9c2e6d4211117e7f7b0d2ea3 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_data_test.cpp @@ -0,0 +1,1592 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "db_common.h" +#include "db_constant.h" +#include "db_errno.h" +#include "default_factory.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "multi_ver_natural_store.h" +#include "multi_ver_natural_store_commit_storage.h" +#include "multi_ver_natural_store_connection.h" +#include "process_communicator_test_stub.h" +#include "sqlite_multi_ver_data_storage.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + const int WAIT_TIME = 1000; + const uint64_t INVALID_TIMESTAMP = 0; + const uint64_t OPERATION_ADD = 1; + const uint64_t OPERATION_DELETE = 2; + const uint64_t OPERATION_CLEAR = 3; + + string g_testDir; + KvDBProperties g_prop; + SQLiteMultiVerTransaction *g_transaction = nullptr; + MultiVerNaturalStore *g_naturalStore = nullptr; + MultiVerNaturalStoreConnection *g_naturalStoreConnection = nullptr; + Version g_version = 0; + const std::string CREATE_TABLE = + "CREATE TABLE IF NOT EXISTS version_data(key BLOB, value BLOB, oper_flag INTEGER, version INTEGER, " \ + "timestamp INTEGER, ori_timestamp INTEGER, hash_key BLOB, " \ + "PRIMARY key(hash_key, version));"; +} + +class DistributedDBStorageTransactionDataTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +static void GetReadTransaction() +{ + if (g_transaction == nullptr) { + g_transaction = new (std::nothrow) SQLiteMultiVerTransaction(); + ASSERT_NE(g_transaction, nullptr); + std::string dir = g_testDir + "/31/multi_ver/multi_ver_data.db"; + LOGI("%s", dir.c_str()); + CipherPassword passwd; + int errCode = g_transaction->Initialize(dir, true, CipherType::AES_256_GCM, passwd); + ASSERT_EQ(errCode, E_OK); + } + Version versionInfo; + ASSERT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, versionInfo), E_OK); + g_version = versionInfo; + g_transaction->SetVersion(versionInfo); +} + +static void ValueEqual(const Value &read, const Value &origin) +{ + EXPECT_EQ(read.size(), origin.size()); + if (read.size() != origin.size()) { + DBCommon::PrintHexVector(origin, __LINE__, "Orig"); + } + + EXPECT_EQ(read, origin); +} + +static int RunSyncMergeForOneCommit(std::vector &entries) +{ + MultiVerCommitNode multiVerCommit; + multiVerCommit.commitId.resize(20); // 20 as commit id size + RAND_bytes(multiVerCommit.commitId.data(), 20); + multiVerCommit.deviceInfo = DBCommon::TransferHashString("deviceB") + "deviceB1"; + + // Put the multiver commit of other device. + int errCode = g_naturalStore->PutCommitData(multiVerCommit, entries, "deviceB"); + if (errCode != E_OK) { + return errCode; + } + + std::vector multiVerCommits; + multiVerCommits.push_back(multiVerCommit); + + // Merge the multiver commit of other device. + errCode = g_naturalStore->MergeSyncCommit(multiVerCommit, multiVerCommits); + + for (auto &item : entries) { + if (item != nullptr) { + delete item; + item = nullptr; + } + } + entries.clear(); + + return errCode; +} + +static uint64_t GetCommitTimestamp(const CommitID& commitId) +{ + MultiVerNaturalStoreCommitStorage *commitStorage = new (std::nothrow) MultiVerNaturalStoreCommitStorage(); + if (commitStorage == nullptr) { + return 0; + } + Timestamp timestamp = INVALID_TIMESTAMP; + CommitID newCommitId; + IKvDBCommit *commit = nullptr; + IKvDBCommitStorage::Property property; + property.isNeedCreate = false; + property.path = g_testDir; + property.identifierName = "31"; + int errCode = commitStorage->Open(property); + if (errCode != E_OK) { + goto END; + } + + if (commitId.empty()) { + newCommitId = commitStorage->GetHeader(errCode); + if (newCommitId.empty()) { + return 0; + } + } else { + newCommitId = commitId; + } + + commit = commitStorage->GetCommit(newCommitId, errCode); + if (commit == nullptr) { + LOGE("Can't get the commit:%d", errCode); + goto END; + } + + timestamp = commit->GetTimestamp(); +END: + if (commit != nullptr) { + commitStorage->ReleaseCommit(commit); + commit = nullptr; + } + + delete commitStorage; + commitStorage = nullptr; + + return timestamp; +} + +static uint64_t GetMaxTimestamp() +{ + CommitID commitId; + return GetCommitTimestamp(commitId); +} + +static void PutAndCommitEntry(const Key &key, const Value &value) +{ + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + IOption option; + EXPECT_EQ(g_naturalStoreConnection->Put(option, key, value), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); +} + +static void PushOneEntry(uint64_t opr, uint64_t timestamp, const Key &key, const Value &value, + std::vector &entries) +{ + GenericMultiVerKvEntry *entry = new (std::nothrow) GenericMultiVerKvEntry; + if (entry != nullptr) { + // set key + entry->SetKey(key); + // set value + MultiVerValueObject valueObject; + valueObject.SetValue(value); + Value objectSerial; + valueObject.GetSerialData(objectSerial); + entry->SetValue(objectSerial); + // set timestamp + entry->SetTimestamp(timestamp); + + // set open_flag + entry->SetOperFlag(opr); + if (opr == OPERATION_DELETE) { + Key hashKey; + DBCommon::CalcValueHash(key, hashKey); + entry->SetKey(hashKey); + } else if (opr == OPERATION_CLEAR) { + Key clearKey = {'c', 'l', 'e', 'a', 'r'}; + entry->SetKey(clearKey); + } + + entries.push_back(entry); + } +} + +static void ValueEqualByKey(const Key &key, const Value &value) +{ + IOption option; + Value valueRead; + int errCode = g_naturalStoreConnection->Get(option, key, valueRead); + EXPECT_EQ(errCode, E_OK); + if (errCode != E_OK) { + DBCommon::PrintHexVector(key, __LINE__, "key"); + } + ValueEqual(value, valueRead); +} + +void DistributedDBStorageTransactionDataTest::SetUpTestCase(void) +{ + IKvDBFactory *factory = new (std::nothrow) DefaultFactory(); + ASSERT_TRUE(factory != nullptr); + IKvDBFactory::Register(factory); +} + +void DistributedDBStorageTransactionDataTest::TearDownTestCase(void) +{ + if (g_transaction != nullptr) { + delete g_transaction; + g_transaction = nullptr; + } + auto factory = IKvDBFactory::GetCurrent(); + if (factory != nullptr) { + delete factory; + factory = nullptr; + } + IKvDBFactory::Register(nullptr); +} + +void DistributedDBStorageTransactionDataTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + + // KvDBProperties prop; + g_prop.SetStringProp(KvDBProperties::APP_ID, "app0"); + g_prop.SetStringProp(KvDBProperties::STORE_ID, "store0"); + g_prop.SetStringProp(KvDBProperties::USER_ID, "user0"); + g_prop.SetStringProp(KvDBProperties::DATA_DIR, g_testDir); + g_prop.SetStringProp(KvDBProperties::IDENTIFIER_DIR, "31"); + g_prop.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + + g_naturalStore = new (std::nothrow) MultiVerNaturalStore(); + ASSERT_NE(g_naturalStore, nullptr); + EXPECT_EQ(g_naturalStore->Open(g_prop), E_OK); + + int errCode = 0; + IKvDBConnection *connection = g_naturalStore->GetDBConnection(errCode); + ASSERT_NE(connection, nullptr); + g_naturalStoreConnection = static_cast(connection); + + LOGI("read directory :%s", g_testDir.c_str()); +} + +void DistributedDBStorageTransactionDataTest::TearDown(void) +{ + if (g_transaction != nullptr) { + delete g_transaction; + g_transaction = nullptr; + } + + if (g_naturalStore != nullptr) { + if (g_naturalStoreConnection != nullptr) { + g_naturalStoreConnection->Close(); + g_naturalStoreConnection = nullptr; + } + g_naturalStore->DecObjRef(g_naturalStore); + g_naturalStore = nullptr; + } + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir + "/31/" + DBConstant::MULTI_SUB_DIR); +} + +/** + * @tc.name: StorageInsert001 + * @tc.desc: Put the non-empty key, non-empty value into the database. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageInsert001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data(non-empty key, non-empty value) into the database. + * @tc.expected: step1. Put returns E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + IOption option; + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); + Value valueRead; + /** + * @tc.steps: step2. Get the data. + * @tc.expected: step2. Get returns E_OK and the value is equal to the put value. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_1, valueRead), E_OK); + ValueEqual(VALUE_1, valueRead); + /** + * @tc.steps: step3. Clear the data. + */ + g_naturalStoreConnection->Clear(option); + /** + * @tc.steps: step4. Put another data(non-empty key, non-empty value) into the database. + * @tc.expected: step4. Put returns E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_2, VALUE_2), E_OK); + EXPECT_NE(g_naturalStoreConnection->Commit(), E_OK); + /** + * @tc.steps: step5. Get the data. + * @tc.expected: step5. Get returns E_OK and the value is equal to the second put value. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_2, valueRead), E_OK); + ValueEqual(VALUE_2, valueRead); +} + + /** + * @tc.name: StorageInsert002 + * @tc.desc: Put the empty key, non-empty value into the database. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageInsert002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data(empty key, non-empty value) into the database. + * @tc.expected: step1. Put returns -E_INVALID_ARGS. + */ + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + IOption option; + EXPECT_EQ(g_naturalStoreConnection->Put(option, NULL_KEY_1, VALUE_1), -E_INVALID_ARGS); + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); +} + +/** + * @tc.name: StorageInsert003 + * @tc.desc: Put the non-empty key, empty value into the database. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageInsert003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data(non-empty key, empty value) into the database. + * @tc.expected: step1. Put returns E_OK. + */ + IOption option; + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, NULL_VALUE_1), E_OK); + GetReadTransaction(); + Value valueRead; + Value valueTmp; + /** + * @tc.steps: step2. Get the data. + * @tc.expected: step2. Get returns E_OK and the value is empty. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_1, valueRead), E_OK); + ValueEqual(NULL_VALUE_1, valueRead); +} + +/** + * @tc.name: StorageUpdate001 + * @tc.desc: Update the value to non-empty + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageUpdate001, TestSize.Level1) +{ + IOption option; + /** + * @tc.steps: step1. Put one valid data into the database. + * @tc.expected: step1. Put returns E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_1), E_OK); + /** + * @tc.steps: step2. Put another data whose key is same to the first put data and value(non-empty) is different. + * @tc.expected: step2. Put returns E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_2), E_OK); + GetReadTransaction(); + Value valueRead; + /** + * @tc.steps: step3. Get the data. + * @tc.expected: step3. Get returns E_OK and the value is equal the second put value. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_1, valueRead), E_OK); + ValueEqual(VALUE_2, valueRead); +} + +/** + * @tc.name: StorageUpdate002 + * @tc.desc: Update the value to empty + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageUpdate002, TestSize.Level1) +{ + IOption option; + /** + * @tc.steps: step1. Put one valid data into the database. + * @tc.expected: step1. Put returns E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_1), E_OK); + /** + * @tc.steps: step2. Put another data whose key is same to the first put data and value is empty. + * @tc.expected: step2. Put returns E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, NULL_VALUE_1), E_OK); + Value valueRead; + /** + * @tc.steps: step3. Get the data. + * @tc.expected: step3. Get returns E_OK and the value is empty. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_1, valueRead), E_OK); + ValueEqual(NULL_VALUE_1, valueRead); +} + +/** + * @tc.name: StorageDelete001 + * @tc.desc: Delete the existed data + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageDelete001, TestSize.Level1) +{ + IOption option; + /** + * @tc.steps: step1. Put one valid data. + */ + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_1), E_OK); + /** + * @tc.steps: step2. Delete the data. + */ + EXPECT_EQ(g_naturalStoreConnection->Delete(option, KEY_1), E_OK); + GetReadTransaction(); + Value valueRead; + /** + * @tc.steps: step3. Get the data. + * @tc.expected: step3. Get returns -E_NOT_FOUND. + */ + EXPECT_EQ(g_transaction->Get(KEY_1, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: StorageDelete002 + * @tc.desc: Delete the non-existed data + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageDelete002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Delete one non-existed data. + * @tc.expected: step1. Delete returns -E_NOT_FOUND. + */ + IOption option; + EXPECT_EQ(g_naturalStoreConnection->Delete(option, KEY_1), -E_NOT_FOUND); + GetReadTransaction(); + Value valueRead; + /** + * @tc.steps: step2. Get the non-existed data. + * @tc.expected: step2. Get returns -E_NOT_FOUND. + */ + EXPECT_EQ(g_transaction->Get(KEY_1, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: StorageDelete003 + * @tc.desc: Delete the invalid key data + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageDelete003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Delete the empty-key data. + * @tc.expected: step1. Delete returns not E_OK. + */ + IOption option; + EXPECT_NE(g_naturalStoreConnection->Delete(option, NULL_KEY_1), E_OK); +} + +/** + * @tc.name: StorageClear001 + * @tc.desc: Clear the data + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageClear001, TestSize.Level1) +{ + /** + * @tc.steps: step1. put one data. + */ + IOption option; + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_1), E_OK); + /** + * @tc.steps: step2. clear the data. + * @tc.expected: step2. Returns E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->Clear(option), E_OK); + GetReadTransaction(); + /** + * @tc.steps: step3. Check the data. + * @tc.expected: step3. Getting the data result -E_NOT_FOUND. + */ + Value valueRead; + EXPECT_EQ(g_transaction->Get(KEY_1, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: StorageInsertBatch001 + * @tc.desc: Put the valid batch data + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageInsertBatch001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the batch data. + * @tc.expected: step1. Returns E_OK. + */ + std::vector entries; + entries.push_back(KV_ENTRY_1); + entries.push_back(KV_ENTRY_2); + IOption option; + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entries), E_OK); + Value valueRead; + + /** + * @tc.steps: step2. Check the data. + * @tc.expected: step2. Get the data from the database and the value are equal to the data put before. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_1, valueRead), E_OK); + ValueEqual(VALUE_1, valueRead); + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_2, valueRead), E_OK); + ValueEqual(VALUE_2, valueRead); +} + +/** + * @tc.name: StorageInsertBatch002 + * @tc.desc: Put the partially valid batch data + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageInsertBatch002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the batch data(partially valid, partially invalid). + * @tc.expected: step1. Returns not E_OK. + */ + std::vector entries; + Entry entry; + entries.push_back(KV_ENTRY_1); + entries.push_back(entry); + IOption option; + EXPECT_NE(g_naturalStoreConnection->PutBatch(option, entries), E_OK); + GetReadTransaction(); + Value valueRead; + /** + * @tc.steps: step2. Check the data. + * @tc.expected: step2. Getting the data results not E_OK. + */ + EXPECT_NE(g_transaction->Get(KEY_1, valueRead), E_OK); +} + +/** + * @tc.name: StorageUpdateBatch001 + * @tc.desc: Update the batch data + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageUpdateBatch001, TestSize.Level1) +{ + std::vector entries1; + entries1.push_back(KV_ENTRY_1); + entries1.push_back(KV_ENTRY_2); + + Entry kvEntry1 = {KEY_1, VALUE_2}; + Entry kvEntry2 = {KEY_2, VALUE_1}; + std::vector entries2; + entries2.push_back(kvEntry1); + entries2.push_back(kvEntry2); + + IOption option; + /** + * @tc.steps: step1. Put the batch data. + */ + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entries1), E_OK); + /** + * @tc.steps: step2. Update the batch data. + * @tc.expected: step2. Returns E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entries2), E_OK); + Value valueRead; + /** + * @tc.steps: step3. Check the data. + * @tc.expected: step3. Get the data from the database and check whether the data have been updated. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_1, valueRead), E_OK); + ValueEqual(VALUE_2, valueRead); + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_2, valueRead), E_OK); + ValueEqual(VALUE_1, valueRead); +} + +/** + * @tc.name: StorageUpdateBatch002 + * @tc.desc: Update the batch data(partially invalid data) + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageUpdateBatch002, TestSize.Level1) +{ + std::vector entrys1; + entrys1.push_back(KV_ENTRY_1); + entrys1.push_back(KV_ENTRY_2); + + Entry kvEntry1 = {KEY_1, VALUE_2}; + Entry kvEntry; + Entry kvEntry2 = {KEY_2, VALUE_1}; + std::vector entrys2; + entrys2.push_back(kvEntry1); + entrys2.push_back(kvEntry); + entrys2.push_back(kvEntry2); + + IOption option; + /** + * @tc.steps: step1. Put the batch data. + */ + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entrys1), E_OK); + /** + * @tc.steps: step2. Update the batch data(partially empty key). + * @tc.expected: step2. Returns not E_OK. + */ + EXPECT_NE(g_naturalStoreConnection->PutBatch(option, entrys2), E_OK); + Value valueRead; + /** + * @tc.steps: step3. Check the data. + * @tc.expected: step3. The getting result data are the first put batch . + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_1, valueRead), E_OK); + ValueEqual(VALUE_1, valueRead); + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_2, valueRead), E_OK); + ValueEqual(VALUE_2, valueRead); +} + +/** + * @tc.name: StorageDeleteBatch001 + * @tc.desc: Delete the batch data + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageDeleteBatch001, TestSize.Level1) +{ + std::vector entries; + entries.push_back(KV_ENTRY_1); + entries.push_back(KV_ENTRY_2); + std::vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + + IOption option; + /** + * @tc.steps: step1. Put the batch data. + */ + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entries), E_OK); + /** + * @tc.steps: step2. Delete the batch data. + * @tc.expected: step2. Return E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->DeleteBatch(option, keys), E_OK); + GetReadTransaction(); + Value valueRead; + /** + * @tc.steps: step3. Check the data. + * @tc.expected: step3. Getting the data results -E_NOT_FOUND. + */ + EXPECT_EQ(g_transaction->Get(KEY_1, valueRead), -E_NOT_FOUND); + EXPECT_EQ(g_transaction->Get(KEY_2, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: StorageDeleteBatch002 + * @tc.desc: Delete the batch data(partially non-existed) + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageDeleteBatch002, TestSize.Level1) +{ + std::vector entries; + entries.push_back(KV_ENTRY_1); + entries.push_back(KV_ENTRY_2); + std::vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + std::vector k3 = {'3'}; + keys.push_back(k3); + + IOption option; + /** + * @tc.steps: step1. Put the batch data. + */ + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entries), E_OK); + /** + * @tc.steps: step2. Delete the batch data(partially non-existed). + * @tc.expected: step2. Return E_OK. + */ + EXPECT_EQ(g_naturalStoreConnection->DeleteBatch(option, keys), E_OK); + GetReadTransaction(); + Value valueRead; + /** + * @tc.steps: step3. Check the data. + * @tc.expected: step3. Cannot Get the delete data in the database. + */ + EXPECT_NE(g_transaction->Get(KEY_1, valueRead), E_OK); + EXPECT_NE(g_transaction->Get(KEY_2, valueRead), E_OK); +} + +/** + * @tc.name: StorageDeleteBatch003 + * @tc.desc: Delete the batch data(partially invalid) + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageDeleteBatch003, TestSize.Level1) +{ + std::vector entries; + entries.push_back(KV_ENTRY_1); + entries.push_back(KV_ENTRY_2); + std::vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + Key k3; + keys.push_back(k3); + + IOption option; + /** + * @tc.steps: step1. Put the batch data. + */ + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entries), E_OK); + /** + * @tc.steps: step2. Delete the batch data(partially invalid). + * @tc.expected: step2. Return E_OK. + */ + EXPECT_NE(g_naturalStoreConnection->DeleteBatch(option, keys), E_OK); + GetReadTransaction(); + Value valueRead; + /** + * @tc.steps: step3. Check the data. + * @tc.expected: step3. Can get the put origined data. + */ + EXPECT_EQ(g_transaction->Get(KEY_1, valueRead), E_OK); + EXPECT_EQ(g_transaction->Get(KEY_2, valueRead), E_OK); +} + +/** + * @tc.name: StorageTransactionCombo001 + * @tc.desc: Multiple operation within the transaction + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, StorageTransactionCombo001, TestSize.Level1) +{ + Entry kvEntry3; + Entry kvEntry4; + kvEntry3.key = {'3'}; + kvEntry4.key = {'4'}; + kvEntry3.value = {'c'}; + kvEntry4.value = {'d'}; + // inserted data + std::vector entrys1; + entrys1.push_back(KV_ENTRY_1); + entrys1.push_back(KV_ENTRY_2); + entrys1.push_back(kvEntry3); + entrys1.push_back(kvEntry4); + + kvEntry3.value = {'e'}; + kvEntry4.value = {'f'}; + // updated data + std::vector entrys2; + entrys2.push_back(kvEntry3); + entrys2.push_back(kvEntry4); + + // deleted data + std::vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + + IOption option; + /** + * @tc.steps: step1. Start the transaction. + */ + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + /** + * @tc.steps: step2. Put the batch data. + */ + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entrys1), E_OK); + /** + * @tc.steps: step3. Delete the batch data. + */ + EXPECT_EQ(g_naturalStoreConnection->DeleteBatch(option, keys), E_OK); + /** + * @tc.steps: step4. Update the batch data. + */ + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entrys2), E_OK); + /** + * @tc.steps: step5. Commit the transaction. + */ + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); + Value valueRead; + /** + * @tc.steps: step6. Check the data. + * @tc.expected: step6. Can get the updated data. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, kvEntry3.key, valueRead), E_OK); + ValueEqual(kvEntry3.value, valueRead); + EXPECT_EQ(g_naturalStoreConnection->Get(option, kvEntry4.key, valueRead), E_OK); + ValueEqual(kvEntry4.value, valueRead); +} + +/** + * @tc.name: TransactionRollback001 + * @tc.desc: Multiple operation within the transaction + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, TransactionRollback001, TestSize.Level1) +{ + std::vector entries; + entries.push_back(KV_ENTRY_1); + entries.push_back(KV_ENTRY_2); + IOption option; + /** + * @tc.steps: step1. Start the transaction. + */ + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + /** + * @tc.steps: step2. Put the batch data. + */ + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entries), E_OK); + /** + * @tc.steps: step3. Rollback the transaction. + */ + EXPECT_EQ(g_naturalStoreConnection->RollBack(), E_OK); + Value valueRead; + /** + * @tc.steps: step4. Check the data. + * @tc.expected: step4. Couldn't find the data in the database. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_1, valueRead), -E_NOT_FOUND); + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_2, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: TransactionGetCommitData001 + * @tc.desc: Get the commit data of one transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, TransactionGetCommitData001, TestSize.Level1) +{ + std::vector entries; + entries.push_back(KV_ENTRY_1); + entries.push_back(KV_ENTRY_2); + IOption option; + /** + * @tc.steps: step1. Put the batch data within in one transaction. + */ + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + EXPECT_EQ(g_naturalStoreConnection->PutBatch(option, entries), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); + + Value valueRead; + /** + * @tc.steps: step2. Check the put batch data, and could get the put data. + */ + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_1, valueRead), E_OK); + ValueEqual(valueRead, KV_ENTRY_1.value); + EXPECT_EQ(g_naturalStoreConnection->Get(option, KEY_2, valueRead), E_OK); + ValueEqual(valueRead, KV_ENTRY_2.value); + /** + * @tc.steps: step3. Get one commit data. + */ + GetReadTransaction(); + std::vector multiVerKvEntries; + EXPECT_EQ(g_transaction->GetEntriesByVersion(g_version, multiVerKvEntries), OK); + ASSERT_EQ(multiVerKvEntries.size(), 2UL); + auto entry1 = static_cast(multiVerKvEntries[0]); + ASSERT_NE(entry1, nullptr); + valueRead.clear(); + EXPECT_EQ(entry1->GetValue(valueRead), E_OK); + /** + * @tc.steps: step4. Check the commit data. + * @tc.expected: step4. Could find the batch put data in the commit data. + */ + auto entry2 = static_cast(multiVerKvEntries[1]); + ASSERT_NE(entry2, nullptr); + valueRead.clear(); + EXPECT_EQ(entry2->GetValue(valueRead), E_OK); + for (auto &item : multiVerKvEntries) { + delete item; + item = nullptr; + } +} + +/** + * @tc.name: TransactionSqliteKvEntry001 + * @tc.desc: Serialize the kv entry and deserialize the data. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, TransactionSqliteKvEntry001, TestSize.Level1) +{ + GenericMultiVerKvEntry entry; + entry.SetOperFlag(17); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + entry.SetKey(key); + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value, 20); + /** + * @tc.steps: step1. Initialize the multi version kv entry. + */ + MultiVerValueObject valueObject; + valueObject.SetValue(value); + Value objectSerial; + + valueObject.GetSerialData(objectSerial); + entry.SetValue(objectSerial); + /** + * @tc.steps: step2. Get the serial data of the entry. + */ + std::vector serialData; + EXPECT_EQ(entry.GetSerialData(serialData), E_OK); + /** + * @tc.steps: step3. Deserial the data. + */ + GenericMultiVerKvEntry deEntry; + EXPECT_EQ(deEntry.DeSerialData(serialData), E_OK); + Key keyRead; + Value valueRead; + Value valueTmp; + uint64_t flag; + deEntry.GetKey(keyRead); + deEntry.GetValue(valueTmp); + deEntry.GetOperFlag(flag); + ValueEqual(keyRead, key); + MultiVerValueObject objectRead; + EXPECT_EQ(objectRead.DeSerialData(valueTmp), E_OK); + objectRead.GetValue(valueRead); + /** + * @tc.steps: step4. Check the deserialized data. + * @tc.expected: step4. the deserialized value is equal to the set value. + */ + ValueEqual(valueRead, value); + EXPECT_EQ(flag, 17UL); +} + +/** + * @tc.name: TransactionPutForeignData001 + * @tc.desc: Put the remote commit data into the current device database. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: huangnaigu + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, TransactionPutForeignData001, TestSize.Level1) +{ + GenericMultiVerKvEntry entry; + entry.SetOperFlag(1); + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + entry.SetKey(key); + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + MultiVerValueObject valueObject; + valueObject.SetValue(value); + Value objectSerial; + valueObject.GetSerialData(objectSerial); + entry.SetValue(objectSerial); + + std::vector entries; + entries.push_back(&entry); + /** + * @tc.steps: step1. Create the multiver commit. + */ + MultiVerCommitNode multiVerCommit; + multiVerCommit.commitId.resize(20); // commit id size + RAND_bytes(multiVerCommit.commitId.data(), 20); + multiVerCommit.deviceInfo = DBCommon::TransferHashString("deviceB") + "deviceB1"; + /** + * @tc.steps: step2. Put the multiver commit of other device. + */ + EXPECT_EQ(g_naturalStore->PutCommitData(multiVerCommit, entries, "deviceB"), E_OK); + std::vector multiVerCommits; + multiVerCommits.push_back(multiVerCommit); + /** + * @tc.steps: step3. Merge the multiver commit of other device. + */ + EXPECT_EQ(g_naturalStore->MergeSyncCommit(multiVerCommit, multiVerCommits), E_OK); + + /** + * @tc.steps: step4. Get the commit data, the foreign synced data would be got from sync. + */ + std::vector readEntries; + EXPECT_EQ(g_naturalStore->GetCommitData(multiVerCommit, readEntries), E_OK); + ASSERT_EQ(readEntries.size(), 0UL); +} + +/** + * @tc.name: DefaultConflictResolution001 + * @tc.desc: Merge data without conflicts + * @tc.type: FUNC + * @tc.require: AR000CQE13 AR000CQE14 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, DefaultConflictResolution001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the local data(KEY_1, VALUE_1) into the database. + * @tc.expected: step1. Put returns E_OK. + */ + PutAndCommitEntry(KEY_1, VALUE_1); + /** + * @tc.steps: step2. Put the external data(KEY_2, VALUE_2) into the database. + * @tc.expected: step2. Put returns E_OK + */ + std::vector entries; + PushOneEntry(OPERATION_ADD, 1, KEY_2, VALUE_2, entries); + EXPECT_EQ(RunSyncMergeForOneCommit(entries), E_OK); + /** + * @tc.steps: step3. Get value1 and value2 + * @tc.expected: step3. Value1 and value2 are correct. + */ + ValueEqualByKey(KEY_1, VALUE_1); + ValueEqualByKey(KEY_2, VALUE_2); +} + +/** + * @tc.name: DefaultConflictResolution002 + * @tc.desc: Merge data with conflicts ,no clear operation in the external data and local data + * @tc.type: FUNC + * @tc.require: AR000CQE13 AR000CQE14 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, DefaultConflictResolution002, TestSize.Level1) +{ + PutAndCommitEntry(KEY_1, VALUE_1); + /** + * @tc.steps: step1. Put the [KEY_2,V2] and [KEY_3,V3] into the database and delete [KEY_1,V1] + * @tc.expected: step1. Both Put and Delete operation returns E_OK. + */ + IOption option; + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_2, VALUE_2), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_3, VALUE_3), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Delete(option, KEY_1), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); + /** + * @tc.steps: step2. Get latest timestamp + */ + Timestamp t1 = GetMaxTimestamp(); + EXPECT_TRUE(t1 > 0); + /** + * @tc.steps: step3. Put the external entry[KEY_2,VALUE_4,T2] into the database. + * @tc.expected: step3. Put returns E_OK + */ + std::vector entriesV4; + PushOneEntry(OPERATION_ADD, t1 - 1, KEY_2, VALUE_4, entriesV4); + EXPECT_EQ(RunSyncMergeForOneCommit(entriesV4), E_OK); + /** + * @tc.steps: step4. Get value of K2 + * @tc.expected: step4. value of K2 is equals V2 + */ + ValueEqualByKey(KEY_2, VALUE_2); + /** + * @tc.steps: step5. Put the external entry[KEY_2,VALUE_5,T3] into the database. + * @tc.expected: step5. Put returns E_OK + */ + std::vector entriesV5; + PushOneEntry(OPERATION_ADD, t1 + 1, KEY_2, VALUE_5, entriesV5); + EXPECT_EQ(RunSyncMergeForOneCommit(entriesV5), E_OK); + /** + * @tc.steps: step6. Get value of K2 + * @tc.expected: step6. value of K2 is equals V5 + */ + ValueEqualByKey(KEY_2, VALUE_5); + /** + * @tc.steps: step7. Put the external Delete entry[KEY_3,T2] into the database. + * @tc.expected: step7. Put returns E_OK + */ + std::vector entriesV6; + PushOneEntry(OPERATION_DELETE, t1 - 1, KEY_3, VALUE_6, entriesV6); + EXPECT_EQ(RunSyncMergeForOneCommit(entriesV6), E_OK); + /** + * @tc.steps: step8. Get value of K3 + * @tc.expected: step8. value of K3 is equals V3 + */ + ValueEqualByKey(KEY_3, VALUE_3); + /** + * @tc.steps: step9. Put the external Delete entry[KEY_3,T2] into the database. + * @tc.expected: step9. Put returns E_OK + */ + std::vector entriesV7; + PushOneEntry(OPERATION_DELETE, t1 + 1, KEY_3, VALUE_7, entriesV7); + EXPECT_EQ(RunSyncMergeForOneCommit(entriesV7), E_OK); + /** + * @tc.steps: step10. Get value of K3 + * @tc.expected: step10. Return NOT_FOUND + */ + GetReadTransaction(); + Value valueTmp; + EXPECT_EQ(g_transaction->Get(KEY_3, valueTmp), -E_NOT_FOUND); + /** + * @tc.steps: step11. Put the external entry[KEY_1,VALUE_6,T2] into the database. + * @tc.expected: step11. Put returns E_OK + */ + std::vector entriesV8; + PushOneEntry(OPERATION_ADD, t1 - 1, KEY_1, VALUE_6, entriesV8); + EXPECT_EQ(RunSyncMergeForOneCommit(entriesV8), E_OK); + /** + * @tc.steps: step12. Get value of K1 + * @tc.expected: step12. Return NOT_FOUND + */ + GetReadTransaction(); + EXPECT_EQ(g_transaction->Get(KEY_1, valueTmp), -E_NOT_FOUND); + /** + * @tc.steps: step13. Put the external entry[KEY_1,VALUE_7,T2] into the database. + * @tc.expected: step13. Put returns E_OK + */ + std::vector entriesV9; + PushOneEntry(OPERATION_ADD, t1 + 1, KEY_1, VALUE_7, entriesV9); + EXPECT_EQ(RunSyncMergeForOneCommit(entriesV9), E_OK); + /** + * @tc.steps: step14. Get value of K1 + * @tc.expected: step14. value of K1 is V7 + */ + ValueEqualByKey(KEY_1, VALUE_7); +} + +/** + * @tc.name: DefaultConflictResolution003 + * @tc.desc: Merge data with conflicts, clear operation is in the external data + * @tc.type: FUNC + * @tc.require: AR000CQE13 AR000CQE14 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, DefaultConflictResolution003, TestSize.Level2) +{ + /** + * @tc.steps: step1. Put the local data(KEY_1, VALUE_1) into the database. + * @tc.expected: step1. Put returns E_OK. + */ + PutAndCommitEntry(KEY_1, VALUE_1); + /** + * @tc.steps: step2. Get timestampV1 + */ + Timestamp timestampV1 = GetMaxTimestamp(); + Timestamp timestampClear = timestampV1 + 1; + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step3. Put the local data(KEY_2, VALUE_2) into the database. + * @tc.expected: step3. Put returns E_OK. + */ + PutAndCommitEntry(KEY_2, VALUE_2); + /** + * @tc.steps: step4. Get timestampV2 + */ + Timestamp timestampV2 = GetMaxTimestamp(); + /** + * @tc.steps: step5. Put the external clear entry into the database. + * @tc.expected: step5. Put returns E_OK + */ + std::vector entries; + PushOneEntry(OPERATION_CLEAR, timestampClear, KEY_3, VALUE_3, entries); + EXPECT_EQ(RunSyncMergeForOneCommit(entries), E_OK); + + EXPECT_TRUE(timestampV1 < timestampClear); + EXPECT_TRUE(timestampClear < timestampV2); + + /** + * @tc.steps: step6. Get value1 and value2 + * @tc.expected: step6. Get Value1 return NOT_FOUND , value2 is correct. + */ + Value valueTmp; + GetReadTransaction(); + EXPECT_EQ(g_transaction->Get(KEY_1, valueTmp), -E_NOT_FOUND); + + ValueEqualByKey(KEY_2, VALUE_2); +} + +/** + * @tc.name: DefaultConflictResolution004 + * @tc.desc: Merge data with conflicts, clear operation is in the local data + * @tc.type: FUNC + * @tc.require: AR000CQE13 AR000CQE14 + * @tc.author: wumin + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, DefaultConflictResolution004, TestSize.Level2) +{ + /** + * @tc.steps: step1. Put the local data(KEY_1, VALUE_1) into the database and get the latest timestamp. + * @tc.expected: step1. Put returns E_OK. + */ + PutAndCommitEntry(KEY_1, VALUE_1); + Timestamp t1 = GetMaxTimestamp(); + EXPECT_TRUE(t1 > 0); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step2. Put the local data(KEY_2, VALUE_2) into the database and get the latest timestamp. + * @tc.expected: step2. Put returns E_OK. + */ + PutAndCommitEntry(KEY_2, VALUE_2); + Timestamp t2 = GetMaxTimestamp(); + /** + * @tc.steps: step3. Execute Clear() operation and get the latest timestamp. + */ + IOption option; + g_naturalStoreConnection->Clear(option); + Timestamp t3 = GetMaxTimestamp(); + EXPECT_TRUE(t3 > 0); + /** + * @tc.steps: step4. Put the local data(KEY_3, VALUE_3) into the database and get the latest timestamp. + * @tc.expected: step4. Put returns E_OK. + */ + PutAndCommitEntry(KEY_3, VALUE_3); + /** + * @tc.steps: step5. Put the external data [Clear, T5],[KEY_4,VALUE_4,T6],[KEY_5,VALUE_5,T7] into the database. + * @tc.expected: step5. Put returns E_OK + */ + EXPECT_TRUE(t1 + 1 < t2); + std::vector entries; + // put clear entry + PushOneEntry(OPERATION_CLEAR, t1 + 1, KEY_7, VALUE_7, entries); + // put K4 entry + PushOneEntry(OPERATION_ADD, t3 - 1, KEY_4, VALUE_4, entries); + // put K5 entry + PushOneEntry(OPERATION_ADD, t3 + 1, KEY_5, VALUE_5, entries); + // merge data + EXPECT_EQ(RunSyncMergeForOneCommit(entries), E_OK); + /** + * @tc.steps: step6. Get value of KEY_1,KEY_2,KEY_3,KEY_4,K5 + */ + GetReadTransaction(); + Value valueTmp; + EXPECT_EQ(g_transaction->Get(KEY_1, valueTmp), -E_NOT_FOUND); + EXPECT_EQ(g_transaction->Get(KEY_2, valueTmp), -E_NOT_FOUND); + EXPECT_EQ(g_transaction->Get(KEY_4, valueTmp), -E_NOT_FOUND); + + ValueEqualByKey(KEY_3, VALUE_3); + ValueEqualByKey(KEY_5, VALUE_5); +} + +/** + * @tc.name: CommitTimestamp001 + * @tc.desc: Test the timestamp of the native commit. + * @tc.type: FUNC + * @tc.require: AR000CQE11 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, CommitTimestamp001, TestSize.Level2) +{ + /** + * @tc.steps: step1. Put in some data(non-empty key, non-empty value) within one transaction. + */ + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + IOption option; + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_2, VALUE_2), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); + + /** + * @tc.steps: step2. Add different operations(add,update,delete,clear) within one transaction. + */ + std::srand(std::time(0)); // set the current time to the seed. + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_4, VALUE_4), E_OK); // add + std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 1000)); + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_3), E_OK); // update + std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 1000)); + EXPECT_EQ(g_naturalStoreConnection->Delete(option, KEY_2), E_OK); // delete + std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 1000)); + EXPECT_EQ(g_naturalStoreConnection->Clear(option), E_OK); // clear + std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 1000)); + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); + + /** + * @tc.steps: step3. Get all the record data in the newest commit. + */ + GetReadTransaction(); + std::vector entries; + g_transaction->GetEntriesByVersion(g_version, entries); + ASSERT_EQ(entries.size(), 4UL); // add, update, delete and clear for 4 operation. + + std::set timeSet; + for (auto item : entries) { + uint64_t timestamp = 0; + item->GetTimestamp(timestamp); + timeSet.insert(timestamp); + } + + ASSERT_EQ(timeSet.size(), 1UL); // only one timestamp in one commit. + // Tobe compare the timestamp. + CommitID commitId; + Timestamp commitTimestamp = GetCommitTimestamp(commitId); + LOGD("TimeRecord:%" PRIu64 ", TimeCommit:%" PRIu64, *(timeSet.begin()), commitTimestamp); + ASSERT_EQ(*(timeSet.begin()), commitTimestamp); + ASSERT_NE(commitTimestamp, 0UL); + + for (auto &item : entries) { + g_naturalStore->ReleaseKvEntry(item); + item = nullptr; + } +} + +static bool PutFirstSyncCommitData(MultiVerCommitNode &multiVerCommit) +{ + GenericMultiVerKvEntry entry; + entry.SetOperFlag(1); // add the new data. + entry.SetKey(KEY_2); + + MultiVerValueObject valueObject; + valueObject.SetValue(VALUE_2); + Value objectSerial; + valueObject.GetSerialData(objectSerial); + entry.SetValue(objectSerial); + entry.SetTimestamp(1000UL); // the first data timestamp. + + std::vector entries; + entries.push_back(&entry); + /** + * @tc.steps: step1. Create the multiver commit. + */ + multiVerCommit.commitId.resize(20); // commit id size + RAND_bytes(multiVerCommit.commitId.data(), 20); // commit id size + multiVerCommit.deviceInfo = DBCommon::TransferHashString("deviceB") + "deviceB1"; + multiVerCommit.timestamp = 1000UL; // the first data timestamp. + /** + * @tc.steps: step2. Put the multiver commit of other device. + */ + int errCode = g_naturalStore->PutCommitData(multiVerCommit, entries, "deviceB"); + if (errCode != E_OK) { + return false; + } + return true; +} + +static bool PutSecondSyncCommitData(const MultiVerCommitNode &multiVerCommit, MultiVerCommitNode &newCommit) +{ + GenericMultiVerKvEntry entry; + entry.SetOperFlag(1); // add flag + entry.SetKey(KEY_3); + + MultiVerValueObject valueObject; + valueObject.SetValue(VALUE_3); + Value objectSerial; + valueObject.GetSerialData(objectSerial); + entry.SetValue(objectSerial); + entry.SetTimestamp(2000UL); // bigger than the first one. + + std::vector entries; + entries.push_back(&entry); + + newCommit.commitId.resize(20); // commit id size + RAND_bytes(newCommit.commitId.data(), 20); // commit id size. + newCommit.leftParent = multiVerCommit.commitId; + newCommit.deviceInfo = DBCommon::TransferHashString("deviceB") + "deviceB1"; + newCommit.timestamp = 2000UL; // bigger than the first one. + + int errCode = g_naturalStore->PutCommitData(newCommit, entries, "deviceB"); + if (errCode != E_OK) { + return false; + } + std::vector multiVerCommits; + multiVerCommits.push_back(multiVerCommit); + multiVerCommits.push_back(newCommit); + + errCode = g_naturalStore->MergeSyncCommit(newCommit, multiVerCommits); + if (errCode != E_OK) { + return false; + } + return true; +} + +/** + * @tc.name: CommitTimestamp002 + * @tc.desc: Test the timestamp of the native commits. + * @tc.type: FUNC + * @tc.require: AR000CQE11 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, CommitTimestamp002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put in some data(non-empty key, non-empty value) within one transaction. + */ + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + IOption option; + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_2, VALUE_2), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); + + CommitID commitId; + Timestamp stampFirst = GetCommitTimestamp(commitId); + ASSERT_NE(stampFirst, 0UL); // non-zero + + /** + * @tc.steps: step2. Add another data within one transaction. + */ + std::srand(std::time(0)); // set the current time to the seed. + std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 1000)); + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_4, VALUE_4), E_OK); + /** + * @tc.steps: step3. Check the timestamp of the two commits. + * @tc.expected: step3. the timestamp of the second commit is greater than the timestamp of the first commit. + */ + Timestamp stampSecond = GetCommitTimestamp(commitId); + ASSERT_NE(stampSecond, 0UL); // non-zero + LOGD("TimeFirst:%" PRIu64 ", TimeSecond:%" PRIu64, stampFirst, stampSecond); + ASSERT_GT(stampSecond, stampFirst); +} +static void ReleaseKvEntries(std::vector &entries) +{ + for (auto &item : entries) { + if (item == nullptr) { + continue; + } + g_naturalStore->ReleaseKvEntry(item); + item = nullptr; + } +} + +/** + * @tc.name: CommitTimestamp003 + * @tc.desc: Test the timestamp of the foreign commits. + * @tc.type: FUNC + * @tc.require: AR000CQE11 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, CommitTimestamp003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put in some data(non-empty key, non-empty value) within one transaction. + */ + EXPECT_EQ(g_naturalStoreConnection->StartTransaction(), E_OK); + IOption option; + EXPECT_EQ(g_naturalStoreConnection->Put(option, KEY_1, VALUE_1), E_OK); + EXPECT_EQ(g_naturalStoreConnection->Commit(), E_OK); + + /** + * @tc.steps: step2. Add different operations(add,update,delete,clear) within one transaction. + */ + std::srand(std::time(0)); // set the current time to the seed. + std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 1000)); + MultiVerCommitNode commit; + ASSERT_EQ(PutFirstSyncCommitData(commit), true); // add + + GetReadTransaction(); + std::vector entries; + g_transaction->GetEntriesByVersion(g_version, entries); + ASSERT_EQ(entries.size(), 1UL); // sync commit have only one entry. + ASSERT_NE(entries[0], nullptr); + uint64_t timestamp = 0; + entries[0]->GetTimestamp(timestamp); + ReleaseKvEntries(entries); + + /** + * @tc.steps: step3. Check the timestamp of the commit and the data. + * @tc.steps: expected. the timestamp of the sync commit is equal to the timestamp of the data record. + */ + Timestamp commitTimestamp = GetCommitTimestamp(commit.commitId); + LOGD("TimeRecord:%" PRIu64 ", TimeCommit:%" PRIu64, timestamp, commitTimestamp); + ASSERT_EQ(timestamp, commitTimestamp); + ASSERT_NE(commitTimestamp, 0UL); +} + +/** + * @tc.name: CommitTimestamp004 + * @tc.desc: Test the timestamp of the merge commits. + * @tc.type: FUNC + * @tc.require: AR000CQE11 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, CommitTimestamp004, TestSize.Level1) +{ + /** + * @tc.steps: step1. Add the first sync commit. + */ + MultiVerCommitNode commit; + ASSERT_EQ(PutFirstSyncCommitData(commit), true); // add the first sync commit. + + std::srand(std::time(0)); // set the current time to the seed. + std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 1000)); // sleep for random interval. + + /** + * @tc.steps: step2. Add the second sync commit and merge the commit. + */ + MultiVerCommitNode newCommit; + ASSERT_EQ(PutSecondSyncCommitData(commit, newCommit), true); + + /** + * @tc.steps: step3. Get the newest entries. + */ + GetReadTransaction(); + std::vector entries; + g_transaction->GetEntriesByVersion(g_version, entries); + ASSERT_EQ(entries.size(), 2UL); // merge node has 2 entries. + + std::set timeSet; + for (auto &item : entries) { + ASSERT_NE(item, nullptr); + uint64_t timestamp = 0; + item->GetTimestamp(timestamp); + g_naturalStore->ReleaseKvEntry(item); + item = nullptr; + timeSet.insert(timestamp); + } + /** + * @tc.steps: step4. Get the timestamp of newest entries. + * @tc.expected: step4. The merged commit have different timestamp. + */ + ASSERT_EQ(timeSet.size(), 2UL); // entries have different timestamp. + for (auto item : timeSet) { + ASSERT_NE(item, 0UL); + } +} + +/** + * @tc.name: GetBranchTag + * @tc.desc: Test the branch tag of the commits. + * @tc.type: FUNC + * @tc.require: AR000CQE11 + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionDataTest, GetBranchTag001, TestSize.Level1) +{ + KvStoreDelegateManager::SetProcessLabel("123", "456"); + KvStoreDelegateManager::SetProcessCommunicator(std::make_shared()); + ASSERT_NE(g_naturalStore, nullptr); + std::vector vectTag; + g_naturalStore->GetCurrentTag(vectTag); + DBCommon::PrintHexVector(vectTag); + for (int i = 0; i < 10; i++) { + if (g_naturalStore != nullptr) { + if (g_naturalStoreConnection != nullptr) { + g_naturalStoreConnection->Close(); + g_naturalStoreConnection = nullptr; + } + g_naturalStore->DecObjRef(g_naturalStore); + g_naturalStore = new (std::nothrow) MultiVerNaturalStore; + ASSERT_NE(g_naturalStore, nullptr); + EXPECT_EQ(g_naturalStore->Open(g_prop), E_OK); + std::vector readTag; + g_naturalStore->GetCurrentTag(readTag); + DBCommon::PrintHexVector(readTag); + EXPECT_EQ(vectTag, readTag); + } + } +} diff --git a/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_record_test.cpp b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_record_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d0814b1a2bffa06326746137aef188af94e0b1e --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_record_test.cpp @@ -0,0 +1,1103 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "db_constant.h" +#include "distributeddb_tools_unit_test.h" +#include "sqlite_local_kvdb_connection.h" +#include "sqlite_multi_ver_data_storage.h" +#include "sqlite_utils.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + string g_storeDir; + SQLiteMultiVerTransaction *g_transaction = nullptr; + const std::string CREATE_TABLE = + "CREATE TABLE IF NOT EXISTS version_data(key BLOB, value BLOB, oper_flag INTEGER, version INTEGER, " \ + "timestamp INTEGER, ori_timestamp INTEGER, hash_key BLOB, " \ + "PRIMARY key(hash_key, version));"; +} + +class DistributedDBStorageTransactionRecordTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBStorageTransactionRecordTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_storeDir = g_testDir + "/test_multi_version.db"; + LOGI("read directory :%s", g_storeDir.c_str()); +} + +void DistributedDBStorageTransactionRecordTest::TearDownTestCase(void) +{ + remove(g_testDir.c_str()); +} + +void DistributedDBStorageTransactionRecordTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_transaction = new (std::nothrow) SQLiteMultiVerTransaction(); + ASSERT_NE(g_transaction, nullptr); + CipherPassword passwd; + int errCode = g_transaction->Initialize(g_storeDir, false, CipherType::AES_256_GCM, passwd); + ASSERT_EQ(errCode, E_OK); +} + +void DistributedDBStorageTransactionRecordTest::TearDown(void) +{ + if (g_transaction != nullptr) { + delete g_transaction; + g_transaction = nullptr; + } + + if (remove(g_storeDir.c_str()) != 0) { + LOGE("remove db failed, errno:%d", errno); + } +} + +/** + * @tc.name: MultiverStorage001 + * @tc.desc: test the putting non empty data with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the current version. + */ + Version originVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, originVer), E_OK); + + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + + /** + * @tc.steps: step2. Put the new data into the database. + * @tc.expected: step2. Put returns E_OK. + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + + /** + * @tc.steps: step3. Get the new data and check the value. + * @tc.expected: step3. Get returns E_OK and the read value is equal to the put value. + */ + Value valueRead; + EXPECT_EQ(g_transaction->Get(key, valueRead), E_OK); + + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value), true); + + /** + * @tc.steps: step4. Get the current max version. + * @tc.expected: step4. The current max version is greater than the max version before put. + */ + Version currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + ASSERT_GT(currentVer, originVer); +} + +/** + * @tc.name: MultiverStorage002 + * @tc.desc: test the putting data(empty key) with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the current version. + */ + Version originVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, originVer), E_OK); + + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + /** + * @tc.steps: step2. Put the new data whose key is empty and value is not empty into the database. + * @tc.expected: step2. Put returns not E_OK + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_NE(g_transaction->Put(key, value), E_OK); + /** + * @tc.steps: step3. Get the current max version. + * @tc.expected: step3. The current max version is equal to the max version before put + */ + Version currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_EQ(currentVer, originVer); +} + +/** + * @tc.name: MultiverStorage003 + * @tc.desc: test the putting data(empty value) with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the current version. + */ + Version originVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, originVer), E_OK); + + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + Value value; + /** + * @tc.steps: step2. Put the new data whose key is not empty and value is empty into the database. + * @tc.expected: step2. Put returns E_OK + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + + Value valueRead; + /** + * @tc.steps: step3. Get the new data and check the value. + * @tc.expected: step3. Get returns E_OK and the read value is equal to the put value. + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value), true); + /** + * @tc.steps: step4. Get the current max version. + * @tc.expected: step4. The current max version is greater than the max version before put. + */ + Version currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + ASSERT_GT(currentVer, originVer); +} + +/** + * @tc.name: MultiverStorage004 + * @tc.desc: Update the data value to non-empty with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage004, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the current version. + */ + Version originVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, originVer), E_OK); + + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + + /** + * @tc.steps: step2. Put the data whose key is not empty and value is empty into the database. + * @tc.expected: step2. Put returns E_OK + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + Version currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_GT(currentVer, originVer); + originVer = currentVer; + g_transaction->ResetVersion(); + + Value valueChanged; + + /** + * @tc.steps: step3. Update the data with another non-empty value. + */ + DistributedDBToolsUnitTest::GetRandomKeyValue(valueChanged); + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key, valueChanged), E_OK); + Value valueRead; + /** + * @tc.steps: step4. Get the data according the key and check the value. + * @tc.steps: step5. Get the current max version. + * @tc.expected: step4. Get returns E_OK and the value is equal to the new put value. + * @tc.expected: step5. The current max version is greater than the max version before update. + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, valueChanged), true); + + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + ASSERT_GT(currentVer, originVer); +} + +/** + * @tc.name: MultiverStorage005 + * @tc.desc: Update the data value to empty with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage005, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the current version. + */ + Version originVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, originVer), E_OK); + + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + /** + * @tc.steps: step2. Put the data whose key is not empty and value is not empty into the database. + * @tc.expected: step2. Put returns E_OK + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + Version currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_GT(currentVer, originVer); + originVer = currentVer; + g_transaction->ResetVersion(); + + Value valueChanged; + /** + * @tc.steps: step3. Update the data with empty value. + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key, valueChanged), E_OK); + Value valueRead; + /** + * @tc.steps: step4. Get the data according the key and check the value. + * @tc.steps: step5. Get the current max version. + * @tc.expected: step4. Get returns E_OK and the value is empty. + * @tc.expected: step5. The current max version is greater than the max version before update. + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, valueChanged), true); + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + ASSERT_GT(currentVer, originVer); +} + +/** + * @tc.name: MultiverStorage006 + * @tc.desc: Delete the existed data with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage006, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the current version. + */ + Version originVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, originVer), E_OK); + + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + /** + * @tc.steps: step2. Put the data whose key is not empty and value is not empty into the database. + * @tc.expected: step2. Put returns E_OK + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + Version currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_GT(currentVer, originVer); + originVer = currentVer; + + EXPECT_EQ(g_transaction->Get(key, value), E_OK); + Value valueChanged; + /** + * @tc.steps: step3. Delete the data according the key. + * @tc.expected: step3. Delete returns E_OK. + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Delete(key), E_OK); + Value valueRead; + /** + * @tc.steps: step4. Get the value according the key. + * @tc.expected: step4. Get returns -E_NOT_FOUND. + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), -E_NOT_FOUND); + /** + * @tc.steps: step5. Get the current max version. + * @tc.expected: step5. The current max version is greater than the max version before delete. + */ + currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + ASSERT_GT(currentVer, originVer); +} + +/** + * @tc.name: MultiverStorage007 + * @tc.desc: Delete the non-existed data with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage007, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the current version. + */ + Version originVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, originVer), E_OK); + + Key key = {12, 57, 89}; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + Version currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_GT(currentVer, originVer); + originVer = currentVer; + g_transaction->ResetVersion(); + + Key newKey = {87, 68, 78}; + /** + * @tc.steps: step2. Delete the non-existed data according the key. + * @tc.expected: step2. Delete returns not E_OK. + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_NE(g_transaction->Delete(newKey), E_OK); + /** + * @tc.steps: step3. Get the current max version. + * @tc.expected: step2. The current max version is equal to the max version before delete. + */ + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_EQ(currentVer, originVer); +} + +/** + * @tc.name: MultiverStorage008 + * @tc.desc: Delete an empty key with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage008, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the current version. + */ + Version originVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, originVer), E_OK); + + Key key; + /** + * @tc.steps: step2. Delete the data whose key is empty from the empty database. + * @tc.steps: step3. Get the current max version. + * @tc.expected: step2. Delete returns not E_OK + * @tc.expected: step3. The current max version is equal to the max version before delete. + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_NE(g_transaction->Delete(key), E_OK); + Version currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_EQ(currentVer, originVer); + originVer = currentVer; + g_transaction->ResetVersion(); + + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + /** + * @tc.steps: step4. Put the non-empty key and non-empty value into the database. + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_NE(currentVer, originVer); + originVer = currentVer; + g_transaction->ResetVersion(); + key.clear(); + /** + * @tc.steps: step5. Delete the data whose key is empty from the non-empty database. + * @tc.steps: step6. Get the current max version. + * @tc.expected: step5. Delete returns not E_OK + * @tc.expected: step6. The current max version is equal to the max version before delete. + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_NE(g_transaction->Delete(key), E_OK); + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_EQ(currentVer, originVer); +} + +/** + * @tc.name: MultiverStorage009 + * @tc.desc: Clear the existed data with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage009, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the current version. + */ + Version originVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, originVer), E_OK); + Key key1, key2; + Value value1, value2; + + DistributedDBToolsUnitTest::GetRandomKeyValue(key1); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + DistributedDBToolsUnitTest::GetRandomKeyValue(key2); + DistributedDBToolsUnitTest::GetRandomKeyValue(value2); + + /** + * @tc.steps: step2. Put 2 entries into the database. + * @tc.expected: step2. Put returns E_OK + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Put(key1, value1), E_OK); + EXPECT_EQ(g_transaction->Put(key2, value2), E_OK); + Version currentVer = 0; + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_NE(currentVer, originVer); + originVer = currentVer; + g_transaction->ResetVersion(); + /** + * @tc.steps: step3. Clear data from the database. + * @tc.expected: step3. Clear returns E_OK + */ + g_transaction->SetVersion(originVer + 1); + EXPECT_EQ(g_transaction->Clear(), E_OK); + Value value1Read, value2Read; + /** + * @tc.steps: step4. Get the current max version. + * @tc.expected: step4. The current max version is greater than the max version before clear. + */ + EXPECT_EQ(g_transaction->GetMaxVersion(MultiVerDataType::ALL_TYPE, currentVer), E_OK); + EXPECT_GT(currentVer, originVer); + /** + * @tc.steps: step5. Get the put data before clear. + * @tc.expected: step5. Cannot get the data after clear. + */ + EXPECT_NE(g_transaction->Get(key1, value1Read), E_OK); + EXPECT_NE(g_transaction->Get(key2, value2Read), E_OK); +} + +/** + * @tc.name: MultiverStorage010 + * @tc.desc: Get the existed data with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage010, TestSize.Level1) +{ + /** + * @tc.steps: step1. Generate the random data. + */ + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + Value valueRead; + EXPECT_EQ(g_transaction->Get(key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value), true); +} + +/** + * @tc.name: MultiverStorage011 + * @tc.desc: Get the non-existed data with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage011, TestSize.Level1) +{ + /** + * @tc.steps: step1. Generate the random data. + */ + Key key; + Value value, valueRead; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + EXPECT_EQ(g_transaction->Get(key, valueRead), -E_NOT_FOUND); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + + /** + * @tc.steps: step2. Put the data whose key is not empty and value is not empty into the database. + * @tc.expected: step2. Put returns E_OK + */ + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + + key.push_back('7'); + /** + * @tc.steps: step3. Get the non-existed key. + * @tc.expected: step3. Get returns E_OK + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: MultiverStorage012 + * @tc.desc: Get the empty-key data with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage012, TestSize.Level1) +{ + /** + * @tc.steps: step1. Generate the random data. + */ + Key key; + Value value; + /** + * @tc.steps: step2. Get the value according the empty key from the empty database. + * @tc.expected: step2. Get returns not E_OK. + */ + EXPECT_NE(g_transaction->Get(key, value), E_OK); + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + /** + * @tc.steps: step3. Put the non-empty data into the database. + */ + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + Key keyEmpty; + /** + * @tc.steps: step4. Get the value according the empty key from the non-empty database. + * @tc.expected: step4. Get returns not E_OK. + */ + EXPECT_NE(g_transaction->Get(keyEmpty, value), E_OK); +} + +/** + * @tc.name: MultiverStorage013 + * @tc.desc: Get the deleted data with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage013, TestSize.Level1) +{ + /** + * @tc.steps: step1. Generate the random data. + */ + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + /** + * @tc.steps: step2. put the non-empty data into the database. + */ + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + EXPECT_EQ(g_transaction->Get(key, value), E_OK); + /** + * @tc.steps: step3. delete the data from the database. + */ + EXPECT_EQ(g_transaction->Delete(key), E_OK); + Value valueRead; + /** + * @tc.steps: step4. Get the value according the key. + * @tc.expected: step4. Get returns -E_NOT_FOUND. + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: MultiverStorage014 + * @tc.desc: Get the modified data with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage014, TestSize.Level1) +{ + /** + * @tc.steps: step1. Generate the random data. + */ + Key key; + Value value; + + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + /** + * @tc.steps: step2. put the non-empty data into the database. + */ + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + Value valueChanged = value; + valueChanged.push_back('H'); + /** + * @tc.steps: step3. update the data into the database. + */ + EXPECT_EQ(g_transaction->Put(key, valueChanged), E_OK); + Value valueRead; + /** + * @tc.steps: step4. Get the value according the key and check the value. + * @tc.expected: step4. Get returns E_OK and the read value is equal to the put value. + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, valueChanged), true); +} + +/** + * @tc.name: MultiverStorage015 + * @tc.desc: Get the data after clear with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage015, TestSize.Level1) +{ + /** + * @tc.steps: step1. Generate the random data. + */ + Key key; + Value value; + + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value); + + /** + * @tc.steps: step2. put the non-empty data into the database. + */ + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + /** + * @tc.steps: step3. clear the database. + */ + EXPECT_EQ(g_transaction->Clear(), E_OK); + Value valueRead; + /** + * @tc.steps: step4. get the data from the database after clear. + * @tc.expected: step4. Get returns -E_NOT_FOUND. + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), -E_NOT_FOUND); +} + +/** + * @tc.name: MultiverStorage016 + * @tc.desc: Get the new inserted data after clear with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage016, TestSize.Level1) +{ + /** + * @tc.steps: step1. Generate the random data. + */ + Key key1; + Value value1; + + DistributedDBToolsUnitTest::GetRandomKeyValue(key1); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + + /** + * @tc.steps: step2. put the non-empty data into the database. + */ + EXPECT_EQ(g_transaction->Put(key1, value1), E_OK); + /** + * @tc.steps: step3. clear the database. + */ + EXPECT_EQ(g_transaction->Clear(), E_OK); + Value valueRead; + EXPECT_EQ(g_transaction->Get(key1, valueRead), -E_NOT_FOUND); + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2); + /** + * @tc.steps: step4. re-put the data into the database using another value. + */ + EXPECT_EQ(g_transaction->Put(key1, value2), E_OK); + /** + * @tc.steps: step5. Get the value according the key and check the value. + * @tc.expected: step5. Get returns E_OK and the read value is equal to the new put value. + */ + EXPECT_EQ(g_transaction->Get(key1, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, value2), true); +} + +/** + * @tc.name: MultiverStorage017 + * @tc.desc: Get the new inserted data after delete with the transaction. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage017, TestSize.Level1) +{ + /** + * @tc.steps: step1. Get the random data. + */ + Key key; + Value value; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + DistributedDBToolsUnitTest::GetRandomKeyValue(value, 79); + + /** + * @tc.steps: step2. Put one data into the database. + */ + EXPECT_EQ(g_transaction->Put(key, value), E_OK); + /** + * @tc.steps: step3. Delete the data from the database. + */ + EXPECT_EQ(g_transaction->Delete(key), E_OK); + Value valueRead; + /** + * @tc.steps: step4. Get the data from the database according the key. + * @tc.expected: step4. Get returns -E_NOT_FOUND. + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), -E_NOT_FOUND); + Value valueChanged; + /** + * @tc.steps: step5. Put the same key, different value into the database. + */ + DistributedDBToolsUnitTest::GetRandomKeyValue(valueChanged, 178); + EXPECT_EQ(g_transaction->Put(key, valueChanged), E_OK); + /** + * @tc.steps: step6. Get the data from the database according the key. + * @tc.expected: step6. Get returns E_OK and the get value is equal to the value put int the step5. + */ + EXPECT_EQ(g_transaction->Get(key, valueRead), E_OK); + EXPECT_EQ(DistributedDBToolsUnitTest::IsValueEqual(valueRead, valueChanged), true); +} + +/** + * @tc.name: MultiverStorage018 + * @tc.desc: Get the batch inserted data through the non-empty prefix key. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage018, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the batch data into the database(3 entries have the same prefix key, + * and another has different prefix key). + */ + Entry entry1, entry2, entry3, entry4; + DistributedDBToolsUnitTest::GetRandomKeyValue(entry1.key, 97); + entry2.key = entry1.key; + entry2.key.push_back('W'); + entry3.key = entry1.key; + entry3.key.push_back('C'); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry4.key, 67); + + DistributedDBToolsUnitTest::GetRandomKeyValue(entry1.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry2.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry3.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry4.value); + + EXPECT_EQ(g_transaction->Put(entry1.key, entry1.value), E_OK); + EXPECT_EQ(g_transaction->Put(entry2.key, entry2.value), E_OK); + EXPECT_EQ(g_transaction->Put(entry3.key, entry3.value), E_OK); + EXPECT_EQ(g_transaction->Put(entry4.key, entry4.value), E_OK); + /** + * @tc.steps: step2. Get the batch data using the prefix key. + * @tc.expected: step2. GetEntries returns E_OK and the number of the result entries is E_OK. + */ + std::vector entriesRead; + EXPECT_EQ(g_transaction->GetEntries(entry1.key, entriesRead), E_OK); + EXPECT_EQ(entriesRead.size(), 3UL); + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry1, entriesRead), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry2, entriesRead), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry3, entriesRead), true); +} + +/** + * @tc.name: MultiverStorage019 + * @tc.desc: Get the non-existed data through the non-empty prefix key. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage019, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the batch data into the database. + */ + Entry entry1, entry2; + DistributedDBToolsUnitTest::GetRandomKeyValue(entry1.key, 97); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry2.key, 204); + + DistributedDBToolsUnitTest::GetRandomKeyValue(entry1.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry2.value); + + EXPECT_EQ(g_transaction->Put(entry1.key, entry1.value), E_OK); + EXPECT_EQ(g_transaction->Put(entry2.key, entry2.value), E_OK); + /** + * @tc.steps: step2. Get the batch data from the database using the prefix key different from the data put before. + * @tc.expected: step2. GetEntries returns -E_NOT_FOUND. + */ + std::vector entriesRead; + Key key; + DistributedDBToolsUnitTest::GetRandomKeyValue(key, 210); + EXPECT_EQ(g_transaction->GetEntries(key, entriesRead), -E_NOT_FOUND); +} + +/** + * @tc.name: MultiverStorage020 + * @tc.desc: Get all the data through the empty prefix key. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage020, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the data. + */ + Entry entry1, entry2, entry3; + DistributedDBToolsUnitTest::GetRandomKeyValue(entry1.key, 134); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry2.key, 204); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry3.key, 43); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry1.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry2.value); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry3.value); + + EXPECT_EQ(g_transaction->Put(entry1.key, entry1.value), E_OK); + EXPECT_EQ(g_transaction->Put(entry2.key, entry2.value), E_OK); + EXPECT_EQ(g_transaction->Put(entry3.key, entry3.value), E_OK); + /** + * @tc.steps: step2. Get the batch data from the database using the empty prefix key. + * @tc.expected: step2. GetEntries returns E_OK and . + */ + std::vector entriesRead; + Key keyEmpty; + EXPECT_EQ(g_transaction->GetEntries(keyEmpty, entriesRead), E_OK); + EXPECT_EQ(entriesRead.size(), 3UL); + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry1, entriesRead), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry2, entriesRead), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry3, entriesRead), true); +} + +/** + * @tc.name: MultiverStorage021 + * @tc.desc: Get the data through the empty prefix key for multiple put the same key data. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage021, TestSize.Level1) +{ + /** + * @tc.steps: step1. Put the same key, different value for 3 times into the database. + */ + Key key; + Value value1, value2, value3; + DistributedDBToolsUnitTest::GetRandomKeyValue(key); + + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, 46); + EXPECT_EQ(g_transaction->Put(key, value1), E_OK); + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, 28); + EXPECT_EQ(g_transaction->Put(key, value2), E_OK); + DistributedDBToolsUnitTest::GetRandomKeyValue(value3, 157); + EXPECT_EQ(g_transaction->Put(key, value3), E_OK); + /** + * @tc.steps: step2. Get the batch data from the database using the empty prefix key. + * @tc.expected: step2. GetEntries returns E_OK and the entries size is 1. + */ + std::vector entriesRead; + EXPECT_EQ(g_transaction->GetEntries(key, entriesRead), E_OK); + EXPECT_EQ(entriesRead.size(), 1UL); + Entry entry = {key, value3}; + + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry, entriesRead), true); +} + +/** + * @tc.name: MultiverStorage022 + * @tc.desc: Get the data through the empty prefix key for deleted data. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage022, TestSize.Level1) +{ + std::vector entries; + Entry entry; + DistributedDBToolsUnitTest::GetRandomKeyValue(entry.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry.value); + Entry entry1 = entry; + entry1.key.push_back('o'); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry1.value); + entries.push_back(entry); + entries.push_back(entry1); + /** + * @tc.steps: step1. Put the batch data. + */ + EXPECT_EQ(g_transaction->PutBatch(entries), E_OK); + std::vector keys = {entry.key, entry1.key}; + /** + * @tc.steps: step2. Delete the batch data. + */ + EXPECT_EQ(g_transaction->Delete(entry.key), E_OK); + EXPECT_EQ(g_transaction->Delete(entry1.key), E_OK); + + /** + * @tc.steps: step3. Get all the data. + * @tc.expected: step3. Returns -E_NOT_FOUND. + */ + Key keyEmpty; + std::vector entriesRead; + EXPECT_EQ(g_transaction->GetEntries(keyEmpty, entriesRead), -E_NOT_FOUND); +} + +/** + * @tc.name: MultiverStorage023 + * @tc.desc: Get the data through the empty prefix key for updated data. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage023, TestSize.Level1) +{ + Key key1, key2; + Value value1, value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, 10); + key2 = key1; + key2.push_back('S'); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, 46); + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, 28); + /** + * @tc.steps: step1. Put the batch data. + */ + EXPECT_EQ(g_transaction->Put(key1, value1), E_OK); + EXPECT_EQ(g_transaction->Put(key2, value2), E_OK); + Value value1Changed, value2Changed; + /** + * @tc.steps: step2. Update the batch data. + */ + DistributedDBToolsUnitTest::GetRandomKeyValue(value1Changed, 86); + DistributedDBToolsUnitTest::GetRandomKeyValue(value2Changed, 149); + EXPECT_EQ(g_transaction->Put(key1, value1Changed), E_OK); + EXPECT_EQ(g_transaction->Put(key2, value2Changed), E_OK); + Key keyEmpty; + /** + * @tc.steps: step3. Get all the data. + * @tc.expected: step3. the data are equal to the updated data. + */ + std::vector entriesRead; + EXPECT_EQ(g_transaction->GetEntries(keyEmpty, entriesRead), E_OK); + ASSERT_EQ(entriesRead.size(), 2UL); + + Entry entry1 = {key1, value1Changed}; + Entry entry2 = {key2, value2Changed}; + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry1, entriesRead), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry2, entriesRead), true); +} + +/** + * @tc.name: MultiverStorage024 + * @tc.desc: Get the data through the empty prefix key for cleared data. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage024, TestSize.Level1) +{ + Key key1, key2; + Value value1, value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, 10); + DistributedDBToolsUnitTest::GetRandomKeyValue(key2, 20); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1); + DistributedDBToolsUnitTest::GetRandomKeyValue(value2); + /** + * @tc.steps: step1. Put the batch data. + */ + EXPECT_EQ(g_transaction->Put(key1, value1), E_OK); + EXPECT_EQ(g_transaction->Put(key2, value2), E_OK); + Key keyEmpty; + std::vector entriesRead; + /** + * @tc.steps: step2. Get all the data. + * @tc.expected: step2. the data are equal to the data put before. + */ + EXPECT_EQ(g_transaction->GetEntries(keyEmpty, entriesRead), E_OK); + ASSERT_EQ(entriesRead.size(), 2UL); + /** + * @tc.steps: step3. Clear the data and get all the data. + * @tc.expected: step3. Get returns -E_NOT_FOUND. + */ + EXPECT_EQ(g_transaction->Clear(), E_OK); + EXPECT_EQ(g_transaction->GetEntries(keyEmpty, entriesRead), -E_NOT_FOUND); +} + +/** + * @tc.name: MultiverStorage025 + * @tc.desc: Get the data through the put, delete, re-put operation. + * @tc.type: FUNC + * @tc.require: AR000C6TRV AR000CQDTM + * @tc.author: wangbingquan + */ +HWTEST_F(DistributedDBStorageTransactionRecordTest, MultiverStorage025, TestSize.Level1) +{ + std::vector entries; + Entry entry; + DistributedDBToolsUnitTest::GetRandomKeyValue(entry.key); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry.value); + Entry entry1 = entry; + entry1.key.push_back('q'); + DistributedDBToolsUnitTest::GetRandomKeyValue(entry1.value); + entries.push_back(entry); + entries.push_back(entry1); + /** + * @tc.steps: step1. Put the batch data. + */ + EXPECT_EQ(g_transaction->PutBatch(entries), E_OK); + std::vector keys = {entry.key, entry1.key}; + /** + * @tc.steps: step2. Delete the batch data. + */ + EXPECT_EQ(g_transaction->Delete(entry.key), E_OK); + EXPECT_EQ(g_transaction->Delete(entry1.key), E_OK); + /** + * @tc.steps: step3. Get all the data. + * @tc.expected: step3. Get results -E_NOT_FOUND. + */ + Key keyEmpty; + std::vector entriesRead; + EXPECT_EQ(g_transaction->GetEntries(keyEmpty, entriesRead), -E_NOT_FOUND); + entry.value.push_back('q'); + entry1.value.push_back('s'); + Value valueRead, valueRead1; + entriesRead.clear(); + /** + * @tc.steps: step5. Re-put the different value into the database. + */ + EXPECT_EQ(g_transaction->Put(entry.key, valueRead), E_OK); + EXPECT_EQ(g_transaction->Put(entry1.key, valueRead1), E_OK); + /** + * @tc.steps: step6. Get all the data. + * @tc.expected: step6. Get results E_OK and the data are equal to the inserted data after deleted operation. + */ + EXPECT_EQ(g_transaction->GetEntries(keyEmpty, entriesRead), E_OK); + ASSERT_EQ(entriesRead.size(), 2UL); + + entry.value.clear(); + entry1.value.clear(); + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry, entriesRead), true); + EXPECT_EQ(DistributedDBToolsUnitTest::IsKvEntryExist(entry1, entriesRead), true); +} + diff --git a/mock/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.cpp b/mock/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a69f282f0cec87995a4cf48146c7becaa5a50f2 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "multi_ver_vacuum_executor_stub.h" +#include +#include "db_errno.h" + +using namespace DistributedDB; + +MultiVerVacuumExecutorStub::MultiVerVacuumExecutorStub(const DbScale &inScale, int timeCostEachCall) + : dbScale_(inScale), timeCostEachCall_(timeCostEachCall), transactionOccupied_(false) +{ +} + +MultiVerVacuumExecutorStub::~MultiVerVacuumExecutorStub() +{ +} + +bool MultiVerVacuumExecutorStub::IsTransactionOccupied() +{ + return transactionOccupied_; +} + +int MultiVerVacuumExecutorStub::GetVacuumAbleCommits(std::list &leftBranchCommits, + std::list &rightBranchCommits) const +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + for (uint8_t i = dbScale_.left + dbScale_.right; i > dbScale_.right; i--) { + MultiVerCommitInfo commit; + commit.version = i; + commit.commitId.push_back(i); + leftBranchCommits.push_back(commit); + } + for (uint8_t i = dbScale_.right; i > 0; i--) { + MultiVerCommitInfo commit; + commit.version = i; + commit.commitId.push_back(i); + rightBranchCommits.push_back(commit); + } + return E_OK; +} + +int MultiVerVacuumExecutorStub::GetVacuumNeedRecordsByVersion(uint64_t version, + std::list &vacuumNeedRecords) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + for (uint8_t i = dbScale_.vacuumNeed; i > 0; i--) { + MultiVerRecordInfo record; + record.type = RecordType::VALID; + record.version = version; + record.hashKey.push_back(i); + vacuumNeedRecords.push_back(record); + } + return E_OK; +} + +int MultiVerVacuumExecutorStub::GetShadowRecordsOfClearTypeRecord(uint64_t version, + const std::vector &hashKey, std::list &shadowRecords) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + return E_OK; +} + +int MultiVerVacuumExecutorStub::GetShadowRecordsOfNonClearTypeRecord(uint64_t version, + const std::vector &hashKey, std::list &shadowRecords) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + for (uint8_t i = dbScale_.shadow; i > 0; i--) { + MultiVerRecordInfo record; + record.type = RecordType::VALID; + record.version = i; + record.hashKey = hashKey; + shadowRecords.push_back(record); + } + return E_OK; +} + +int MultiVerVacuumExecutorStub::StartTransactionForVacuum() +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + transactionOccupied_ = true; + return E_OK; +} + +int MultiVerVacuumExecutorStub::CommitTransactionForVacuum() +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + transactionOccupied_ = false; + return E_OK; +} + +int MultiVerVacuumExecutorStub::RollBackTransactionForVacuum() +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + transactionOccupied_ = false; + return E_OK; +} + +int MultiVerVacuumExecutorStub::DeleteRecordTotally(uint64_t version, const std::vector &hashKey) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + return E_OK; +} + +int MultiVerVacuumExecutorStub::MarkRecordAsVacuumDone(uint64_t version, const std::vector &hashKey) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + return E_OK; +} + +int MultiVerVacuumExecutorStub::MarkCommitAsVacuumDone(const std::vector &commitId) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(timeCostEachCall_)); + return E_OK; +} diff --git a/mock/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.h b/mock/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..a96f02aa2f2cdc99ecb750c622167cc8dc5aae0e --- /dev/null +++ b/mock/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MULTI_VER_VACUUM_EXECUTOR_STUB_H +#define MULTI_VER_VACUUM_EXECUTOR_STUB_H + +#include +#include "multi_ver_vacuum_executor.h" + +namespace DistributedDB { +struct DbScale { + uint8_t left = 1; + uint8_t right = 1; + uint8_t vacuumNeed = 1; + uint8_t shadow = 1; +}; + +class MultiVerVacuumExecutorStub : public MultiVerVacuumExecutor { +public: + // Total Time: (3 + 2L + 2LT + LTS + 2R + RT) Multiple timeCostEachCall(In Millisecond) + MultiVerVacuumExecutorStub(const DbScale &inScale, int timeCostEachCall); + ~MultiVerVacuumExecutorStub(); + + bool IsTransactionOccupied(); + + int GetVacuumAbleCommits(std::list &leftBranchCommits, + std::list &rightBranchCommits) const; + int GetVacuumNeedRecordsByVersion(uint64_t version, std::list &vacuumNeedRecords); + int GetShadowRecordsOfClearTypeRecord(uint64_t version, const std::vector &hashKey, + std::list &shadowRecords); + int GetShadowRecordsOfNonClearTypeRecord(uint64_t version, const std::vector &hashKey, + std::list &shadowRecords); + + int StartTransactionForVacuum(); + int CommitTransactionForVacuum(); + int RollBackTransactionForVacuum(); + + int DeleteRecordTotally(uint64_t version, const std::vector &hashKey); + int MarkRecordAsVacuumDone(uint64_t version, const std::vector &hashKey); + int MarkCommitAsVacuumDone(const std::vector &commitId); +private: + DbScale dbScale_; + int timeCostEachCall_; + std::atomic transactionOccupied_; +}; +} + +#endif // MULTI_VER_VACUUM_EXECUTOR_STUB_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_ability_sync_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_ability_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bd9ec951f80ba9373c9a24020712afed40bb09e7 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_ability_sync_test.cpp @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ability_sync.h" +#include "distributeddb_tools_unit_test.h" +#include "single_ver_kv_sync_task_context.h" +#include "sync_types.h" +#include "version.h" +#include "virtual_communicator_aggregator.h" +#include "virtual_single_ver_sync_db_Interface.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; + +namespace { + const std::string DEVICE_A = "deviceA"; + const std::string DEVICE_B = "deviceB"; + const std::string TEST_SCHEMA = "{\"SCHEMA_DEFINE\":{\"value\":\"LONG\"},\"SCHEMA_MODE\":\"COMPATIBLE\"," + "\"SCHEMA_VERSION\":\"1.0\"}"; + + VirtualSingleVerSyncDBInterface *g_syncInterface = nullptr; + VirtualCommunicatorAggregator *g_communicatorAggregator = nullptr; + + ICommunicator *g_communicatorA = nullptr; + ICommunicator *g_communicatorB = nullptr; + std::shared_ptr g_meta = nullptr; +} + +class DistributedDBAbilitySyncTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBAbilitySyncTest::SetUpTestCase(void) +{ + /** + * @tc.setup: NA + */ +} + +void DistributedDBAbilitySyncTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: NA + */ +} + +void DistributedDBAbilitySyncTest::SetUp(void) +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: create the instance for virtual communicator, virtual storage + */ + g_syncInterface = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(g_syncInterface != nullptr); + g_syncInterface->SetSchemaInfo(TEST_SCHEMA); + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator; + ASSERT_TRUE(g_communicatorAggregator != nullptr); + int errCode = E_OK; + g_communicatorA = g_communicatorAggregator->AllocCommunicator(DEVICE_A, errCode); + ASSERT_TRUE(g_communicatorA != nullptr); + g_communicatorB = g_communicatorAggregator->AllocCommunicator(DEVICE_B, errCode); + ASSERT_TRUE(g_communicatorB != nullptr); + g_meta = std::make_shared(); + g_meta->Initialize(g_syncInterface); +} + +void DistributedDBAbilitySyncTest::TearDown(void) +{ + /** + * @tc.teardown: delete the ptr for testing + */ + if (g_communicatorA != nullptr && g_communicatorAggregator != nullptr) { + g_communicatorAggregator->ReleaseCommunicator(g_communicatorA); + g_communicatorA = nullptr; + } + if (g_communicatorB != nullptr && g_communicatorAggregator != nullptr) { + g_communicatorAggregator->ReleaseCommunicator(g_communicatorB); + g_communicatorB = nullptr; + } + if (g_communicatorAggregator != nullptr) { + RefObject::KillAndDecObjRef(g_communicatorAggregator); + g_communicatorAggregator = nullptr; + } + if (g_syncInterface != nullptr) { + delete g_syncInterface; + g_syncInterface = nullptr; + } +} + +/** + * @tc.name: RequestPacketTest001 + * @tc.desc: Verify RequestPacketSerialization and RequestPacketDeSerialization function. + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBAbilitySyncTest, RequestPacketTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create a AbilityRequestPacket packet1 + * @tc.steps: step2. set version = ABILITY_SYNC_VERSION_V1. schema = TEST_SCHEMA. + */ + AbilitySyncRequestPacket packet1; + DbAbility ability1; +#ifndef OMIT_ZLIB + ability1.SetAbilityItem(SyncConfig::DATABASE_COMPRESSION_ZLIB, SUPPORT_MARK); +#endif + packet1.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet1.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + packet1.SetSchema(TEST_SCHEMA); + packet1.SetSendCode(E_OK); + packet1.SetDbAbility(ability1); + Message msg1(ABILITY_SYNC_MESSAGE); + msg1.SetMessageType(TYPE_REQUEST); + msg1.SetCopiedObject(packet1); + + /** + * @tc.steps: step3. call Serialization to Serialization the msg + * @tc.expected: step3. Serialization return E_OK + */ + uint32_t bufflen = packet1.CalculateLen(); + ASSERT_TRUE(bufflen != 0); + std::vector buff(bufflen, 0); + ASSERT_TRUE(AbilitySync::Serialization(buff.data(), bufflen, &msg1) == E_OK); + + /** + * @tc.steps: step4. call DeSerialization to DeSerialization the buff + * @tc.expected: step4. DeSerialization return E_OK + */ + Message msg2(ABILITY_SYNC_MESSAGE); + msg2.SetMessageType(TYPE_REQUEST); + ASSERT_TRUE(AbilitySync::DeSerialization(buff.data(), bufflen, &msg2) == E_OK); + const AbilitySyncRequestPacket *packet2 = msg2.GetObject(); + ASSERT_TRUE(packet2 != nullptr); + + /** + * @tc.expected: step5. packet1 == packet2 + */ + EXPECT_TRUE(packet2->GetProtocolVersion() == ABILITY_SYNC_VERSION_V1); + EXPECT_TRUE(packet2->GetSoftwareVersion() == SOFTWARE_VERSION_CURRENT); + EXPECT_TRUE(packet2->GetSendCode() == E_OK); + EXPECT_TRUE(packet2->GetDbAbility() == ability1); + std::string schema = packet2->GetSchema(); + EXPECT_EQ(schema, TEST_SCHEMA); +} + +/** + * @tc.name: RequestPacketTest002 + * @tc.desc: Verify RequestPacketSerialization and RequestPacketDeSerialization function when version not support. + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBAbilitySyncTest, RequestPacketTest002, TestSize.Level0) +{ + /** + * @tc.steps: step1. create a AbilityRequestPacket packet1 + * @tc.steps: step2. set version = ABILITY_SYNC_VERSION_V1 + 1. schema = TEST_SCHEMA. + */ + AbilitySyncRequestPacket packet1; + packet1.SetProtocolVersion(ABILITY_SYNC_VERSION_V1 + 1); + packet1.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + packet1.SetSchema(""); + Message msg1(ABILITY_SYNC_MESSAGE); + msg1.SetMessageType(TYPE_REQUEST); + msg1.SetCopiedObject(packet1); + + /** + * @tc.steps: step3. call Serialization to Serialization the msg + * @tc.expected: step3. Serialization return E_OK + */ + uint32_t bufflen = packet1.CalculateLen(); + ASSERT_TRUE(bufflen != 0); + std::vector buff(bufflen, 0); + ASSERT_TRUE(AbilitySync::Serialization(buff.data(), bufflen, &msg1) == E_OK); + + /** + * @tc.steps: step4. call DeSerialization to DeSerialization the buff + * @tc.expected: step4. DeSerialization return E_OK + */ + Message msg2(ABILITY_SYNC_MESSAGE); + msg2.SetMessageType(TYPE_REQUEST); + ASSERT_TRUE(AbilitySync::DeSerialization(buff.data(), bufflen, &msg2) == E_OK); + const AbilitySyncRequestPacket *packet2 = msg2.GetObject(); + ASSERT_TRUE(packet2 != nullptr); + + /** + * @tc.expected: step5. packet2->GetSendCode() == -E_VERSION_NOT_SUPPORT + */ + EXPECT_TRUE(packet2->GetSendCode() == -E_VERSION_NOT_SUPPORT); +} + +/** + * @tc.name: RequestPacketTest003 + * @tc.desc: Verify RequestPacketSerialization and RequestPacketDeSerialization function. + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBAbilitySyncTest, RequestPacketTest003, TestSize.Level0) +{ + /** + * @tc.steps: step1. create a AbilityRequestPacket packet1 + * @tc.steps: step2. set version = ABILITY_SYNC_VERSION_V1. schema = TEST_SCHEMA. + */ + AbilitySyncRequestPacket packet1; + packet1.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet1.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + packet1.SetSchema(TEST_SCHEMA); + packet1.SetSendCode(E_OK); + int secLabel = 3; // label 3 + int secFlag = 1; // flag 1 + packet1.SetSecLabel(secLabel); + packet1.SetSecFlag(secFlag); + Message msg1(ABILITY_SYNC_MESSAGE); + msg1.SetMessageType(TYPE_REQUEST); + msg1.SetCopiedObject(packet1); + + /** + * @tc.steps: step3. call Serialization to Serialization the msg + * @tc.expected: step3. Serialization return E_OK + */ + uint32_t bufflen = packet1.CalculateLen(); + ASSERT_TRUE(bufflen != 0); + std::vector buff(bufflen, 0); + ASSERT_TRUE(AbilitySync::Serialization(buff.data(), bufflen, &msg1) == E_OK); + + /** + * @tc.steps: step4. call DeSerialization to DeSerialization the buff + * @tc.expected: step4. DeSerialization return E_OK + */ + Message msg2(ABILITY_SYNC_MESSAGE); + msg2.SetMessageType(TYPE_REQUEST); + ASSERT_TRUE(AbilitySync::DeSerialization(buff.data(), bufflen, &msg2) == E_OK); + const AbilitySyncRequestPacket *packet2 = msg2.GetObject(); + ASSERT_TRUE(packet2 != nullptr); + + /** + * @tc.expected: step5. packet1 == packet2 + */ + EXPECT_TRUE(packet2->GetProtocolVersion() == ABILITY_SYNC_VERSION_V1); + EXPECT_TRUE(packet2->GetSoftwareVersion() == SOFTWARE_VERSION_CURRENT); + EXPECT_TRUE(packet2->GetSendCode() == E_OK); + std::string schema = packet2->GetSchema(); + EXPECT_EQ(schema, TEST_SCHEMA); + EXPECT_TRUE(packet2->GetSecFlag() == secFlag); + EXPECT_TRUE(packet2->GetSecLabel() == secLabel); +} + +/** + * @tc.name: RequestPacketTest004 + * @tc.desc: Verify RequestPacketSerialization and RequestPacketDeSerialization function. + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBAbilitySyncTest, RequestPacketTest004, TestSize.Level0) +{ + /** + * @tc.steps: step1. create a AbilityRequestPacket packet1 + * @tc.steps: step2. set version = ABILITY_SYNC_VERSION_V1. schema = TEST_SCHEMA. + */ + AbilitySyncRequestPacket packet1; + packet1.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet1.SetSoftwareVersion(SOFTWARE_VERSION_RELEASE_2_0); + packet1.SetSchema(TEST_SCHEMA); + packet1.SetSendCode(E_OK); + int secLabel = 3; // label 3 + int secFlag = 1; // flag 1 + packet1.SetSecLabel(secLabel); + packet1.SetSecFlag(secFlag); + Message msg1(ABILITY_SYNC_MESSAGE); + msg1.SetMessageType(TYPE_REQUEST); + msg1.SetCopiedObject(packet1); + + /** + * @tc.steps: step3. call Serialization to Serialization the msg + * @tc.expected: step3. Serialization return E_OK + */ + uint32_t bufflen = packet1.CalculateLen(); + ASSERT_TRUE(bufflen != 0); + std::vector buff(bufflen, 0); + ASSERT_TRUE(AbilitySync::Serialization(buff.data(), bufflen, &msg1) == E_OK); + + /** + * @tc.steps: step4. call DeSerialization to DeSerialization the buff + * @tc.expected: step4. DeSerialization return E_OK + */ + Message msg2(ABILITY_SYNC_MESSAGE); + msg2.SetMessageType(TYPE_REQUEST); + ASSERT_TRUE(AbilitySync::DeSerialization(buff.data(), bufflen, &msg2) == E_OK); + const AbilitySyncRequestPacket *packet2 = msg2.GetObject(); + ASSERT_TRUE(packet2 != nullptr); + + /** + * @tc.expected: step5. packet1 == packet2 + */ + EXPECT_TRUE(packet2->GetProtocolVersion() == ABILITY_SYNC_VERSION_V1); + EXPECT_TRUE(packet2->GetSoftwareVersion() == SOFTWARE_VERSION_RELEASE_2_0); + EXPECT_TRUE(packet2->GetSendCode() == E_OK); + std::string schema = packet2->GetSchema(); + EXPECT_EQ(schema, TEST_SCHEMA); + EXPECT_TRUE(packet2->GetSecFlag() == 0); + EXPECT_TRUE(packet2->GetSecLabel() == 0); +} + +/** + * @tc.name: AckPacketTest001 + * @tc.desc: Verify AckPacketSerialization and AckPacketDeSerialization function. + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBAbilitySyncTest, AckPacketTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create a AbilityAckPacket packet1 + * @tc.steps: step2. set version = ABILITY_SYNC_VERSION_V1. schema = TEST_SCHEMA. + */ + AbilitySyncAckPacket packet1; + DbAbility ability1; +#ifndef OMIT_ZLIB + ability1.SetAbilityItem(SyncConfig::DATABASE_COMPRESSION_ZLIB, SUPPORT_MARK); +#endif + packet1.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet1.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + packet1.SetSchema(TEST_SCHEMA); + packet1.SetAckCode(E_VERSION_NOT_SUPPORT); + packet1.SetDbAbility(ability1); + Message msg1(ABILITY_SYNC_MESSAGE); + msg1.SetMessageType(TYPE_RESPONSE); + msg1.SetCopiedObject(packet1); + + /** + * @tc.steps: step3. call Serialization to Serialization the msg + * @tc.expected: step3. Serialization return E_OK + */ + uint32_t bufflen = packet1.CalculateLen(); + ASSERT_TRUE(bufflen != 0); + std::vector buff(bufflen, 0); + ASSERT_EQ(AbilitySync::Serialization(buff.data(), bufflen, &msg1), E_OK); + + /** + * @tc.steps: step4. call DeSerialization to DeSerialization the buff + * @tc.expected: step4. DeSerialization return E_OK + */ + Message msg2(ABILITY_SYNC_MESSAGE); + msg2.SetMessageType(TYPE_RESPONSE); + ASSERT_TRUE(AbilitySync::DeSerialization(buff.data(), bufflen, &msg2) == E_OK); + const AbilitySyncAckPacket *packet2 = msg2.GetObject(); + ASSERT_TRUE(packet2 != nullptr); + + /** + * @tc.expected: step5. packet1 == packet2 + */ + EXPECT_TRUE(packet2->GetProtocolVersion() == ABILITY_SYNC_VERSION_V1); + EXPECT_TRUE(packet2->GetSoftwareVersion() == SOFTWARE_VERSION_CURRENT); + EXPECT_TRUE(packet2->GetAckCode() == E_VERSION_NOT_SUPPORT); + EXPECT_TRUE(packet2->GetDbAbility() == ability1); + std::string schema = packet2->GetSchema(); + ASSERT_TRUE(schema == TEST_SCHEMA); +} + +/** + * @tc.name: SyncStartTest001 + * @tc.desc: Verify Ability sync SyncStart function. + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBAbilitySyncTest, SyncStart001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create a AbilitySync + */ + AbilitySync async; + async.Initialize(g_communicatorB, g_syncInterface, g_meta, DEVICE_A); + + /** + * @tc.steps: step2. call SyncStart + * @tc.expected: step2. SyncStart return E_OK + */ + EXPECT_EQ(async.SyncStart(1, 1, 1), E_OK); + + /** + * @tc.steps: step3. disable the communicator + */ + static_cast(g_communicatorB)->Disable(); + + /** + * @tc.steps: step4. call SyncStart + * @tc.expected: step4. SyncStart return -E_PERIPHERAL_INTERFACE_FAIL + */ + EXPECT_TRUE(async.SyncStart(1, 1, 1) == -E_PERIPHERAL_INTERFACE_FAIL); +} +#ifndef OMIT_JSON +/** + * @tc.name: RequestReceiveTest001 + * @tc.desc: Verify Ability RequestReceive callback. + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBAbilitySyncTest, RequestReceiveTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create a AbilitySync + */ + AbilitySync async; + async.Initialize(g_communicatorB, g_syncInterface, g_meta, DEVICE_A); + + /** + * @tc.steps: step2. call RequestRecv, set inMsg nullptr or set context nullptr + * @tc.expected: step2. RequestRecv return -E_INVALID_ARGS + */ + Message msg1(ABILITY_SYNC_MESSAGE); + msg1.SetMessageType(TYPE_REQUEST); + SingleVerSyncTaskContext *context = new (std::nothrow) SingleVerKvSyncTaskContext(); + ASSERT_TRUE(context != nullptr); + EXPECT_EQ(async.RequestRecv(nullptr, context), -E_INVALID_ARGS); + EXPECT_EQ(async.RequestRecv(&msg1, nullptr), -E_INVALID_ARGS); + + /** + * @tc.steps: step3. call RequestRecv, set inMsg with no packet + * @tc.expected: step3. RequestRecv return -E_INVALID_ARGS + */ + EXPECT_EQ(async.RequestRecv(&msg1, context), -E_INVALID_ARGS); + + /** + * @tc.steps: step4. create a AbilityRequestkPacket packet1 + */ + AbilitySyncRequestPacket packet1; + packet1.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet1.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + packet1.SetSchema(TEST_SCHEMA); + msg1.SetCopiedObject(packet1); + + /** + * @tc.steps: step5. call RequestRecv, set inMsg with packet + * @tc.expected: step5. RequestRecv return ok, GetRemoteSoftwareVersion is SOFTWARE_VERSION_CURRENT + * IsSchemaCompatible true + * + */ + EXPECT_EQ(async.RequestRecv(&msg1, context), OK); + EXPECT_TRUE(context->GetRemoteSoftwareVersion() == SOFTWARE_VERSION_CURRENT); + EXPECT_TRUE(context->GetTaskErrCode() != -E_SCHEMA_MISMATCH); + + /** + * @tc.steps: step6. call RequestRecv, set inMsg sendCode -E_VERSION_NOT_SUPPORT + * @tc.expected: step6. RequestRecv return E_VERSION_NOT_SUPPORT + */ + packet1.SetSendCode(-E_VERSION_NOT_SUPPORT); + msg1.SetCopiedObject(packet1); + EXPECT_EQ(async.RequestRecv(&msg1, context), -E_VERSION_NOT_SUPPORT); + + /** + * @tc.steps: step7. call RequestRecv, SetSchema "" + * @tc.expected: step7. IsSchemaCompatible false + */ + packet1.SetSchema(""); + packet1.SetSendCode(E_OK); + msg1.SetCopiedObject(packet1); + EXPECT_EQ(async.RequestRecv(&msg1, context), E_OK); + EXPECT_FALSE(context->GetTaskErrCode() != -E_SCHEMA_MISMATCH); + RefObject::KillAndDecObjRef(context); +} +#endif +/** + * @tc.name: AckReceiveTest001 + * @tc.desc: Verify Ability AckReceive callback. + * @tc.type: FUNC + * @tc.require: AR000DR9K4 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBAbilitySyncTest, AckReceiveTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create a AbilitySync + */ + AbilitySync async; + async.Initialize(g_communicatorB, g_syncInterface, g_meta, DEVICE_A); + + /** + * @tc.steps: step2. call AckRecv, set inMsg nullptr or set context nullptr + * @tc.expected: step2. AckRecv return -E_INVALID_ARGS + */ + SingleVerSyncTaskContext *context = new (std::nothrow) SingleVerKvSyncTaskContext(); + ASSERT_TRUE(context != nullptr); + Message msg1(ABILITY_SYNC_MESSAGE); + msg1.SetMessageType(TYPE_RESPONSE); + EXPECT_EQ(async.AckRecv(nullptr, context), -E_INVALID_ARGS); + EXPECT_EQ(async.AckRecv(&msg1, nullptr), -E_INVALID_ARGS); + + /** + * @tc.steps: step3. call AckRecv, set inMsg with no packet + * @tc.expected: step3. AckRecv return -E_INVALID_ARGS + */ + EXPECT_EQ(async.AckRecv(&msg1, context), -E_INVALID_ARGS); + ASSERT_TRUE(context != nullptr); + + /** + * @tc.steps: step4. create a AbilityAckPacket packet1 + */ + AbilitySyncAckPacket packet1; + packet1.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet1.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + packet1.SetAckCode(E_OK); + packet1.SetSchema(TEST_SCHEMA); + msg1.SetCopiedObject(packet1); + + /** + * @tc.steps: step5. call AckRecv, set inMsg with packet + * @tc.expected: step5. AckRecv return ok GetRemoteSoftwareVersion is SOFTWARE_VERSION_CURRENT + * IsSchemaCompatible true; + */ + EXPECT_EQ(async.AckRecv(&msg1, context), OK); + EXPECT_TRUE(context->GetRemoteSoftwareVersion() == SOFTWARE_VERSION_CURRENT); + EXPECT_TRUE(context->GetTaskErrCode() != -E_SCHEMA_MISMATCH); + + /** + * @tc.steps: step6. call RequestRecv, SetSchema "" + * @tc.expected: step6. IsSchemaCompatible false + */ + packet1.SetSchema(""); + msg1.SetCopiedObject(packet1); + EXPECT_EQ(async.AckRecv(&msg1, context), E_OK); + + /** + * @tc.steps: step7. call AckRecv, set inMsg sendCode -E_VERSION_NOT_SUPPORT + * @tc.expected: step7. return -E_VERSION_NOT_SUPPORT + */ + packet1.SetSchema(TEST_SCHEMA); + packet1.SetAckCode(-E_VERSION_NOT_SUPPORT); + msg1.SetCopiedObject(packet1); + EXPECT_EQ(async.AckRecv(&msg1, context), -E_VERSION_NOT_SUPPORT); + RefObject::KillAndDecObjRef(context); +} diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_anti_dos_sync_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_anti_dos_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..13e1b2835e1a81c3e4711ae435ebb45c6edbf0fa --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_anti_dos_sync_test.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "generic_single_ver_kv_entry.h" +#include "message.h" +#include "meta_data.h" +#include "ref_object.h" +#include "single_ver_data_sync.h" +#include "single_ver_sync_engine.h" +#include "version.h" +#include "virtual_communicator_aggregator.h" +#include "virtual_single_ver_sync_db_Interface.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const string ANTI_DOS_STORE_ID = "anti_dos_sync_test"; +#ifndef RELATIONAL_STORE + const int NUM = 108; +#else + const int NUM = 120; +#endif + const int WAIT_LONG_TIME = 26000; + const int WAIT_SHORT_TIME = 18000; + const int TEST_ONE = 2; + const int TEST_TWO = 10; + const int TEST_THREE_THREAD = 20; + const int TEST_THREE_OUTDATA = 2048; + const int TEST_THREE_DATATIEM = 1024; + const int LIMIT_QUEUE_CACHE_SIZE = 1024 * 1024; + const int DEFAULT_CACHE_SIZE = 160 * 1024 * 1024; // Initial the default cache size of queue as 160MB + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate* g_kvDelegatePtr = nullptr; + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + std::shared_ptr g_metaData = nullptr; + SingleVerSyncEngine *g_syncEngine = nullptr; + VirtualCommunicator *g_communicator = nullptr; + VirtualSingleVerSyncDBInterface *g_syncInterface = nullptr; + + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); +} + +class DistributeddbAntiDosSyncTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributeddbAntiDosSyncTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Init datadir and VirtualCommunicatorAggregator. + */ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); +} + +void DistributeddbAntiDosSyncTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Release VirtualCommunicatorAggregator and clear data dir. + */ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); +} + +void DistributeddbAntiDosSyncTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: create VirtualCommunicator, VirtualSingleVerSyncDBInterface, SyncEngine, + * and set maximum cache of queue. + */ + const std::string remoteDeviceId = "real_device"; + KvStoreNbDelegate::Option option = {true}; + g_mgr.GetKvStore(ANTI_DOS_STORE_ID, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + g_syncInterface = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(g_syncInterface != nullptr); + g_metaData = std::make_shared(); + int errCodeMetaData = g_metaData->Initialize(g_syncInterface); + ASSERT_TRUE(errCodeMetaData == E_OK); + g_syncEngine = new (std::nothrow) SingleVerSyncEngine(); + ASSERT_TRUE(g_syncEngine != nullptr); + int errCodeSyncEngine = g_syncEngine->Initialize(g_syncInterface, g_metaData, nullptr, nullptr, nullptr); + ASSERT_TRUE(errCodeSyncEngine == E_OK); + g_communicator = static_cast(g_communicatorAggregator->GetCommunicator(remoteDeviceId)); + ASSERT_TRUE(g_communicator != nullptr); + g_syncEngine->SetMaxQueueCacheSize(LIMIT_QUEUE_CACHE_SIZE); +} + +void DistributeddbAntiDosSyncTest::TearDown(void) +{ + /** + * @tc.teardown: Release VirtualCommunicator, VirtualSingleVerSyncDBInterface and SyncEngine. + */ + if (g_communicator != nullptr) { + g_communicator->KillObj(); + g_communicator = nullptr; + } + if (g_syncEngine != nullptr) { + g_syncEngine->SetMaxQueueCacheSize(DEFAULT_CACHE_SIZE); + auto syncEngine = g_syncEngine; + g_syncEngine->OnKill([syncEngine]() { syncEngine->Close(); }); + RefObject::KillAndDecObjRef(g_syncEngine); + g_syncEngine = nullptr; + } + g_metaData = nullptr; + if (g_syncInterface != nullptr) { + delete g_syncInterface; + g_syncInterface = nullptr; + } + if (g_kvDelegatePtr != nullptr) { + g_mgr.CloseKvStore(g_kvDelegatePtr); + g_kvDelegatePtr = nullptr; + } + g_mgr.DeleteKvStore(ANTI_DOS_STORE_ID); +} + +/** + * @tc.name: Anti Dos attack Sync 001 + * @tc.desc: Whether function run normally when the amount of message is lower than the maximum of threads + * and the whole length of message is lower than the maximum size of queue. + * @tc.type: FUNC + * @tc.require: AR000D08KU + * @tc.author: yiguang + */ +HWTEST_F(DistributeddbAntiDosSyncTest, AntiDosAttackSync001, TestSize.Level3) +{ + /** + * @tc.steps: step1. control MessageReceiveCallback to send messages, whose number is lower than + * the maximum of threads and length is lower than the maximum size of queue. + */ + const std::string srcTarget = "001"; + std::vector outData; + + for (unsigned int index = 0; index < g_syncEngine->GetMaxExecNum() - TEST_ONE; index++) { + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + ASSERT_TRUE(packet != nullptr); + Message *message = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + ASSERT_TRUE(message != nullptr); + + GenericSingleVerKvEntry *kvEntry = new (std::nothrow) GenericSingleVerKvEntry(); + ASSERT_TRUE(kvEntry != nullptr); + outData.push_back(kvEntry); + packet->SetData(outData); + packet->SetSendCode(E_OK); + packet->SetVersion(SOFTWARE_VERSION_CURRENT); + uint32_t sessionId = index; + uint32_t sequenceId = index; + message->SetMessageType(TYPE_REQUEST); + message->SetTarget(srcTarget); + int errCode = message->SetExternalObject(packet); + ASSERT_TRUE(errCode == E_OK); + message->SetSessionId(sessionId); + message->SetSequenceId(sequenceId); + g_communicator->CallbackOnMessage(srcTarget, message); + /** + * @tc.expected: step1. no message was found to be enqueued and discarded. + */ + EXPECT_TRUE(g_syncEngine->GetQueueCacheSize() == 0); + } + EXPECT_TRUE(g_syncEngine->GetDiscardMsgNum() == 0); +} + +/** + * @tc.name: Anti Dos attack Sync 002 + * @tc.desc: Check if the enqueued and dequeue are normal when the whole length of messages is lower than + * maximum size of queue. + * @tc.type: FUNC + * @tc.require: AR000D08KU + * @tc.author: yiguang + */ +HWTEST_F(DistributeddbAntiDosSyncTest, AntiDosAttackSync002, TestSize.Level3) +{ + /** + * @tc.steps: step1. set block in function DispatchMessage as true. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_SHORT_TIME)); + g_communicatorAggregator->SetBlockValue(true); + + /** + * @tc.steps: step2. control MessageReceiveCallback to send suitable messages. + */ + const std::string srcTarget = "001"; + + for (unsigned int index = 0; index < g_syncEngine->GetMaxExecNum() + TEST_TWO; index++) { + std::vector outData; + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + ASSERT_TRUE(packet != nullptr); + Message *message = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + ASSERT_TRUE(message != nullptr); + + GenericSingleVerKvEntry *kvEntry = new (std::nothrow) GenericSingleVerKvEntry(); + ASSERT_TRUE(kvEntry != nullptr); + outData.push_back(kvEntry); + packet->SetData(outData); + packet->SetSendCode(E_OK); + packet->SetVersion(SOFTWARE_VERSION_CURRENT); + + uint32_t sessionId = index; + uint32_t sequenceId = index; + message->SetMessageType(TYPE_REQUEST); + message->SetTarget(srcTarget); + int errCode = message->SetExternalObject(packet); + ASSERT_TRUE(errCode == E_OK); + message->SetSessionId(sessionId); + message->SetSequenceId(sequenceId); + g_communicator->CallbackOnMessage(srcTarget, message); + } + + /** + * @tc.expected: step2. all messages enter the queue. + */ + EXPECT_TRUE(g_syncEngine->GetDiscardMsgNum() == 0); + EXPECT_TRUE(g_syncEngine->GetQueueCacheSize() / NUM == TEST_TWO); + + /** + * @tc.steps: step3. set block in function DispatchMessage as false after a period of time. + */ + g_communicator->Disable(); + g_communicatorAggregator->SetBlockValue(false); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_LONG_TIME)); + + /** + * @tc.expected: step3. the queue is eventually empty and no message is discarded. + */ + EXPECT_TRUE(g_syncEngine->GetDiscardMsgNum() == 0); + EXPECT_TRUE(g_syncEngine->GetQueueCacheSize() == 0); +} + +/** + * @tc.name: Anti Dos attack Sync 003 + * @tc.desc: Whether message enter and drop when all threads hang. + * @tc.type: FUNC + * @tc.require: AR000D08KU + * @tc.author: yiguang + */ +HWTEST_F(DistributeddbAntiDosSyncTest, AntiDosAttackSync003, TestSize.Level3) +{ + /** + * @tc.steps: step1. set block in function DispatchMessage as true. + */ + g_communicatorAggregator->SetBlockValue(true); + + /** + * @tc.steps: step2. control MessageReceiveCallback to send messages that are more than maximum size of queue. + */ + const std::string srcTarget = "001"; + + for (unsigned int index = 0; index < g_syncEngine->GetMaxExecNum() + TEST_THREE_THREAD; index++) { + std::vector outData; + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + ASSERT_TRUE(packet != nullptr); + Message *message = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + ASSERT_TRUE(message != nullptr); + for (int outIndex = 0; outIndex < TEST_THREE_OUTDATA; outIndex++) { + GenericSingleVerKvEntry *kvEntry = new (std::nothrow) GenericSingleVerKvEntry(); + ASSERT_TRUE(kvEntry != nullptr); + outData.push_back(kvEntry); + } + packet->SetData(outData); + packet->SetSendCode(E_OK); + packet->SetVersion(SOFTWARE_VERSION_CURRENT); + + uint32_t sessionId = index; + uint32_t sequenceId = index; + message->SetMessageType(TYPE_REQUEST); + message->SetTarget(srcTarget); + int errCode = message->SetExternalObject(packet); + ASSERT_TRUE(errCode == E_OK); + message->SetSessionId(sessionId); + message->SetSequenceId(sequenceId); + g_communicator->CallbackOnMessage(srcTarget, message); + } + + /** + * @tc.expected: step2. after part of messages are enqueued, the rest of the messages are discarded. + */ + EXPECT_TRUE(g_syncEngine->GetDiscardMsgNum() > 0); + EXPECT_TRUE(g_syncEngine->GetQueueCacheSize() > 0); + g_communicatorAggregator->SetBlockValue(false); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_communicator_proxy_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_communicator_proxy_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2706f6154fb7f7371a16da051c8cceea5d41a7b8 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_communicator_proxy_test.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "communicator_proxy.h" +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "kv_store_nb_delegate.h" +#include "mock_communicator.h" +#include "platform_specific.h" +#include "virtual_communicator_aggregator.h" + +using namespace testing::ext; +using namespace testing; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const string STORE_ID = "kv_store_sync_test"; + const std::string DEVICE_B = "deviceB"; + const std::string DEVICE_C = "deviceC"; + const std::string DEVICE_D = "deviceD"; + const std::string DEVICE_E = "deviceE"; + + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + DistributedDBToolsUnitTest g_tool; + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate* g_kvDelegatePtr = nullptr; + + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); +} + +class DistributedDBCommunicatorProxyTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + +protected: + MockCommunicator extComm_; + MockCommunicator mainComm_; + CommunicatorProxy *commProxy_ = nullptr; +}; + +void DistributedDBCommunicatorProxyTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Init datadir and Virtual Communicator. + */ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + string dir = g_testDir + "/single_ver"; + DIR* dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + + auto communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(communicatorAggregator); +} + +void DistributedDBCommunicatorProxyTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Release virtual Communicator and clear data dir. + */ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); +} + +void DistributedDBCommunicatorProxyTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: Get a KvStoreNbDelegate and init the CommunicatorProxy + */ + KvStoreNbDelegate::Option option; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + std::string identifier = g_mgr.GetKvStoreIdentifier(USER_ID, APP_ID, STORE_ID); + ASSERT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + commProxy_ = new (std::nothrow) CommunicatorProxy(); + ASSERT_TRUE(commProxy_ != nullptr); + commProxy_->SetMainCommunicator(&mainComm_); + commProxy_->SetEqualCommunicator(&extComm_, identifier, { DEVICE_C }); +} + +void DistributedDBCommunicatorProxyTest::TearDown(void) +{ + /** + * @tc.teardown: Release the KvStoreNbDelegate and CommunicatorProxy + */ + if (g_kvDelegatePtr != nullptr) { + ASSERT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + DBStatus status = g_mgr.DeleteKvStore(STORE_ID); + LOGD("delete kv store status %d", status); + ASSERT_TRUE(status == OK); + } + if (commProxy_ != nullptr) { + RefObject::DecObjRef(commProxy_); + } + commProxy_ = nullptr; +} + +/** + * @tc.name: Interface set equal 001 + * @tc.desc: Test set equal identifier from interface. + * @tc.type: FUNC + * @tc.require: AR000F4GVG + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBCommunicatorProxyTest, InterfaceSetEqualId001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Call GetKvStoreIdentifier to make a store identifier. + */ + std::string identifier = g_mgr.GetKvStoreIdentifier("default", APP_ID, STORE_ID); + + /** + * @tc.steps: step2. Call SetEqualIdentifier to set the store identifier B, D, E. + * @tc.expected: step2. SetEqualIdentifier return OK. + */ + DBStatus status = g_kvDelegatePtr->SetEqualIdentifier(identifier, { DEVICE_B, DEVICE_D, DEVICE_E }); + EXPECT_EQ(status, DBStatus::OK); + + /** + * @tc.steps: step2. Call SetEqualIdentifier to set the store identifier B. + * @tc.expected: step2. SetEqualIdentifier return OK and D, E will offline. + */ + status = g_kvDelegatePtr->SetEqualIdentifier(identifier, { DEVICE_B }); + EXPECT_EQ(status, DBStatus::OK); +} + +/** + * @tc.name: Register callback 001 + * @tc.desc: Test register callback from CommunicatorProxy. + * @tc.type: FUNC + * @tc.require: AR000F4GVG + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBCommunicatorProxyTest, RegCallBack001, TestSize.Level1) +{ + OnMessageCallback msgCallback; + OnConnectCallback connCallback; + std::function sendableCallback; + Finalizer finalizer; + + /** + * @tc.steps: step1. Call RegOnMessageCallback from CommProxy. + * @tc.expected: step1. mainComm and extComm's RegOnMessageCallback should be called once. + */ + EXPECT_CALL(extComm_, RegOnMessageCallback(_, _)).Times(1); + EXPECT_CALL(mainComm_, RegOnMessageCallback(_, _)).Times(1); + commProxy_->RegOnMessageCallback(msgCallback, finalizer); + + /** + * @tc.steps: step2. Call RegOnConnectCallback from CommProxy. + * @tc.expected: step2. mainComm and extComm's RegOnConnectCallback should be called once. + */ + EXPECT_CALL(extComm_, RegOnConnectCallback(_, _)).Times(1); + EXPECT_CALL(mainComm_, RegOnConnectCallback(_, _)).Times(1); + commProxy_->RegOnConnectCallback(connCallback, finalizer); + + /** + * @tc.steps: step3. Call RegOnSendableCallback from CommProxy. + * @tc.expected: step3. mainComm and extComm's RegOnSendableCallback should be called once. + */ + EXPECT_CALL(extComm_, RegOnSendableCallback(_, _)).Times(1); + EXPECT_CALL(mainComm_, RegOnSendableCallback(_, _)).Times(1); + commProxy_->RegOnSendableCallback(sendableCallback, finalizer); +} + +/** + * @tc.name: Activate 001 + * @tc.desc: Test Activate called from CommunicatorProxy. + * @tc.type: FUNC + * @tc.require: AR000F4GVG + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBCommunicatorProxyTest, Activate001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Call Activate from CommProxy. + * @tc.expected: step1. mainComm and extComm's Activate should be called once. + */ + EXPECT_CALL(extComm_, Activate()).Times(1); + EXPECT_CALL(mainComm_, Activate()).Times(1); + commProxy_->Activate(); +} + +/** + * @tc.name: Get mtu 001 + * @tc.desc: Test mtu called from CommunicatorProxy. + * @tc.type: FUNC + * @tc.require: AR000F4GVG + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBCommunicatorProxyTest, GetMtu001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Call GetCommunicatorMtuSize from CommProxy with no param. + * @tc.expected: step1. GetCommunicatorMtuSize return DBConstant::MIN_MTU_SIZE. + */ + EXPECT_CALL(mainComm_, GetCommunicatorMtuSize()).WillOnce(Return(DBConstant::MIN_MTU_SIZE)); + EXPECT_EQ(commProxy_->GetCommunicatorMtuSize(), DBConstant::MIN_MTU_SIZE); + + /** + * @tc.steps: step2. Call GetCommunicatorMtuSize from CommProxy with param DEVICE_C. + * @tc.expected: step2. GetCommunicatorMtuSize return DBConstant::MAX_MTU_SIZE. + */ + EXPECT_CALL(extComm_, GetCommunicatorMtuSize(DEVICE_C)).WillOnce(Return(DBConstant::MAX_MTU_SIZE)); + EXPECT_EQ(commProxy_->GetCommunicatorMtuSize(DEVICE_C), DBConstant::MAX_MTU_SIZE); +} + +/** + * @tc.name: Get local identify 001 + * @tc.desc: Test Get local identify from CommunicatorProxy. + * @tc.type: FUNC + * @tc.require: AR000F4GVG + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBCommunicatorProxyTest, GetLocalIdentity001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Call GetLocalIdentity from CommProxy, and set mainComm return DEVICE_B. + * @tc.expected: step1. GetCommunicatorMtuSize return DEVICE_B and function call return E_OK. + */ + EXPECT_CALL(mainComm_, GetLocalIdentity(_)).WillOnce(DoAll(SetArgReferee<0>(DEVICE_B), Return(E_OK))); + std::string localId; + EXPECT_EQ(commProxy_->GetLocalIdentity(localId), E_OK); + EXPECT_EQ(localId, DEVICE_B); +} + +/** + * @tc.name: Get remote version 001 + * @tc.desc: Test Get remote version from CommunicatorProxy. + * @tc.type: FUNC + * @tc.require: AR000F4GVG + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBCommunicatorProxyTest, GetRemoteVersion001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Set mainComm called GetRemoteCommunicatorVersion will return SOFTWARE_VERSION_BASE. + */ + EXPECT_CALL(mainComm_, GetRemoteCommunicatorVersion(DEVICE_B, _)) + .WillOnce(DoAll(SetArgReferee<1>(SOFTWARE_VERSION_BASE), Return(E_OK))); + + /** + * @tc.steps: step2. Call GetRemoteCommunicatorVersion from CommProxy with param DEVICE_B. + * @tc.expected: step2. GetRemoteCommunicatorVersion return SOFTWARE_VERSION_BASE and function call return E_OK. + */ + uint16_t version = 0; + EXPECT_EQ(commProxy_->GetRemoteCommunicatorVersion(DEVICE_B, version), E_OK); + EXPECT_EQ(version, SOFTWARE_VERSION_BASE); + + /** + * @tc.steps: step3. Set extComm called GetRemoteCommunicatorVersion will return SOFTWARE_VERSION_CURRENT. + */ + EXPECT_CALL(extComm_, GetRemoteCommunicatorVersion(DEVICE_C, _)) + .WillOnce(DoAll(SetArgReferee<1>(SOFTWARE_VERSION_CURRENT), Return(E_OK))); + + /** + * @tc.steps: step4. Call GetRemoteCommunicatorVersion from CommProxy with param DEVICE_C. + * @tc.expected: step4. GetRemoteCommunicatorVersion return SOFTWARE_VERSION_CURRENT and function call return E_OK. + */ + EXPECT_EQ(commProxy_->GetRemoteCommunicatorVersion(DEVICE_C, version), E_OK); + EXPECT_EQ(version, SOFTWARE_VERSION_CURRENT); +} + +/** + * @tc.name: Send message 001 + * @tc.desc: Test Send message from CommunicatorProxy. + * @tc.type: FUNC + * @tc.require: AR000F4GVG + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBCommunicatorProxyTest, SendMessage001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Call SendMessage from CommProxy with param DEVICE_B. + * @tc.expected: step1. MainComm's SendMessage willed called and return E_OK. + */ + SendConfig conf = {true, false, 0}; + EXPECT_CALL(mainComm_, SendMessage(DEVICE_B, _, _, _)).WillOnce(Return(E_OK)); + EXPECT_EQ(commProxy_->SendMessage(DEVICE_B, nullptr, conf, nullptr), E_OK); + + /** + * @tc.steps: step1. Call SendMessage from CommProxy with param DEVICE_C. + * @tc.expected: step1. ExtComm's SendMessage willed called and return E_OK. + */ + EXPECT_CALL(extComm_, SendMessage(DEVICE_C, _, _, _)).WillOnce(Return(E_OK)); + EXPECT_EQ(commProxy_->SendMessage(DEVICE_C, nullptr, conf, nullptr), E_OK); +} + +/** + * @tc.name: Get timeout time 001 + * @tc.desc: Test get timeout called from CommunicatorProxy. + * @tc.type: FUNC + * @tc.require: AR000F4GVG + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBCommunicatorProxyTest, GetTimeout001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Call GetTimeout from CommProxy with no param. + * @tc.expected: step1. GetTimeout return DBConstant::MIN_TIMEOUT. + */ + EXPECT_CALL(mainComm_, GetTimeout()).WillOnce(Return(DBConstant::MIN_TIMEOUT)); + EXPECT_EQ(commProxy_->GetTimeout(), DBConstant::MIN_TIMEOUT); + + /** + * @tc.steps: step2. Call GetTimeout from CommProxy with param DEVICE_C. + * @tc.expected: step2. GetTimeout return DBConstant::MAX_MTU_SIZE. + */ + EXPECT_CALL(extComm_, GetTimeout(DEVICE_C)).WillOnce(Return(DBConstant::MAX_TIMEOUT)); + EXPECT_EQ(commProxy_->GetTimeout(DEVICE_C), DBConstant::MAX_TIMEOUT); +} diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_mock_sync_module_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_mock_sync_module_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f971581a29607251e9121d9ece4dfa8561121da --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_mock_sync_module_test.cpp @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include "distributeddb_tools_unit_test.h" +#include "message.h" +#include "mock_auto_launch.h" +#include "mock_communicator.h" +#include "mock_meta_data.h" +#include "mock_single_ver_data_sync.h" +#include "mock_single_ver_state_machine.h" +#include "mock_sync_task_context.h" +#include "single_ver_kv_syncer.h" +#include "single_ver_relational_sync_task_context.h" +#include "virtual_communicator_aggregator.h" +#include "virtual_single_ver_sync_db_Interface.h" +#ifdef DATA_SYNC_CHECK_003 +#include "virtual_relational_ver_sync_db_interface.h" +#endif + +using namespace testing::ext; +using namespace testing; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { +void Init(MockSingleVerStateMachine &stateMachine, MockSyncTaskContext &syncTaskContext, + MockCommunicator &communicator, VirtualSingleVerSyncDBInterface &dbSyncInterface) +{ + std::shared_ptr metadata = std::make_shared(); + (void)syncTaskContext.Initialize("device", &dbSyncInterface, metadata, &communicator); + (void)stateMachine.Initialize(&syncTaskContext, &dbSyncInterface, metadata, &communicator); +} +} + +class DistributedDBMockSyncModuleTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBMockSyncModuleTest::SetUpTestCase(void) +{ +} + +void DistributedDBMockSyncModuleTest::TearDownTestCase(void) +{ +} + +void DistributedDBMockSyncModuleTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); +} + +void DistributedDBMockSyncModuleTest::TearDown(void) +{ +} + +/** + * @tc.name: StateMachineCheck001 + * @tc.desc: Test machine do timeout when has same timerId. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, StateMachineCheck001, TestSize.Level1) +{ + MockSingleVerStateMachine stateMachine; + MockSyncTaskContext syncTaskContext; + MockCommunicator communicator; + VirtualSingleVerSyncDBInterface dbSyncInterface; + Init(stateMachine, syncTaskContext, communicator, dbSyncInterface); + + TimerId expectId = 0; + TimerId actualId = expectId; + EXPECT_CALL(syncTaskContext, GetTimerId()).WillOnce(Return(expectId)); + EXPECT_CALL(stateMachine, SwitchStateAndStep(_)).WillOnce(Return()); + + stateMachine.CallStepToTimeout(actualId); +} + +/** + * @tc.name: StateMachineCheck002 + * @tc.desc: Test machine do timeout when has diff timerId. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, StateMachineCheck002, TestSize.Level1) +{ + MockSingleVerStateMachine stateMachine; + MockSyncTaskContext syncTaskContext; + MockCommunicator communicator; + VirtualSingleVerSyncDBInterface dbSyncInterface; + Init(stateMachine, syncTaskContext, communicator, dbSyncInterface); + + TimerId expectId = 0; + TimerId actualId = 1; + EXPECT_CALL(syncTaskContext, GetTimerId()).WillOnce(Return(expectId)); + EXPECT_CALL(stateMachine, SwitchStateAndStep(_)).Times(0); + + stateMachine.CallStepToTimeout(actualId); +} + +/** + * @tc.name: StateMachineCheck003 + * @tc.desc: Test machine exec next task when queue not empty. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, StateMachineCheck003, TestSize.Level1) +{ + MockSingleVerStateMachine stateMachine; + MockSyncTaskContext syncTaskContext; + MockCommunicator communicator; + VirtualSingleVerSyncDBInterface dbSyncInterface; + Init(stateMachine, syncTaskContext, communicator, dbSyncInterface); + + EXPECT_CALL(stateMachine, PrepareNextSyncTask()).WillOnce(Return(E_OK)); + + EXPECT_CALL(syncTaskContext, IsTargetQueueEmpty()).WillRepeatedly(Return(false)); + EXPECT_CALL(syncTaskContext, MoveToNextTarget()).WillRepeatedly(Return()); + EXPECT_CALL(syncTaskContext, IsCurrentSyncTaskCanBeSkipped()) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + // we expect machine don't change context status when queue not empty + EXPECT_CALL(syncTaskContext, SetOperationStatus(_)).WillOnce(Return()); + EXPECT_CALL(syncTaskContext, SetTaskExecStatus(_)).Times(0); + + EXPECT_EQ(stateMachine.CallExecNextTask(), E_OK); +} + +/** + * @tc.name: StateMachineCheck004 + * @tc.desc: Test machine deal time sync ack failed. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, StateMachineCheck004, TestSize.Level1) +{ + MockSingleVerStateMachine stateMachine; + MockSyncTaskContext syncTaskContext; + MockCommunicator communicator; + VirtualSingleVerSyncDBInterface dbSyncInterface; + Init(stateMachine, syncTaskContext, communicator, dbSyncInterface); + + DistributedDB::Message *message = new(std::nothrow) DistributedDB::Message(); + ASSERT_NE(message, nullptr); + message->SetMessageType(TYPE_RESPONSE); + message->SetSessionId(1u); + EXPECT_CALL(syncTaskContext, GetRequestSessionId()).WillRepeatedly(Return(1u)); + EXPECT_EQ(stateMachine.CallTimeMarkSyncRecv(message), -E_INVALID_ARGS); + EXPECT_EQ(syncTaskContext.GetTaskErrCode(), -E_INVALID_ARGS); + delete message; +} + +/** + * @tc.name: StateMachineCheck005 + * @tc.desc: Test machine recv errCode. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, StateMachineCheck005, TestSize.Level1) +{ + MockSingleVerStateMachine stateMachine; + MockSyncTaskContext syncTaskContext; + MockCommunicator communicator; + VirtualSingleVerSyncDBInterface dbSyncInterface; + Init(stateMachine, syncTaskContext, communicator, dbSyncInterface); + EXPECT_CALL(stateMachine, SwitchStateAndStep(_)).WillRepeatedly(Return()); + EXPECT_CALL(syncTaskContext, GetRequestSessionId()).WillRepeatedly(Return(0u)); + + std::initializer_list testCode = {-E_DISTRIBUTED_SCHEMA_CHANGED, -E_DISTRIBUTED_SCHEMA_NOT_FOUND}; + for (int errCode : testCode) { + stateMachine.DataRecvErrCodeHandle(0, errCode); + EXPECT_EQ(syncTaskContext.GetTaskErrCode(), errCode); + stateMachine.CallDataAckRecvErrCodeHandle(errCode, true); + EXPECT_EQ(syncTaskContext.GetTaskErrCode(), errCode); + } +} + +/** + * @tc.name: StateMachineCheck006 + * @tc.desc: Test machine exec next task when queue not empty to empty. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, StateMachineCheck006, TestSize.Level1) +{ + MockSingleVerStateMachine stateMachine; + MockSyncTaskContext syncTaskContext; + MockCommunicator communicator; + VirtualSingleVerSyncDBInterface dbSyncInterface; + Init(stateMachine, syncTaskContext, communicator, dbSyncInterface); + + syncTaskContext.CallSetSyncMode(QUERY_PUSH); + EXPECT_CALL(syncTaskContext, IsTargetQueueEmpty()) + .WillOnce(Return(false)) + .WillOnce(Return(true)); + EXPECT_CALL(syncTaskContext, IsCurrentSyncTaskCanBeSkipped()) + .WillRepeatedly(Return(syncTaskContext.CallIsCurrentSyncTaskCanBeSkipped())); + EXPECT_CALL(syncTaskContext, MoveToNextTarget()).WillOnce(Return()); + // we expect machine don't change context status when queue not empty + EXPECT_CALL(syncTaskContext, SetOperationStatus(_)).WillOnce(Return()); + EXPECT_CALL(syncTaskContext, SetTaskExecStatus(_)).WillOnce(Return()); + EXPECT_CALL(syncTaskContext, Clear()).WillOnce(Return()); + + EXPECT_EQ(stateMachine.CallExecNextTask(), -E_NO_SYNC_TASK); +} + +/** + * @tc.name: StateMachineCheck007 + * @tc.desc: Test machine DoSaveDataNotify in another thread. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, StateMachineCheck007, TestSize.Level3) +{ + MockSingleVerStateMachine stateMachine; + uint8_t callCount = 0; + EXPECT_CALL(stateMachine, DoSaveDataNotify(_, _, _)) + .WillRepeatedly([&callCount](uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) { + (void) sessionId; + (void) sequenceId; + (void) inMsgId; + callCount++; + std::this_thread::sleep_for(std::chrono::seconds(4)); // sleep 4s + }); + stateMachine.CallStartSaveDataNotify(0, 0, 0); + std::this_thread::sleep_for(std::chrono::seconds(5)); // sleep 5s + stateMachine.CallStopSaveDataNotify(); + // timer is called once in 2s, we sleep 5s timer call twice + EXPECT_EQ(callCount, 2); + std::this_thread::sleep_for(std::chrono::seconds(10)); // sleep 10s to wait all thread exit +} + +/** + * @tc.name: DataSyncCheck001 + * @tc.desc: Test dataSync recv error ack. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, DataSyncCheck001, TestSize.Level1) +{ + SingleVerDataSync dataSync; + DistributedDB::Message *message = new(std::nothrow) DistributedDB::Message(); + ASSERT_TRUE(message != nullptr); + message->SetErrorNo(E_FEEDBACK_COMMUNICATOR_NOT_FOUND); + EXPECT_EQ(dataSync.AckPacketIdCheck(message), true); + delete message; +} + +/** + * @tc.name: DataSyncCheck002 + * @tc.desc: Test dataSync recv notify ack. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, DataSyncCheck002, TestSize.Level1) +{ + SingleVerDataSync dataSync; + DistributedDB::Message *message = new(std::nothrow) DistributedDB::Message(); + ASSERT_TRUE(message != nullptr); + message->SetMessageType(TYPE_NOTIFY); + EXPECT_EQ(dataSync.AckPacketIdCheck(message), true); + delete message; +} +#ifdef DATA_SYNC_CHECK_003 +/** + * @tc.name: DataSyncCheck003 + * @tc.desc: Test dataSync recv notify ack. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, DataSyncCheck003, TestSize.Level1) +{ + MockSingleVerDataSync mockDataSync; + MockSyncTaskContext mockSyncTaskContext; + auto mockMetadata = std::make_shared(); + SyncTimeRange dataTimeRange = {1, 0, 1, 0}; + mockDataSync.CallUpdateSendInfo(dataTimeRange, &mockSyncTaskContext); + + VirtualRelationalVerSyncDBInterface storage; + MockCommunicator communicator; + std::shared_ptr metadata = std::static_pointer_cast(mockMetadata); + mockDataSync.Initialize(&storage, &communicator, metadata, "deviceId"); + + DistributedDB::Message *message = new(std::nothrow) DistributedDB::Message(); + ASSERT_TRUE(message != nullptr); + DataAckPacket packet; + message->SetSequenceId(1); + message->SetCopiedObject(packet); + mockSyncTaskContext.SetQuerySync(true); + + EXPECT_CALL(*mockMetadata, GetLastQueryTime(_, _, _)).WillOnce(Return(E_OK)); + EXPECT_CALL(*mockMetadata, SetLastQueryTime(_, _, _)).WillOnce([&dataTimeRange](const std::string &queryIdentify, + const std::string &deviceId, const Timestamp ×tamp) { + EXPECT_EQ(timestamp, dataTimeRange.endTime); + return E_OK; + }); + EXPECT_CALL(mockSyncTaskContext, SetOperationStatus(_)).WillOnce(Return()); + EXPECT_EQ(mockDataSync.TryContinueSync(&mockSyncTaskContext, message), -E_FINISHED); + delete message; +} +#endif +/** + * @tc.name: AutoLaunchCheck001 + * @tc.desc: Test autoLaunch close connection. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, AutoLaunchCheck001, TestSize.Level1) +{ + MockAutoLaunch mockAutoLaunch; + /** + * @tc.steps: step1. put AutoLaunchItem in cache to simulate a connection was auto launched + */ + std::string id = "TestAutoLaunch"; + std::string userId = "userId"; + AutoLaunchItem item; + mockAutoLaunch.SetAutoLaunchItem(id, userId, item); + EXPECT_CALL(mockAutoLaunch, TryCloseConnection(_)).WillOnce(Return()); + /** + * @tc.steps: step2. send close signal to simulate a connection was unused in 1 min + * @tc.expected: 10 thread try to close the connection and one thread close success + */ + const int loopCount = 10; + int finishCount = 0; + std::mutex mutex; + std::unique_lock lock(mutex); + std::condition_variable cv; + for (int i = 0; i < loopCount; i++) { + std::thread t = std::thread([&finishCount, &mockAutoLaunch, &id, &userId, &mutex, &cv] { + mockAutoLaunch.CallExtConnectionLifeCycleCallbackTask(id, userId); + finishCount++; + if (finishCount == loopCount) { + std::unique_lock lockInner(mutex); + cv.notify_one(); + } + }); + t.detach(); + } + cv.wait(lock, [&finishCount, &loopCount]() { + return finishCount == loopCount; + }); +} + +/** + * @tc.name: SyncDataSync001 + * @tc.desc: Test request start when RemoveDeviceDataIfNeed failed. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, SyncDataSync001, TestSize.Level1) +{ + MockSyncTaskContext syncTaskContext; + MockSingleVerDataSync dataSync; + + EXPECT_CALL(dataSync, RemoveDeviceDataIfNeed(_)).WillRepeatedly(Return(-E_BUSY)); + EXPECT_EQ(dataSync.CallRequestStart(&syncTaskContext, PUSH), -E_BUSY); + EXPECT_EQ(syncTaskContext.GetTaskErrCode(), -E_BUSY); +} + +/** + * @tc.name: SyncDataSync002 + * @tc.desc: Test pull request start when RemoveDeviceDataIfNeed failed. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, SyncDataSync002, TestSize.Level1) +{ + MockSyncTaskContext syncTaskContext; + MockSingleVerDataSync dataSync; + + EXPECT_CALL(dataSync, RemoveDeviceDataIfNeed(_)).WillRepeatedly(Return(-E_BUSY)); + EXPECT_EQ(dataSync.CallPullRequestStart(&syncTaskContext), -E_BUSY); + EXPECT_EQ(syncTaskContext.GetTaskErrCode(), -E_BUSY); +} + +/** + * @tc.name: SyncDataSync003 + * @tc.desc: Test call RemoveDeviceDataIfNeed in diff thread. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, SyncDataSync003, TestSize.Level1) +{ + MockSyncTaskContext syncTaskContext; + MockSingleVerDataSync dataSync; + + VirtualSingleVerSyncDBInterface storage; + MockCommunicator communicator; + std::shared_ptr mockMetadata = std::make_shared(); + std::shared_ptr metadata = std::static_pointer_cast(mockMetadata); + metadata->Initialize(&storage); + const std::string deviceId = "deviceId"; + dataSync.Initialize(&storage, &communicator, metadata, deviceId); + syncTaskContext.SetRemoteSoftwareVersion(SOFTWARE_VERSION_CURRENT); + syncTaskContext.Initialize(deviceId, &storage, metadata, &communicator); + syncTaskContext.EnableClearRemoteStaleData(true); + + /** + * @tc.steps: step1. set diff db createtime for rebuild label in meta + */ + metadata->SetDbCreateTime(deviceId, 1, true); // 1 is old db createTime + metadata->SetDbCreateTime(deviceId, 2, true); // 1 is new db createTime + + DistributedDB::Key k1 = {'k', '1'}; + DistributedDB::Value v1 = {'v', '1'}; + DistributedDB::Key k2 = {'k', '2'}; + DistributedDB::Value v2 = {'v', '2'}; + + /** + * @tc.steps: step2. call RemoveDeviceDataIfNeed in diff thread and then put data + */ + std::thread thread1([&]() { + (void)dataSync.CallRemoveDeviceDataIfNeed(&syncTaskContext); + storage.PutDeviceData(deviceId, k1, v1); + LOGD("PUT FINISH"); + }); + std::thread thread2([&]() { + (void)dataSync.CallRemoveDeviceDataIfNeed(&syncTaskContext); + storage.PutDeviceData(deviceId, k2, v2); + LOGD("PUT FINISH"); + }); + thread1.join(); + thread2.join(); + + DistributedDB::Value actualValue; + storage.GetDeviceData(deviceId, k1, actualValue); + EXPECT_EQ(v1, actualValue); + storage.GetDeviceData(deviceId, k2, actualValue); + EXPECT_EQ(v2, actualValue); +} + +/** + * @tc.name: AbilitySync001 + * @tc.desc: Test abilitySync abort when recv error. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, AbilitySync001, TestSize.Level1) +{ + MockSyncTaskContext syncTaskContext; + AbilitySync abilitySync; + + DistributedDB::Message *message = new(std::nothrow) DistributedDB::Message(); + ASSERT_TRUE(message != nullptr); + AbilitySyncAckPacket packet; + packet.SetAckCode(-E_BUSY); + message->SetCopiedObject(packet); + EXPECT_EQ(abilitySync.AckRecv(message, &syncTaskContext), -E_BUSY); + delete message; + EXPECT_EQ(syncTaskContext.GetTaskErrCode(), -E_BUSY); +} + +/** + * @tc.name: AbilitySync002 + * @tc.desc: Test abilitySync abort when save meta failed. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, AbilitySync002, TestSize.Level1) +{ + MockSyncTaskContext syncTaskContext; + AbilitySync abilitySync; + MockCommunicator comunicator; + VirtualSingleVerSyncDBInterface syncDBInterface; + std::shared_ptr metaData = std::make_shared(); + metaData->Initialize(&syncDBInterface); + abilitySync.Initialize(&comunicator, &syncDBInterface, metaData, "deviceId"); + + /** + * @tc.steps: step1. set AbilitySyncAckPacket ackCode is E_OK for pass the ack check + */ + DistributedDB::Message *message = new(std::nothrow) DistributedDB::Message(); + ASSERT_TRUE(message != nullptr); + AbilitySyncAckPacket packet; + packet.SetAckCode(E_OK); + packet.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + message->SetCopiedObject(packet); + /** + * @tc.steps: step2. set syncDBInterface busy for save data return -E_BUSY + */ + syncDBInterface.SetBusy(true); + SyncStrategy mockStrategy = {true, false, false}; + EXPECT_CALL(syncTaskContext, GetSyncStrategy(_)).WillOnce(Return(mockStrategy)); + EXPECT_EQ(abilitySync.AckRecv(message, &syncTaskContext), -E_BUSY); + delete message; + EXPECT_EQ(syncTaskContext.GetTaskErrCode(), -E_BUSY); +} + +/** + * @tc.name: AbilitySync002 + * @tc.desc: Test abilitySync when offline. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, AbilitySync003, TestSize.Level1) +{ + /** + * @tc.steps: step1. set table TEST is permitSync + */ + SingleVerRelationalSyncTaskContext *context = new (std::nothrow) SingleVerRelationalSyncTaskContext(); + ASSERT_NE(context, nullptr); + RelationalSyncStrategy strategy; + const std::string tableName = "TEST"; + strategy[tableName] = {true, true, true}; + context->SetRelationalSyncStrategy(strategy); + QuerySyncObject query; + query.SetTableName(tableName); + /** + * @tc.steps: step2. set table is need reset ability sync but it still permit sync + */ + context->SetIsNeedResetAbilitySync(true); + EXPECT_EQ(context->GetSyncStrategy(query).permitSync, true); + /** + * @tc.steps: step3. set table is schema change now it don't permit sync + */ + context->SchemaChange(); + EXPECT_EQ(context->GetSyncStrategy(query).permitSync, false); + RefObject::KillAndDecObjRef(context); +} + +/** + * @tc.name: SyncLifeTest001 + * @tc.desc: Test syncer alive when thread still exist. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, SyncLifeTest001, TestSize.Level3) +{ + std::shared_ptr syncer = std::make_shared(); + VirtualCommunicatorAggregator *virtualCommunicatorAggregator = new VirtualCommunicatorAggregator(); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(virtualCommunicatorAggregator); + VirtualSingleVerSyncDBInterface *syncDBInterface = new VirtualSingleVerSyncDBInterface(); + syncer->Initialize(syncDBInterface, true); + syncer->EnableAutoSync(true); + for (int i = 0; i < 1000; i++) { // trigger 1000 times auto sync check + syncer->LocalDataChanged(SQLITE_GENERAL_NS_PUT_EVENT); + } + syncer = nullptr; + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); + delete syncDBInterface; +} + +/** + * @tc.name: MessageScheduleTest001 + * @tc.desc: Test MessageSchedule stop timer when no message. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBMockSyncModuleTest, MessageScheduleTest001, TestSize.Level1) +{ + MockSyncTaskContext *context = new MockSyncTaskContext(); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_CURRENT); + bool last = false; + context->OnLastRef([&last]() { + last = true; + }); + SingleVerDataMessageSchedule schedule; + bool isNeedHandle = false; + bool isNeedContinue = false; + schedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + RefObject::KillAndDecObjRef(context); + std::this_thread::sleep_for(std::chrono::seconds(1)); + EXPECT_TRUE(last); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_multi_ver_p2p_sync_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_multi_ver_p2p_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..70d3ffbcd2af86d0aef10dfcec00985654e753de --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_multi_ver_p2p_sync_test.cpp @@ -0,0 +1,1649 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "commit_history_sync.h" +#include "db_common.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "ikvdb_connection.h" +#include "kv_store_delegate.h" +#include "kv_virtual_device.h" +#include "kvdb_manager.h" +#include "kvdb_pragma.h" +#include "log_print.h" +#include "meta_data.h" +#include "multi_ver_data_sync.h" +#include "platform_specific.h" +#include "sync_types.h" +#include "time_sync.h" +#include "virtual_multi_ver_sync_db_interface.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +#ifndef LOW_LEVEL_MEM_DEV +namespace { + string g_testDir; + const string STORE_ID = "kv_store_sync_test"; + const string STORE_ID_A = "kv_store_sync_test_a"; + const string STORE_ID_B = "kv_store_sync_test_b"; + const int WAIT_TIME_1 = 1000; + const int WAIT_TIME_2 = 2000; + const int WAIT_LONG_TIME = 10000; + const int WAIT_LIMIT_TIME = 30000; + const std::string DEVICE_B = "deviceB"; + const std::string DEVICE_C = "deviceC"; + const int LIMIT_KEY_SIZE = 1024; + constexpr int BIG_VALUE_SIZE = 1024 + 1; // > 1K + constexpr int LIMIT_VALUE_SIZE = 4 * 1024 * 1024; // 4M + KvStoreDelegateManager g_mgr("sync_test", "sync_test"); + KvStoreConfig g_config; + KvStoreDelegate::Option g_option; + + // define the g_kvDelegateCallback, used to get some information when open a kv store. + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreDelegate *g_kvDelegatePtr = nullptr; + MultiVerNaturalStoreConnection *g_connectionA; + MultiVerNaturalStoreConnection *g_connectionB; + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + KvVirtualDevice *g_deviceB = nullptr; + KvVirtualDevice *g_deviceC = nullptr; + + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + + MultiVerNaturalStoreConnection *GetConnection(const std::string &dir, const std::string &storeId, int errCode) + { + KvDBProperties prop; + prop.SetStringProp(KvDBProperties::USER_ID, "sync_test"); + prop.SetStringProp(KvDBProperties::APP_ID, "sync_test"); + prop.SetStringProp(KvDBProperties::STORE_ID, storeId); + std::string identifier = DBCommon::TransferHashString("sync_test-sync_test-" + storeId); + + prop.SetStringProp(KvDBProperties::IDENTIFIER_DATA, identifier); + std::string identifierDir = DBCommon::TransferStringToHex(identifier); + prop.SetStringProp(KvDBProperties::IDENTIFIER_DIR, identifierDir); + prop.SetStringProp(KvDBProperties::DATA_DIR, dir); + prop.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + prop.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + errCode = E_OK; + auto conn = KvDBManager::GetDatabaseConnection(prop, errCode); + if (errCode != E_OK) { + LOGE("[DistributeddbMultiVerP2PSyncTes] db create failed path, err %d", errCode); + return nullptr; + } + return static_cast(conn); + } + + int GetDataFromConnection(IKvDBConnection *conn, const Key &key, Value &value) + { + IKvDBSnapshot *snapshot = nullptr; + int errCode = conn->GetSnapshot(snapshot); + if (errCode != E_OK) { + return errCode; + } + errCode = snapshot->Get(key, value); + conn->ReleaseSnapshot(snapshot); + return errCode; + } +} + +class DistributedDBMultiVerP2PSyncTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBMultiVerP2PSyncTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Init datadir and Virtual Communicator. + */ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + string dir = g_testDir + "/commitstore"; + g_config.dataDir = dir; + DIR* dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + g_mgr.SetKvStoreConfig(g_config); + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); +} + +void DistributedDBMultiVerP2PSyncTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Release virtual Communicator and clear data dir. + */ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); + g_communicatorAggregator = nullptr; +} + +void DistributedDBMultiVerP2PSyncTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: create virtual device B and C + */ + g_communicatorAggregator->Disable(); + g_deviceB = new (std::nothrow) KvVirtualDevice(DEVICE_B); + ASSERT_TRUE(g_deviceB != nullptr); + VirtualMultiVerSyncDBInterface *syncInterfaceB = new (std::nothrow) VirtualMultiVerSyncDBInterface; + ASSERT_TRUE(syncInterfaceB != nullptr); + ASSERT_EQ(syncInterfaceB->Initialize(DEVICE_B), E_OK); + ASSERT_EQ(g_deviceB->Initialize(g_communicatorAggregator, syncInterfaceB), E_OK); + + g_deviceC = new (std::nothrow) KvVirtualDevice(DEVICE_C); + ASSERT_TRUE(g_deviceC != nullptr); + VirtualMultiVerSyncDBInterface *syncInterfaceC = new (std::nothrow) VirtualMultiVerSyncDBInterface; + ASSERT_TRUE(syncInterfaceC != nullptr); + ASSERT_EQ(syncInterfaceC->Initialize(DEVICE_C), E_OK); + ASSERT_EQ(g_deviceC->Initialize(g_communicatorAggregator, syncInterfaceC), E_OK); + g_communicatorAggregator->Enable(); + + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + return true;}; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); +} + +void DistributedDBMultiVerP2PSyncTest::TearDown(void) +{ + /** + * @tc.teardown: Release device A, B, C, connectionA and connectionB + */ + if (g_kvDelegatePtr != nullptr) { + ASSERT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + DBStatus status = g_mgr.DeleteKvStore(STORE_ID); + LOGD("delete kv store status %d", status); + ASSERT_TRUE(status == OK); + } + if (g_deviceB != nullptr) { + delete g_deviceB; + g_deviceB = nullptr; + } + if (g_deviceC != nullptr) { + delete g_deviceC; + g_deviceC = nullptr; + } + if (g_connectionA != nullptr) { + g_connectionA->Close(); + ASSERT_EQ(g_mgr.DeleteKvStore(STORE_ID_A), OK); + g_connectionA = nullptr; + } + if (g_connectionB != nullptr) { + g_connectionB->Close(); + ASSERT_EQ(g_mgr.DeleteKvStore(STORE_ID_B), OK); + g_connectionB = nullptr; + } + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +static DBStatus GetData(KvStoreDelegate *kvStore, const Key &key, Value &value) +{ + KvStoreSnapshotDelegate *snapshotTmp = nullptr; + DBStatus statusTmp; + kvStore->GetKvStoreSnapshot(nullptr, + [&statusTmp, &snapshotTmp](DBStatus status, KvStoreSnapshotDelegate *snapshot) { + statusTmp = status; + snapshotTmp = snapshot; + }); + if (statusTmp != E_OK) { + return statusTmp; + } + snapshotTmp->Get(key, [&statusTmp, &value](DBStatus status, const Value &outValue) { + statusTmp = status; + value = outValue; + }); + if (statusTmp == OK) { + LOGD("[DistributeddbMultiVerP2PSyncTes] GetData key %c, value = %c", key[0], value[0]); + } + kvStore->ReleaseKvStoreSnapshot(snapshotTmp); + return statusTmp; +} + +/** + * @tc.name: Transaction Sync 001 + * @tc.desc: Verify put transaction sync function. + * @tc.type: FUNC + * @tc.require: AR000BVRO4 AR000CQE0K + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, TransactionSync001, TestSize.Level2) +{ + /** + * @tc.steps: step1. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + + /** + * @tc.steps: step2. deviceB put {k1, v1}, {k2,v2} in a transaction + */ + g_deviceB->StartTransaction(); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_2, DistributedDBUnitTest::VALUE_2), E_OK); + g_deviceB->Commit(); + + /** + * @tc.steps: step3. deviceB online and wait for sync + */ + g_deviceB->Online(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + /** + * @tc.steps: step4. deviceC put {k3, v3}, {k4,v4} in a transaction + */ + g_deviceC->StartTransaction(); + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_3, DistributedDBUnitTest::VALUE_3), E_OK); + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_4, DistributedDBUnitTest::VALUE_4), E_OK); + g_deviceC->Commit(); + + /** + * @tc.steps: step5. deviceC online for sync + */ + g_deviceC->Online(); + + /** + * @tc.steps: step6. deviceC offline + */ + g_deviceC->Offline(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + /** + * @tc.expected: step6. deviceA have {k1, v1}, {k2, v2}, not have k3, k4 + */ + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_1, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_1); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_2, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_2); + + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_3, value), NOT_FOUND); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_4, value), NOT_FOUND); +} + +/** + * @tc.name: Transaction Sync 002 + * @tc.desc: Verify delete transaction sync function. + * @tc.type: FUNC + * @tc.require: AR000BVRO4 AR000CQE0K + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, TransactionSync002, TestSize.Level2) +{ + /** + * @tc.steps: step1. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + + /** + * @tc.steps: step2. deviceB put {k1, v1}, {k2,v2} in a transaction + */ + g_deviceB->StartTransaction(); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_2, DistributedDBUnitTest::VALUE_2), E_OK); + g_deviceB->Commit(); + + /** + * @tc.steps: step3. deviceB online and wait for sync + */ + g_deviceB->Online(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + /** + * @tc.steps: step4. deviceC put {k3, v3}, and delete k3 in a transaction + */ + g_deviceC->StartTransaction(); + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_3, DistributedDBUnitTest::VALUE_3), E_OK); + ASSERT_EQ(g_deviceC->DeleteData(DistributedDBUnitTest::KEY_3), E_OK); + g_deviceC->Commit(); + + /** + * @tc.steps: step5. deviceB online for sync + */ + g_deviceC->Online(); + + /** + * @tc.steps: step6. deviceC offline + */ + g_deviceC->Offline(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + /** + * @tc.expected: step6. deviceA have {k1, v1}, {k2, v2}, not have k3, k4 + */ + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_1, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_1); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_2, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_2); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_3, value), NOT_FOUND); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_3, value), NOT_FOUND); +} + +/** + * @tc.name: Transaction Sync 003 + * @tc.desc: Verify update transaction sync function. + * @tc.type: FUNC + * @tc.require: AR000BVRO4 AR000CQE0K + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, TransactionSync003, TestSize.Level2) +{ + /** + * @tc.steps: step1. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + + /** + * @tc.steps: step2. deviceB put {k1, v1}, {k2,v2} in a transaction + */ + g_deviceB->StartTransaction(); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_2, DistributedDBUnitTest::VALUE_2), E_OK); + g_deviceB->Commit(); + + /** + * @tc.steps: step3. deviceB online and wait for sync + */ + g_deviceB->Online(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + /** + * @tc.steps: step4. deviceC put {k3, v3}, and update {k3, v4} in a transaction + */ + g_deviceC->StartTransaction(); + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_3, DistributedDBUnitTest::VALUE_3), E_OK); + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_3, DistributedDBUnitTest::VALUE_4), E_OK); + g_deviceC->Commit(); + + /** + * @tc.steps: step5. deviceB online for sync + */ + g_deviceC->Online(); + + /** + * @tc.steps: step6. deviceC offline + */ + g_deviceC->Offline(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + /** + * @tc.expected: step6. deviceA have {k1, v1}, {k2, v2}, not have k3, k4 + */ + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_1, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_1); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_2, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_2); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_3, value), NOT_FOUND); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_3, value), NOT_FOUND); +} + +/** + * @tc.name: Metadata 001 + * @tc.desc: Verify metadata add and update function + * @tc.type: FUNC + * @tc.require: AR000CQE0P AR000CQE0S + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, Metadata001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create a metadata and use VirtualMultiVerSyncDBInterface to init + * @tc.expected: step1. metadata init ok + */ + Metadata metadata; + VirtualMultiVerSyncDBInterface *syncInterface = new (std::nothrow) VirtualMultiVerSyncDBInterface; + ASSERT_TRUE(syncInterface != nullptr); + EXPECT_EQ(syncInterface->Initialize("metadata_test"), E_OK); + EXPECT_EQ(metadata.Initialize(syncInterface), E_OK); + + /** + * @tc.steps: step2. call SaveTimeOffset to write t1. + * @tc.expected: step2. SaveTimeOffset return ok + */ + const TimeOffset timeOffsetA = 1024; + EXPECT_EQ(metadata.SaveTimeOffset(DEVICE_B, timeOffsetA), E_OK); + TimeOffset timeOffsetB = 0; + + /** + * @tc.steps: step3. call GetTimeOffset to read t2. + * @tc.expected: step3. t1 == t2 + */ + metadata.GetTimeOffset(DEVICE_B, timeOffsetB); + EXPECT_EQ(timeOffsetA, timeOffsetB); + + /** + * @tc.steps: step4. call SaveTimeOffset to write t3. t3 != t1 + * @tc.expected: step4. SaveTimeOffset return ok + */ + const TimeOffset timeOffsetC = 2048; + EXPECT_EQ(metadata.SaveTimeOffset(DEVICE_B, timeOffsetC), E_OK); + + /** + * @tc.steps: step5. call GetTimeOffset to read t2. + * @tc.expected: step5. t4 == t3 + */ + TimeOffset timeOffsetD = 0; + metadata.GetTimeOffset(DEVICE_B, timeOffsetD); + EXPECT_EQ(timeOffsetC, timeOffsetD); + syncInterface->DeleteDatabase(); + delete syncInterface; + syncInterface = nullptr; +} + +/** + * @tc.name: Isolation Sync 001 + * @tc.desc: Verify add sync isolation between different kvstore. + * @tc.type: FUNC + * @tc.require: AR000BVDGP + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, IsolationSync001, TestSize.Level2) +{ + int errCode = 0; + + /** + * @tc.steps: step1. Get connectionA, connectionB from different kvstore, + * connectionB not in g_communicatorAggregator + */ + g_communicatorAggregator->Disable(); + g_connectionB = GetConnection(g_config.dataDir, STORE_ID_B, errCode); + ASSERT_TRUE(g_connectionB != nullptr); + g_communicatorAggregator->Enable(); + g_connectionA = GetConnection(g_config.dataDir, STORE_ID_A, errCode); + ASSERT_TRUE(g_connectionA != nullptr); + + /** + * @tc.steps: step2. deviceB put {k1, v1} + */ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + + /** + * @tc.steps: step3. connectionA pull from deviceB + * @tc.expected: step3. Pragma OK, connectionA have {k1, v1} , connectionB don't have k1. + */ + PragmaSync pragmaData(devices, SYNC_MODE_PULL_ONLY, nullptr); + ASSERT_TRUE(g_connectionA->Pragma(PRAGMA_SYNC_DEVICES, &pragmaData) == E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + Value value; + ASSERT_EQ(GetDataFromConnection(g_connectionA, DistributedDBUnitTest::KEY_1, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_1); + EXPECT_EQ(GetDataFromConnection(g_connectionB, DistributedDBUnitTest::KEY_1, value), -E_NOT_FOUND); +} + +/** + * @tc.name: Isolation Sync 002 + * @tc.desc: Verify update sync isolation between different kvstore. + * @tc.type: FUNC + * @tc.require: AR000BVDGP + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, IsolationSync002, TestSize.Level2) +{ + int errCode = 0; + + /** + * @tc.steps: step1. Get connectionA, connectionB from different kvstore, + * connectionB not in g_communicatorAggregator + */ + g_communicatorAggregator->Disable(); + g_connectionB = GetConnection(g_config.dataDir, STORE_ID_B, errCode); + ASSERT_TRUE(g_connectionB != nullptr); + g_communicatorAggregator->Enable(); + g_connectionA = GetConnection(g_config.dataDir, STORE_ID_A, errCode); + ASSERT_TRUE(g_connectionA != nullptr); + + /** + * @tc.steps: step2. deviceB put {k1, v1} and update {k1, v2} + */ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_2), E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + /** + * @tc.steps: step3. connectionA pull from deviceB + * @tc.expected: step3. Pragma OK, connectionA have {k1, v2} , connectionB don't have k1. + */ + PragmaSync pragmaData(devices, SYNC_MODE_PULL_ONLY, nullptr); + ASSERT_TRUE(g_connectionA->Pragma(PRAGMA_SYNC_DEVICES, &pragmaData) == E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + Value value; + EXPECT_EQ(GetDataFromConnection(g_connectionA, DistributedDBUnitTest::KEY_1, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_2); + EXPECT_EQ(GetDataFromConnection(g_connectionB, DistributedDBUnitTest::KEY_1, value), -E_NOT_FOUND); +} + +/** + * @tc.name: Isolation Sync 003 + * @tc.desc: Verify delete sync isolation between different kvstore. + * @tc.type: FUNC + * @tc.require: AR000BVDGP + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, IsolationSync003, TestSize.Level2) +{ + int errCode = 0; + + /** + * @tc.steps: step1. Get connectionA, connectionB from different kvstore, + * connectionB not in g_communicatorAggregator, connectionB put {k1,v1} + */ + g_communicatorAggregator->Disable(); + g_connectionB = GetConnection(g_config.dataDir, STORE_ID_B, errCode); + ASSERT_TRUE(g_connectionB != nullptr); + IOption option; + ASSERT_EQ(g_connectionB->Put(option, KEY_1, VALUE_1), E_OK); + g_communicatorAggregator->Enable(); + g_connectionA = GetConnection(g_config.dataDir, STORE_ID_A, errCode); + ASSERT_TRUE(g_connectionA != nullptr); + + /** + * @tc.steps: step2. deviceB put {k1, v1} and delete k1 + */ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + ASSERT_EQ(g_deviceB->DeleteData(DistributedDBUnitTest::KEY_1), E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + /** + * @tc.steps: step3. connectionA pull from deviceB + * @tc.expected: step3. Pragma OK, connectionA don't have k1, connectionB have {k1.v1} + */ + LOGD("[DistributeddbMultiVerP2PSyncTes] start sync"); + PragmaSync pragmaData(devices, SYNC_MODE_PULL_ONLY, nullptr); + ASSERT_TRUE(g_connectionA->Pragma(PRAGMA_SYNC_DEVICES, &pragmaData) == E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + + Value value; + EXPECT_EQ(GetDataFromConnection(g_connectionA, DistributedDBUnitTest::KEY_1, value), -E_NOT_FOUND); + EXPECT_EQ(GetDataFromConnection(g_connectionB, DistributedDBUnitTest::KEY_1, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_1); +} + +static void SetTimeSyncPacketField(TimeSyncPacket &inPacket, Timestamp sourceBegin, Timestamp sourceEnd, + Timestamp targetBegin, Timestamp targetEnd, SyncId theId) +{ + inPacket.SetSourceTimeBegin(sourceBegin); + inPacket.SetSourceTimeEnd(sourceEnd); + inPacket.SetTargetTimeBegin(targetBegin); + inPacket.SetTargetTimeEnd(targetEnd); +} + +static bool IsTimeSyncPacketEqual(const TimeSyncPacket &inPacketA, const TimeSyncPacket &inPacketB) +{ + bool equal = true; + equal = inPacketA.GetSourceTimeBegin() == inPacketB.GetSourceTimeBegin() ? equal : false; + equal = inPacketA.GetSourceTimeEnd() == inPacketB.GetSourceTimeEnd() ? equal : false; + equal = inPacketA.GetTargetTimeBegin() == inPacketB.GetTargetTimeBegin() ? equal : false; + equal = inPacketA.GetTargetTimeEnd() == inPacketB.GetTargetTimeEnd() ? equal : false; + return equal; +} + +/** + * @tc.name: Timesync Packet 001 + * @tc.desc: Verify TimesyncPacket Serialization and DeSerialization + * @tc.type: FUNC + * @tc.require: AR000BVRNU AR000CQE0J + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, TimesyncPacket001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create TimeSyncPacket packetA aand packetB + */ + TimeSyncPacket packetA; + TimeSyncPacket packetB; + SetTimeSyncPacketField(packetA, 1, 2, 3, 4, 5); // 1, 2, 3, 4, 5 is five field for time sync packet + SetTimeSyncPacketField(packetB, 5, 4, 3, 2, 1); // 1, 2, 3, 4, 5 is five field for time sync packet + Message oriMsgA; + Message oriMsgB; + oriMsgA.SetCopiedObject(packetA); + oriMsgA.SetMessageId(TIME_SYNC_MESSAGE); + oriMsgA.SetMessageType(TYPE_REQUEST); + oriMsgB.SetCopiedObject(packetB); + oriMsgB.SetMessageId(TIME_SYNC_MESSAGE); + oriMsgB.SetMessageType(TYPE_RESPONSE); + + /** + * @tc.steps: step2. Serialization packetA to bufferA + */ + uint32_t lenA = TimeSync::CalculateLen(&oriMsgA); + vector bufferA; + bufferA.resize(lenA); + int ret = TimeSync::Serialization(bufferA.data(), lenA, &oriMsgA); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step3. Serialization packetB to bufferB + */ + uint32_t lenB = TimeSync::CalculateLen(&oriMsgB); + vector bufferB; + bufferB.resize(lenB); + ret = TimeSync::Serialization(bufferB.data(), lenB, &oriMsgB); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step4. DeSerialization bufferA to outPktA + * @tc.expected: step4. packetA == outPktA + */ + Message outMsgA; + outMsgA.SetMessageId(TIME_SYNC_MESSAGE); + outMsgA.SetMessageType(TYPE_REQUEST); + ret = TimeSync::DeSerialization(bufferA.data(), lenA, &outMsgA); + ASSERT_EQ(ret, E_OK); + const TimeSyncPacket *outPktA = outMsgA.GetObject(); + ASSERT_NE(outPktA, nullptr); + EXPECT_EQ(IsTimeSyncPacketEqual(packetA, *outPktA), true); + + /** + * @tc.steps: step5. DeSerialization bufferA to outPktA + * @tc.expected: step5. packetB == outPktB outPktB != outPktA + */ + Message outMsgB; + outMsgB.SetMessageId(TIME_SYNC_MESSAGE); + outMsgB.SetMessageType(TYPE_RESPONSE); + ret = TimeSync::DeSerialization(bufferB.data(), lenB, &outMsgB); + ASSERT_EQ(ret, E_OK); + const TimeSyncPacket *outPktB = outMsgB.GetObject(); + ASSERT_NE(outPktB, nullptr); + EXPECT_EQ(IsTimeSyncPacketEqual(packetB, *outPktB), true); + EXPECT_EQ(IsTimeSyncPacketEqual(*outPktA, *outPktB), false); +} + +static MultiVerCommitNode MakeMultiVerCommitA() +{ + MultiVerCommitNode outCommit; + outCommit.commitId = vector(1, 11); // 1 is length, 11 is value + outCommit.leftParent = vector(2, 22); // 2 is length, 22 is value + outCommit.rightParent = vector(3, 33); // 3 is length, 33 is value + outCommit.timestamp = 444; // 444 is value + outCommit.version = 5555; // 5555 is value + outCommit.isLocal = 66666; // 66666 is value + outCommit.deviceInfo = "AAAAAA"; + return outCommit; +} + +static MultiVerCommitNode MakeMultiVerCommitB() +{ + MultiVerCommitNode outCommit; + outCommit.commitId = vector(9, 99); // 9 is length, 99 is value + outCommit.leftParent = vector(8, 88); // 8 is length, 88 is value + outCommit.rightParent = vector(7, 77); // 7 is length, 77 is value + outCommit.timestamp = 666; // 666 is value + outCommit.version = 5555; // 5555 is value + outCommit.isLocal = 44444; // 44444 is value + outCommit.deviceInfo = "BBBBBB"; + return outCommit; +} + +static MultiVerCommitNode MakeMultiVerCommitC() +{ + MultiVerCommitNode outCommit; + outCommit.commitId = vector(1, 99); // 1 is length, 99 is value + outCommit.leftParent = vector(2, 88); // 2 is length, 88 is value + outCommit.rightParent = vector(3, 77); // 3 is length, 77 is value + outCommit.timestamp = 466; // 466 is value + outCommit.version = 5555; // 5555 is value + outCommit.isLocal = 66444; // 66444 is value + outCommit.deviceInfo = "CCCCCC"; + return outCommit; +} + +static bool IsMultiVerCommitEqual(const MultiVerCommitNode &inCommitA, const MultiVerCommitNode &inCommitB) +{ + bool equal = true; + equal = inCommitA.commitId == inCommitB.commitId ? equal : false; + equal = inCommitA.leftParent == inCommitB.leftParent ? equal : false; + equal = inCommitA.rightParent == inCommitB.rightParent ? equal : false; + equal = inCommitA.timestamp == inCommitB.timestamp ? equal : false; + equal = inCommitA.version == inCommitB.version ? equal : false; + equal = inCommitA.isLocal == inCommitB.isLocal ? equal : false; + equal = inCommitA.deviceInfo == inCommitB.deviceInfo ? equal : false; + return equal; +} + +static void MakeCommitHistorySyncRequestPacketA(CommitHistorySyncRequestPacket &inPacket) +{ + std::map commitMap; + commitMap[string("A")] = MakeMultiVerCommitA(); + commitMap[string("C")] = MakeMultiVerCommitC(); + inPacket.SetCommitMap(commitMap); +} + +static void MakeCommitHistorySyncRequestPacketB(CommitHistorySyncRequestPacket &inPacket) +{ + std::map commitMap; + commitMap[string("B")] = MakeMultiVerCommitB(); + commitMap[string("C")] = MakeMultiVerCommitC(); + commitMap[string("BB")] = MakeMultiVerCommitB(); + inPacket.SetCommitMap(commitMap); +} + +static bool IsCommitHistorySyncRequestPacketEqual(const CommitHistorySyncRequestPacket &inPacketA, + const CommitHistorySyncRequestPacket &inPacketB) +{ + std::map commitMapA; + std::map commitMapB; + inPacketA.GetCommitMap(commitMapA); + inPacketB.GetCommitMap(commitMapB); + for (auto &entry : commitMapA) { + if (commitMapB.count(entry.first) == 0) { + return false; + } + if (!IsMultiVerCommitEqual(entry.second, commitMapB[entry.first])) { + return false; + } + } + for (auto &entry : commitMapB) { + if (commitMapA.count(entry.first) == 0) { + return false; + } + if (!IsMultiVerCommitEqual(entry.second, commitMapA[entry.first])) { + return false; + } + } + return true; +} + +/** + * @tc.name: Commit History Sync Request Packet 001 + * @tc.desc: Verify CommitHistorySyncRequestPacket Serialization and DeSerialization + * @tc.type: FUNC + * @tc.require: AR000BVRNU AR000CQE0J + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, CommitHistorySyncRequestPacket001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create CommitHistorySyncRequestPacket packetA aand packetB + */ + CommitHistorySyncRequestPacket packetA; + CommitHistorySyncRequestPacket packetB; + MakeCommitHistorySyncRequestPacketA(packetA); + MakeCommitHistorySyncRequestPacketB(packetB); + Message oriMsgA; + Message oriMsgB; + oriMsgA.SetCopiedObject(packetA); + oriMsgA.SetMessageId(COMMIT_HISTORY_SYNC_MESSAGE); + oriMsgA.SetMessageType(TYPE_REQUEST); + oriMsgB.SetCopiedObject(packetB); + oriMsgB.SetMessageId(COMMIT_HISTORY_SYNC_MESSAGE); + oriMsgB.SetMessageType(TYPE_REQUEST); + + /** + * @tc.steps: step2. Serialization packetA to bufferA + */ + uint32_t lenA = CommitHistorySync::CalculateLen(&oriMsgA); + vector bufferA; + bufferA.resize(lenA); + int ret = CommitHistorySync::Serialization(bufferA.data(), lenA, &oriMsgA); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step3. Serialization packetB to bufferB + */ + uint32_t lenB = CommitHistorySync::CalculateLen(&oriMsgB); + vector bufferB; + bufferB.resize(lenB); + ret = CommitHistorySync::Serialization(bufferB.data(), lenB, &oriMsgB); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step4. DeSerialization bufferA to outPktA + * @tc.expected: step4. packetA == outPktA + */ + Message outMsgA; + outMsgA.SetMessageId(COMMIT_HISTORY_SYNC_MESSAGE); + outMsgA.SetMessageType(TYPE_REQUEST); + ret = CommitHistorySync::DeSerialization(bufferA.data(), lenA, &outMsgA); + ASSERT_EQ(ret, E_OK); + const CommitHistorySyncRequestPacket *outPktA = outMsgA.GetObject(); + ASSERT_NE(outPktA, nullptr); + EXPECT_EQ(IsCommitHistorySyncRequestPacketEqual(packetA, *outPktA), true); + + /** + * @tc.steps: step5. DeSerialization bufferB to outPktB + * @tc.expected: step5. packetB == outPktB, outPktB != outPktA + */ + Message outMsgB; + outMsgB.SetMessageId(COMMIT_HISTORY_SYNC_MESSAGE); + outMsgB.SetMessageType(TYPE_REQUEST); + ret = CommitHistorySync::DeSerialization(bufferB.data(), lenB, &outMsgB); + ASSERT_EQ(ret, E_OK); + const CommitHistorySyncRequestPacket *outPktB = outMsgB.GetObject(); + ASSERT_NE(outPktB, nullptr); + EXPECT_EQ(IsCommitHistorySyncRequestPacketEqual(packetB, *outPktB), true); + EXPECT_EQ(IsCommitHistorySyncRequestPacketEqual(*outPktA, *outPktB), false); +} + +static void MakeCommitHistorySyncAckPacketA(CommitHistorySyncAckPacket &inPacket) +{ + std::vector commitVec; + commitVec.push_back(MakeMultiVerCommitA()); + commitVec.push_back(MakeMultiVerCommitC()); + inPacket.SetData(commitVec); + inPacket.SetErrorCode(10086); // 10086 is errorcode +} + +static void MakeCommitHistorySyncAckPacketB(CommitHistorySyncAckPacket &inPacket) +{ + std::vector commitVec; + commitVec.push_back(MakeMultiVerCommitB()); + commitVec.push_back(MakeMultiVerCommitC()); + commitVec.push_back(MakeMultiVerCommitB()); + inPacket.SetData(commitVec); + inPacket.SetErrorCode(10010); // 10010 is errorcode +} + +static bool IsCommitHistorySyncAckPacketEqual(const CommitHistorySyncAckPacket &inPacketA, + const CommitHistorySyncAckPacket &inPacketB) +{ + int errCodeA; + int errCodeB; + std::vector commitVecA; + std::vector commitVecB; + inPacketA.GetData(commitVecA); + inPacketB.GetData(commitVecB); + inPacketA.GetErrorCode(errCodeA); + inPacketB.GetErrorCode(errCodeB); + if (errCodeA != errCodeB) { + return false; + } + if (commitVecA.size() != commitVecB.size()) { + return false; + } + int count = 0; + for (auto &entry : commitVecA) { + if (!IsMultiVerCommitEqual(entry, commitVecB[count++])) { + return false; + } + } + return true; +} + +/** + * @tc.name: Commit History Sync Ack Packet 001 + * @tc.desc: Verify CommitHistorySyncAckPacket Serialization and DeSerialization + * @tc.type: FUNC + * @tc.require: AR000BVRNU AR000CQE0J + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, CommitHistorySyncAckPacket001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create CommitHistorySyncAckPacket packetA aand packetB + */ + CommitHistorySyncAckPacket packetA; + CommitHistorySyncAckPacket packetB; + MakeCommitHistorySyncAckPacketA(packetA); + MakeCommitHistorySyncAckPacketB(packetB); + Message oriMsgA; + Message oriMsgB; + oriMsgA.SetCopiedObject(packetA); + oriMsgA.SetMessageId(COMMIT_HISTORY_SYNC_MESSAGE); + oriMsgA.SetMessageType(TYPE_RESPONSE); + oriMsgB.SetCopiedObject(packetB); + oriMsgB.SetMessageId(COMMIT_HISTORY_SYNC_MESSAGE); + oriMsgB.SetMessageType(TYPE_RESPONSE); + + /** + * @tc.steps: step2. Serialization packetA to bufferA + */ + uint32_t lenA = CommitHistorySync::CalculateLen(&oriMsgA); + vector bufferA; + bufferA.resize(lenA); + int ret = CommitHistorySync::Serialization(bufferA.data(), lenA, &oriMsgA); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step3. Serialization packetB to bufferB + */ + uint32_t lenB = CommitHistorySync::CalculateLen(&oriMsgB); + vector bufferB; + bufferB.resize(lenB); + ret = CommitHistorySync::Serialization(bufferB.data(), lenB, &oriMsgB); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step4. DeSerialization bufferA to outPktA + * @tc.expected: step4. packetA == outPktA + */ + Message outMsgA; + outMsgA.SetMessageId(COMMIT_HISTORY_SYNC_MESSAGE); + outMsgA.SetMessageType(TYPE_RESPONSE); + ret = CommitHistorySync::DeSerialization(bufferA.data(), lenA, &outMsgA); + ASSERT_EQ(ret, E_OK); + const CommitHistorySyncAckPacket *outPktA = outMsgA.GetObject(); + ASSERT_NE(outPktA, nullptr); + EXPECT_EQ(IsCommitHistorySyncAckPacketEqual(packetA, *outPktA), true); + + /** + * @tc.steps: step5. DeSerialization bufferB to outPktB + * @tc.expected: step5. packetB == outPktB, outPktB!= outPktA + */ + Message outMsgB; + outMsgB.SetMessageId(COMMIT_HISTORY_SYNC_MESSAGE); + outMsgB.SetMessageType(TYPE_RESPONSE); + ret = CommitHistorySync::DeSerialization(bufferB.data(), lenB, &outMsgB); + ASSERT_EQ(ret, E_OK); + const CommitHistorySyncAckPacket *outPktB = outMsgB.GetObject(); + ASSERT_NE(outPktB, nullptr); + EXPECT_EQ(IsCommitHistorySyncAckPacketEqual(packetB, *outPktB), true); + EXPECT_EQ(IsCommitHistorySyncAckPacketEqual(*outPktA, *outPktB), false); +} + +static bool IsMultiVerRequestPacketEqual(const MultiVerRequestPacket &inPacketA, + const MultiVerRequestPacket &inPacketB) +{ + MultiVerCommitNode commitA; + MultiVerCommitNode commitB; + inPacketA.GetCommit(commitA); + inPacketB.GetCommit(commitB); + return IsMultiVerCommitEqual(commitA, commitB); +} + +/** + * @tc.name: MultiVerValueObject Request Packet 001 + * @tc.desc: Verify MultiVerRequestPacket Serialization and DeSerialization + * @tc.type: FUNC + * @tc.require: AR000BVRNU AR000CQE0J + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, MultiVerRequestPacket001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create CommitHistorySyncAckPacket packetA aand packetB + */ + MultiVerRequestPacket packetA; + MultiVerRequestPacket packetB; + MultiVerCommitNode commitA = MakeMultiVerCommitA(); + MultiVerCommitNode commitB = MakeMultiVerCommitB(); + packetA.SetCommit(commitA); + packetB.SetCommit(commitB); + Message oriMsgA; + Message oriMsgB; + oriMsgA.SetCopiedObject(packetA); + oriMsgA.SetMessageId(MULTI_VER_DATA_SYNC_MESSAGE); + oriMsgA.SetMessageType(TYPE_REQUEST); + oriMsgB.SetCopiedObject(packetB); + oriMsgB.SetMessageId(MULTI_VER_DATA_SYNC_MESSAGE); + oriMsgB.SetMessageType(TYPE_REQUEST); + + /** + * @tc.steps: step2. Serialization packetA to bufferA + */ + uint32_t lenA = MultiVerDataSync::CalculateLen(&oriMsgA); + vector bufferA; + bufferA.resize(lenA); + int ret = MultiVerDataSync::Serialization(bufferA.data(), lenA, &oriMsgA); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step3. Serialization packetB to bufferB + */ + uint32_t lenB = MultiVerDataSync::CalculateLen(&oriMsgB); + vector bufferB; + bufferB.resize(lenB); + ret = MultiVerDataSync::Serialization(bufferB.data(), lenB, &oriMsgB); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step4. DeSerialization bufferA to outPktA + * @tc.expected: step4. packetA == outPktA + */ + Message outMsgA; + outMsgA.SetMessageId(MULTI_VER_DATA_SYNC_MESSAGE); + outMsgA.SetMessageType(TYPE_REQUEST); + ret = MultiVerDataSync::DeSerialization(bufferA.data(), lenA, &outMsgA); + ASSERT_EQ(ret, E_OK); + const MultiVerRequestPacket *outPktA = outMsgA.GetObject(); + ASSERT_NE(outPktA, nullptr); + EXPECT_EQ(IsMultiVerRequestPacketEqual(packetA, *outPktA), true); + + /** + * @tc.steps: step5. DeSerialization bufferB to outPktB + * @tc.expected: step5. packetB == outPktB, outPktB!= outPktA + */ + Message outMsgB; + outMsgB.SetMessageId(MULTI_VER_DATA_SYNC_MESSAGE); + outMsgB.SetMessageType(TYPE_REQUEST); + ret = MultiVerDataSync::DeSerialization(bufferB.data(), lenB, &outMsgB); + ASSERT_EQ(ret, E_OK); + const MultiVerRequestPacket *outPktB = outMsgB.GetObject(); + ASSERT_NE(outPktB, nullptr); + EXPECT_EQ(IsMultiVerRequestPacketEqual(packetB, *outPktB), true); + EXPECT_EQ(IsMultiVerRequestPacketEqual(*outPktA, *outPktB), false); +} + +static void MakeMultiVerAckPacketA(MultiVerAckPacket &inPacket) +{ + std::vector> entryVec; + entryVec.push_back(vector(111, 11)); // 111 is length, 11 is value + entryVec.push_back(vector(222, 22)); // 222 is length, 22 is value + inPacket.SetData(entryVec); + inPacket.SetErrorCode(333); // 333 is errorcode +} + +static void MakeMultiVerAckPacketB(MultiVerAckPacket &inPacket) +{ + std::vector> entryVec; + entryVec.push_back(vector(999, 99)); // 999 is length, 99 is value + entryVec.push_back(vector(888, 88)); // 888 is length, 88 is value + inPacket.SetData(entryVec); + inPacket.SetErrorCode(777); // 777 is errorcode +} + +static bool IsMultiVerAckPacketEqual(const MultiVerAckPacket &inPacketA, const MultiVerAckPacket &inPacketB) +{ + int errCodeA; + int errCodeB; + std::vector> entryVecA; + std::vector> entryVecB; + inPacketA.GetData(entryVecA); + inPacketB.GetData(entryVecB); + inPacketA.GetErrorCode(errCodeA); + inPacketB.GetErrorCode(errCodeB); + if (errCodeA != errCodeB) { + return false; + } + if (entryVecA != entryVecB) { + return false; + } + return true; +} + +/** + * @tc.name: MultiVerValueObject Ack Packet 001 + * @tc.desc: Verify MultiVerAckPacket Serialization and DeSerialization + * @tc.type: FUNC + * @tc.require: AR000BVRNU AR000CQE0J + * @tc.author: xiaozhenjian + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, MultiVerAckPacket001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create MultiVerAckPacket packetA aand packetB + */ + MultiVerAckPacket packetA; + MultiVerAckPacket packetB; + MakeMultiVerAckPacketA(packetA); + MakeMultiVerAckPacketB(packetB); + Message oriMsgA; + Message oriMsgB; + oriMsgA.SetCopiedObject(packetA); + oriMsgA.SetMessageId(MULTI_VER_DATA_SYNC_MESSAGE); + oriMsgA.SetMessageType(TYPE_RESPONSE); + oriMsgB.SetCopiedObject(packetB); + oriMsgB.SetMessageId(MULTI_VER_DATA_SYNC_MESSAGE); + oriMsgB.SetMessageType(TYPE_RESPONSE); + + /** + * @tc.steps: step2. Serialization packetA to bufferA + */ + uint32_t lenA = MultiVerDataSync::CalculateLen(&oriMsgA); + vector bufferA; + bufferA.resize(lenA); + int ret = MultiVerDataSync::Serialization(bufferA.data(), lenA, &oriMsgA); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step3. Serialization packetB to bufferB + */ + uint32_t lenB = MultiVerDataSync::CalculateLen(&oriMsgB); + vector bufferB; + bufferB.resize(lenB); + ret = MultiVerDataSync::Serialization(bufferB.data(), lenB, &oriMsgB); + ASSERT_EQ(ret, E_OK); + + /** + * @tc.steps: step4. DeSerialization bufferA to outPktA + * @tc.expected: step4. packetA == outPktA + */ + Message outMsgA; + outMsgA.SetMessageId(MULTI_VER_DATA_SYNC_MESSAGE); + outMsgA.SetMessageType(TYPE_RESPONSE); + ret = MultiVerDataSync::DeSerialization(bufferA.data(), lenA, &outMsgA); + ASSERT_EQ(ret, E_OK); + const MultiVerAckPacket *outPktA = outMsgA.GetObject(); + ASSERT_NE(outPktA, nullptr); + EXPECT_EQ(IsMultiVerAckPacketEqual(packetA, *outPktA), true); + + /** + * @tc.steps: step5. DeSerialization bufferB to outPktB + * @tc.expected: step5. packetB == outPktB, outPktB!= outPktA + */ + Message outMsgB; + outMsgB.SetMessageId(MULTI_VER_DATA_SYNC_MESSAGE); + outMsgB.SetMessageType(TYPE_RESPONSE); + ret = MultiVerDataSync::DeSerialization(bufferB.data(), lenB, &outMsgB); + ASSERT_EQ(ret, E_OK); + const MultiVerAckPacket *outPktB = outMsgB.GetObject(); + ASSERT_NE(outPktB, nullptr); + EXPECT_EQ(IsMultiVerAckPacketEqual(packetB, *outPktB), true); + EXPECT_EQ(IsMultiVerAckPacketEqual(*outPktA, *outPktB), false); +} + +/** + * @tc.name: Simple Data Sync 001 + * @tc.desc: Verify normal simple data sync function. + * @tc.type: FUNC + * @tc.require: AR000BVDGR + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, SimpleDataSync001, TestSize.Level2) +{ + /** + * @tc.steps: step1. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + + /** + * @tc.steps: step2. deviceB put {k1, v1} + */ + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + + /** + * @tc.steps: step4. deviceB put {k2, v2} + */ + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_2, DistributedDBUnitTest::VALUE_2), E_OK); + + /** + * @tc.steps: step5. enable communicator and set deviceB,C online + */ + g_deviceB->Online(); + g_deviceC->Online(); + + /** + * @tc.steps: step6. wait for sync + * @tc.expected: step6. deviceA has {k1, v2} {k2, v2} + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_1, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_1); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_2, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_2); +} + +/** + * @tc.name: Big Data Sync 001 + * @tc.desc: Verify normal big data sync function. + * @tc.type: FUNC + * @tc.require: AR000BVDGR + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, BigDataSync001, TestSize.Level2) +{ + /** + * @tc.steps: step1. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + + /** + * @tc.steps: step2. deviceB put {k1, v1}, v1 size 1k + */ + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, BIG_VALUE_SIZE); // 1k +1 + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, value1), E_OK); + + /** + * @tc.steps: step4. deviceC put {k2, v2}, v2 size 1k + */ + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, BIG_VALUE_SIZE); // 1k +1 + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_2, value2), E_OK); + + /** + * @tc.steps: step5. set deviceB,C online + */ + g_deviceB->Online(); + g_deviceC->Online(); + + /** + * @tc.steps: step5. wait 2s for sync + * @tc.expected: step5. deviceA has {k1, v2} {k2, v2} + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_1, value), E_OK); + EXPECT_EQ(value, value1); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_2, value), E_OK); + EXPECT_EQ(value, value2); +} + +/** + * @tc.name: Limit Data Sync 001 + * @tc.desc: Verify normal limit data sync function. + * @tc.type: FUNC + * @tc.require: AR000BVDGR + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, LimitDataSync001, TestSize.Level2) +{ + /** + * @tc.steps: step1. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + /** + * @tc.steps: step2. deviceB put {k1, v1}, k1 size 1k, v1 size 4M + */ + Key key1; + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, LIMIT_KEY_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, LIMIT_VALUE_SIZE); + ASSERT_EQ(g_deviceB->PutData(key1, value1), E_OK); + + /** + * @tc.steps: step3. deviceC put {k2, v2}, k2 size 1k, v2 size 4M + */ + Key key2; + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(key2, LIMIT_KEY_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, LIMIT_VALUE_SIZE); + ASSERT_EQ(g_deviceC->PutData(key2, value2), E_OK); + + /** + * @tc.steps: step4. set deviceB,C online + */ + g_deviceB->Online(); + g_deviceC->Online(); + + /** + * @tc.steps: step5. wait 30 for sync + * @tc.expected: step5. deviceA has {k1, v2} {k2, v2} + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_LIMIT_TIME)); + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, key1, value), E_OK); + EXPECT_EQ(value, value1); + EXPECT_EQ(GetData(g_kvDelegatePtr, key2, value), E_OK); + EXPECT_EQ(value, value2); +} + +/** + * @tc.name: Multi Record 001 + * @tc.desc: Verify normal multi record sync function. + * @tc.type: FUNC + * @tc.require: AR000BVDGR + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, MultiRecord001, TestSize.Level2) +{ + /** + * @tc.steps: step1. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + + /** + * @tc.steps: step2. deviceB put {k1, v1} + */ + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + + /** + * @tc.steps: step4. deviceB put {k1, v2} v2 > 1K + */ + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, BIG_VALUE_SIZE); // 1k +1 + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, value2), E_OK); + + /** + * @tc.steps: step4. deviceB put {k2, v3} + */ + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_2, DistributedDBUnitTest::VALUE_3), E_OK); + + /** + * @tc.steps: step5. deviceB put {k3, v3} and delete k3 + */ + ASSERT_TRUE(g_deviceB->StartTransaction() == E_OK); + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_3, DistributedDBUnitTest::VALUE_3), E_OK); + ASSERT_EQ(g_deviceB->DeleteData(DistributedDBUnitTest::KEY_3), E_OK); + ASSERT_TRUE(g_deviceB->Commit() == E_OK); + + /** + * @tc.steps: step6. deviceC put {k4, v4} + */ + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_4, DistributedDBUnitTest::VALUE_4), E_OK); + + /** + * @tc.steps: step7. deviceB put {k4, v5} v2 > 1K + */ + Value value5; + DistributedDBToolsUnitTest::GetRandomKeyValue(value5, BIG_VALUE_SIZE); // 1k +1 + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_4, value5), E_OK); + + /** + * @tc.steps: step8. deviceB put {k5, v6} + */ + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_5, DistributedDBUnitTest::VALUE_6), E_OK); + + /** + * @tc.steps: step9. deviceB put {k6, v6} and delete k6 + */ + ASSERT_TRUE(g_deviceC->StartTransaction() == E_OK); + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_6, DistributedDBUnitTest::VALUE_6), E_OK); + ASSERT_EQ(g_deviceC->DeleteData(DistributedDBUnitTest::KEY_6), E_OK); + ASSERT_TRUE(g_deviceC->Commit() == E_OK); + + /** + * @tc.steps: step10. set deviceB,C online + */ + g_deviceB->Online(); + g_deviceC->Online(); + + /** + * @tc.steps: step11. wait 5s for sync + * @tc.expected: step11. deviceA has {k1, v2}, {k2, v3}, {k4, v5}, {k5, v6} + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_LONG_TIME)); + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_1, value), E_OK); + EXPECT_EQ(value, value2); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_2, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_3); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_4, value), E_OK); + EXPECT_EQ(value, value5); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_5, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_6); +} + +/** + * @tc.name: Net Disconnect Sync 001 + * @tc.desc: Test exception sync when net disconnected. + * @tc.type: FUNC + * @tc.require: AR000BVDGR + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, NetDisconnectSync001, TestSize.Level3) +{ + /** + * @tc.steps: step1. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + ASSERT_TRUE(g_deviceB->StartTransaction() == E_OK); + /** + * @tc.steps: step2. deviceB put {k1, v1} + */ + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + + /** + * @tc.steps: step4. deviceB put {k1, v2} v2 > 1K + */ + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, 1024 + 1); // 1k +1 + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, value2), E_OK); + + /** + * @tc.steps: step4. deviceB put {k2, v3} + */ + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_2, DistributedDBUnitTest::VALUE_3), E_OK); + + /** + * @tc.steps: step5. deviceB put {k3, v3} and delete k3 + */ + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_3, DistributedDBUnitTest::VALUE_3), E_OK); + ASSERT_EQ(g_deviceB->DeleteData(DistributedDBUnitTest::KEY_3), E_OK); + ASSERT_TRUE(g_deviceB->Commit() == E_OK); + + /** + * @tc.steps: step6. deviceB online and enable communicator + */ + g_deviceB->Online(); + + /** + * @tc.steps: step7. disable communicator and wait 5s + * @tc.expected: step7. deviceA has no key1, key2 + */ + g_communicatorAggregator->Disable(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_LONG_TIME + WAIT_LONG_TIME)); + + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_1, value), NOT_FOUND); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_2, value), NOT_FOUND); + + ASSERT_TRUE(g_deviceC->StartTransaction() == E_OK); + /** + * @tc.steps: step8. deviceC put {k4, v4} + */ + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_4, DistributedDBUnitTest::VALUE_4), E_OK); + + /** + * @tc.steps: step9. deviceB put {k4, v5} v2 > 1K + */ + Value value5; + DistributedDBToolsUnitTest::GetRandomKeyValue(value5, BIG_VALUE_SIZE); // 1k +1 + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_4, value5), E_OK); + + /** + * @tc.steps: step10. deviceB put {k5, v6} + */ + ASSERT_TRUE(g_deviceC->PutData(DistributedDBUnitTest::KEY_5, DistributedDBUnitTest::VALUE_6) == E_OK); + + /** + * @tc.steps: step11. deviceB put {k6, v6} and delete k6 + */ + ASSERT_TRUE(g_deviceC->PutData(DistributedDBUnitTest::KEY_6, DistributedDBUnitTest::VALUE_6) == E_OK); + ASSERT_TRUE(g_deviceC->DeleteData(DistributedDBUnitTest::KEY_6) == E_OK); + ASSERT_TRUE(g_deviceC->Commit() == E_OK); + + /** + * @tc.steps: step12. deviceC online and enable communicator + */ + g_communicatorAggregator->Enable(); + g_deviceC->Online(); + + /** + * @tc.steps: step13. wait 5s for sync + * @tc.expected: step13. deviceA has {k4, v5}, {k5, v6} + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_LONG_TIME)); // wait 5s + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_4, value), E_OK); + EXPECT_EQ(value, value5); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_5, value), E_OK); + EXPECT_EQ(value, DistributedDBUnitTest::VALUE_6); +} + +/** + * @tc.name: SyncQueue006 + * @tc.desc: multi version not support sync queue + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, SyncQueue006, TestSize.Level3) +{ + /** + * @tc.steps:step1. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + + /** + * @tc.steps:step2. Set PragmaCmd to be GET_QUEUED_SYNC_SIZE + * @tc.expected: step2. Expect return NOT_SUPPORT. + */ + int param; + PragmaData input = static_cast(¶m); + EXPECT_EQ(g_kvDelegatePtr->Pragma(GET_QUEUED_SYNC_SIZE, input), NOT_SUPPORT); + EXPECT_EQ(g_kvDelegatePtr->Pragma(SET_QUEUED_SYNC_LIMIT, input), NOT_SUPPORT); + EXPECT_EQ(g_kvDelegatePtr->Pragma(GET_QUEUED_SYNC_LIMIT, input), NOT_SUPPORT); +} + +/** + * @tc.name: PermissionCheck001 + * @tc.desc: deviceA permission check not pass + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, PermissionCheck001, TestSize.Level2) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (flag & CHECK_FLAG_RECEIVE) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + + /** + * @tc.steps: step2. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + + /** + * @tc.steps: step3. deviceB put {k1, v1} + */ + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + + /** + * @tc.steps: step4. deviceC put {k2, v2} + */ + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_2, DistributedDBUnitTest::VALUE_2), E_OK); + + /** + * @tc.steps: step5. enable communicator and set deviceB,C online + */ + g_deviceB->Online(); + g_deviceC->Online(); + + /** + * @tc.steps: step6. wait for sync + * @tc.expected: step6. deviceA do not has {k1, v2} {k2, v2} + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_1, value), NOT_FOUND); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_2, value), NOT_FOUND); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** + * @tc.name: PermissionCheck002 + * @tc.desc: deviceB deviceC permission check not pass + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBMultiVerP2PSyncTest, PermissionCheck002, TestSize.Level2) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (flag & CHECK_FLAG_SEND) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + + /** + * @tc.steps: step2. open a KvStoreNbDelegate as deviceA + */ + g_mgr.GetKvStore(STORE_ID, g_option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_1)); + + /** + * @tc.steps: step3. deviceB put {k1, v1} + */ + ASSERT_EQ(g_deviceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1), E_OK); + + /** + * @tc.steps: step4. deviceC put {k2, v2} + */ + ASSERT_EQ(g_deviceC->PutData(DistributedDBUnitTest::KEY_2, DistributedDBUnitTest::VALUE_2), E_OK); + + /** + * @tc.steps: step5. enable communicator and set deviceB,C online + */ + g_deviceB->Online(); + g_deviceC->Online(); + + /** + * @tc.steps: step6. wait for sync + * @tc.expected: step6. deviceA do not has {k1, v2} {k2, v2} + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_2)); + Value value; + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_1, value), NOT_FOUND); + EXPECT_EQ(GetData(g_kvDelegatePtr, DistributedDBUnitTest::KEY_2, value), NOT_FOUND); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_relational_ver_p2p_sync_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_relational_ver_p2p_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75a8193b3dde3c3c870c097d13599dc3e76e75a5 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_relational_ver_p2p_sync_test.cpp @@ -0,0 +1,1241 @@ +/* +* Copyright (c) 2021 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifdef RELATIONAL_STORE +#include + +#include "db_common.h" +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "isyncer.h" +#include "kv_virtual_device.h" +#include "platform_specific.h" +#include "relational_schema_object.h" +#include "relational_store_manager.h" +#include "relational_virtual_device.h" +#include "runtime_config.h" +#include "virtual_relational_ver_sync_db_interface.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + const std::string DEVICE_B = "deviceB"; + const std::string DEVICE_C = "deviceC"; + const std::string g_tableName = "TEST_TABLE"; + + const int ONE_HUNDERED = 100; + const char DEFAULT_CHAR = 'D'; + const std::string DEFAULT_TEXT = "This is a text"; + const std::vector DEFAULT_BLOB(ONE_HUNDERED, DEFAULT_CHAR); + + RelationalStoreManager g_mgr(APP_ID, USER_ID); + std::string g_testDir; + std::string g_dbDir; + std::string g_id; + std::vector g_storageType = { + StorageType::STORAGE_TYPE_INTEGER, StorageType::STORAGE_TYPE_REAL, + StorageType::STORAGE_TYPE_TEXT, StorageType::STORAGE_TYPE_BLOB + }; + DistributedDBToolsUnitTest g_tool; + RelationalStoreDelegate* g_rdbDelegatePtr = nullptr; + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + RelationalVirtualDevice *g_deviceB = nullptr; + RelationalVirtualDevice *g_deviceC = nullptr; + std::vector g_fieldInfoList; + RelationalStoreObserverUnitTest *g_observer = nullptr; + std::string GetDeviceTableName(const std::string &tableName) + { + return "naturalbase_rdb_aux_" + + tableName + "_" + DBCommon::TransferStringToHex(DBCommon::TransferHashString(DEVICE_B)); + } + + void OpenStore() + { + if (g_observer == nullptr) { + g_observer = new (std::nothrow) RelationalStoreObserverUnitTest(); + } + RelationalStoreDelegate::Option option = {g_observer}; + g_mgr.OpenStore(g_dbDir, STORE_ID_1, option, g_rdbDelegatePtr); + ASSERT_TRUE(g_rdbDelegatePtr != nullptr); + } + + int GetDB(sqlite3 *&db) + { + int flag = SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + const auto &dbPath = g_dbDir; + int rc = sqlite3_open_v2(dbPath.c_str(), &db, flag, nullptr); + if (rc != SQLITE_OK) { + return rc; + } + EXPECT_EQ(SQLiteUtils::RegisterCalcHash(db), E_OK); + EXPECT_EQ(SQLiteUtils::RegisterGetSysTime(db), E_OK); + EXPECT_EQ(sqlite3_exec(db, "PRAGMA journal_mode=WAL;", nullptr, nullptr, nullptr), SQLITE_OK); + return rc; + } + + std::string GetType(StorageType type) + { + static std::map typeMap = { + {StorageType::STORAGE_TYPE_INTEGER, "INT"}, + {StorageType::STORAGE_TYPE_REAL, "DOUBLE"}, + {StorageType::STORAGE_TYPE_TEXT, "TEXT"}, + {StorageType::STORAGE_TYPE_BLOB, "BLOB"} + }; + if (typeMap.find(type) == typeMap.end()) { + type = StorageType::STORAGE_TYPE_INTEGER; + } + return typeMap[type]; + } + + int DropTable(sqlite3 *db, const std::string &tableName) + { + std::string sql = "DROP TABLE " + tableName + ";"; + return sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr); + } + + int CreateTable(sqlite3 *db, std::vector &fieldInfoList, const std::string &tableName) + { + std::string sql = "CREATE TABLE " + tableName + "("; + int index = 0; + for (const auto &field : fieldInfoList) { + if (index != 0) { + sql += ","; + } + sql += field.GetFieldName() + " "; + std::string type = GetType(field.GetStorageType()); + sql += type + " "; + if (index == 0) { + sql += "PRIMARY KEY NOT NULL "; + } + index++; + } + sql += ");"; + int rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr); + return rc; + } + + int PrepareInsert(sqlite3 *db, sqlite3_stmt *&statement, + std::vector fieldInfoList, const std::string &tableName) + { + std::string sql = "INSERT OR REPLACE INTO " + tableName + "("; + int index = 0; + for (const auto &fieldInfo : fieldInfoList) { + if (index != 0) { + sql += ","; + } + sql += fieldInfo.GetFieldName(); + index++; + } + sql += ") VALUES ("; + while (index > 0) { + sql += "?"; + if (index != 1) { + sql += ", "; + } + index--; + } + sql += ");"; + return sqlite3_prepare_v2(db, sql.c_str(), -1, &statement, nullptr); + } + + int SimulateCommitData(sqlite3 *db, sqlite3_stmt *&statement) + { + sqlite3_exec(db, "BEGIN IMMEDIATE TRANSACTION", nullptr, nullptr, nullptr); + + int rc = sqlite3_step(statement); + + sqlite3_exec(db, "COMMIT TRANSACTION", nullptr, nullptr, nullptr); + return rc; + } + + void BindValue(const DataValue &item, sqlite3_stmt *stmt, int col) + { + switch (item.GetType()) { + case StorageType::STORAGE_TYPE_INTEGER: { + int64_t intData = 0; + (void)item.GetInt64(intData); + EXPECT_EQ(sqlite3_bind_int64(stmt, col, intData), SQLITE_OK); + break; + } + + case StorageType::STORAGE_TYPE_REAL: { + double doubleData = 0; + (void)item.GetDouble(doubleData); + EXPECT_EQ(sqlite3_bind_double(stmt, col, doubleData), SQLITE_OK); + break; + } + + case StorageType::STORAGE_TYPE_TEXT: { + std::string strData; + (void)item.GetText(strData); + EXPECT_EQ(SQLiteUtils::BindTextToStatement(stmt, col, strData), E_OK); + break; + } + + case StorageType::STORAGE_TYPE_BLOB: { + Blob blob; + (void)item.GetBlob(blob); + std::vector blobData(blob.GetData(), blob.GetData() + blob.GetSize()); + EXPECT_EQ(SQLiteUtils::BindBlobToStatement(stmt, col, blobData, true), E_OK); + break; + } + + case StorageType::STORAGE_TYPE_NULL: { + EXPECT_EQ(SQLiteUtils::MapSQLiteErrno(sqlite3_bind_null(stmt, col)), E_OK); + break; + } + + default: + break; + } + } + + void InsertValue(sqlite3 *db, std::map &dataMap, + std::vector &fieldInfoList, const std::string &tableName) + { + sqlite3_stmt *stmt = nullptr; + EXPECT_EQ(PrepareInsert(db, stmt, fieldInfoList, tableName), SQLITE_OK); + for (int i = 0; i < static_cast(fieldInfoList.size()); ++i) { + const auto &fieldName = fieldInfoList[i].GetFieldName(); + ASSERT_TRUE(dataMap.find(fieldName) != dataMap.end()); + const auto &item = dataMap[fieldName]; + const int index = i + 1; + BindValue(item, stmt, index); + } + EXPECT_EQ(SimulateCommitData(db, stmt), SQLITE_DONE); + sqlite3_finalize(stmt); + } + + void InsertValue(sqlite3 *db, std::map &dataMap) + { + InsertValue(db, dataMap, g_fieldInfoList, g_tableName); + } + + void SetNull(DataValue &dataValue) + { + dataValue.ResetValue(); + } + + void SetInt64(DataValue &dataValue) + { + dataValue = INT64_MAX; + } + + void SetDouble(DataValue &dataValue) + { + dataValue = 1.0; + } + + void SetText(DataValue &dataValue) + { + dataValue.SetText(DEFAULT_TEXT); + } + + void SetBlob(DataValue &dataValue) + { + Blob blob; + blob.WriteBlob(DEFAULT_BLOB.data(), DEFAULT_BLOB.size()); + dataValue.SetBlob(blob); + } + + void GenerateValue(std::map &dataMap, std::vector &fieldInfoList) + { + static std::map typeMapFunction = { + {StorageType::STORAGE_TYPE_NULL, &SetNull}, + {StorageType::STORAGE_TYPE_INTEGER, &SetInt64}, + {StorageType::STORAGE_TYPE_REAL, &SetDouble}, + {StorageType::STORAGE_TYPE_TEXT, &SetText}, + {StorageType::STORAGE_TYPE_BLOB, &SetBlob} + }; + for (auto &fieldInfo : fieldInfoList) { + DataValue dataValue; + if (typeMapFunction.find(fieldInfo.GetStorageType()) == typeMapFunction.end()) { + fieldInfo.SetStorageType(StorageType::STORAGE_TYPE_NULL); + } + typeMapFunction[fieldInfo.GetStorageType()](dataValue); + dataMap[fieldInfo.GetFieldName()] = std::move(dataValue); + } + } + + void InsertFieldInfo(std::vector &fieldInfoList) + { + fieldInfoList.clear(); + FieldInfo columnFirst; + columnFirst.SetFieldName("ID"); + columnFirst.SetStorageType(StorageType::STORAGE_TYPE_INTEGER); + columnFirst.SetColumnId(0); // the first column + FieldInfo columnSecond; + columnSecond.SetFieldName("NAME"); + columnSecond.SetStorageType(StorageType::STORAGE_TYPE_TEXT); + columnSecond.SetColumnId(1); // the 2nd column + FieldInfo columnThird; + columnThird.SetFieldName("AGE"); + columnThird.SetStorageType(StorageType::STORAGE_TYPE_INTEGER); + columnThird.SetColumnId(2); // the 3rd column(index 2 base 0) + fieldInfoList.push_back(columnFirst); + fieldInfoList.push_back(columnSecond); + fieldInfoList.push_back(columnThird); + } + + void BlockSync(const Query &query, SyncMode syncMode, DBStatus exceptStatus, + const std::vector &devices) + { + std::map> statusMap; + SyncStatusCallback callBack = [&statusMap]( + const std::map> &devicesMap) { + statusMap = devicesMap; + }; + DBStatus callStatus = g_rdbDelegatePtr->Sync(devices, syncMode, query, callBack, true); + EXPECT_EQ(callStatus, OK); + for (const auto &tablesRes : statusMap) { + for (const auto &tableStatus : tablesRes.second) { + EXPECT_EQ(tableStatus.status, exceptStatus); + } + } + } + + void BlockSync(const std::string &tableName, SyncMode syncMode, DBStatus exceptStatus, + const std::vector &devices) + { + Query query = Query::Select(tableName); + BlockSync(query, syncMode, exceptStatus, devices); + } + + void BlockSync(SyncMode syncMode, DBStatus exceptStatus, const std::vector &devices) + { + BlockSync(g_tableName, syncMode, exceptStatus, devices); + } + + int PrepareSelect(sqlite3 *db, sqlite3_stmt *&statement, const std::string &table) + { + const std::string sql = "SELECT * FROM " + table; + return sqlite3_prepare_v2(db, sql.c_str(), -1, &statement, nullptr); + } + + void GetDataValue(sqlite3_stmt *statement, int col, DataValue &dataValue) + { + int type = sqlite3_column_type(statement, col); + switch (type) { + case SQLITE_INTEGER: { + dataValue = static_cast(sqlite3_column_int64(statement, col)); + break; + } + case SQLITE_FLOAT: { + dataValue = sqlite3_column_double(statement, col); + break; + } + case SQLITE_TEXT: { + std::string str; + SQLiteUtils::GetColumnTextValue(statement, col, str); + dataValue.SetText(str); + break; + } + case SQLITE_BLOB: { + std::vector blobValue; + (void)SQLiteUtils::GetColumnBlobValue(statement, col, blobValue); + Blob blob; + blob.WriteBlob(blobValue.data(), static_cast(blobValue.size())); + dataValue.SetBlob(blob); + break; + } + case SQLITE_NULL: + break; + default: + LOGW("unknown type[%d] column[%d] ignore", type, col); + } + } + + void GetSyncDataStep(std::map &dataMap, sqlite3_stmt *statement, + std::vector fieldInfoList) + { + int columnCount = sqlite3_column_count(statement); + ASSERT_EQ(static_cast(columnCount), fieldInfoList.size()); + for (int col = 0; col < columnCount; ++col) { + DataValue dataValue; + GetDataValue(statement, col, dataValue); + dataMap[fieldInfoList.at(col).GetFieldName()] = std::move(dataValue); + } + } + + void GetSyncData(sqlite3 *db, std::map &dataMap, const std::string &tableName, + std::vector fieldInfoList) + { + sqlite3_stmt *statement = nullptr; + EXPECT_EQ(PrepareSelect(db, statement, GetDeviceTableName(tableName)), SQLITE_OK); + while (true) { + int rc = sqlite3_step(statement); + if (rc != SQLITE_ROW) { + LOGD("GetSyncData Exist by code[%d]", rc); + break; + } + GetSyncDataStep(dataMap, statement, fieldInfoList); + } + sqlite3_finalize(statement); + } + + void InsertValueToDB(std::map &dataMap) + { + sqlite3 *db = nullptr; + EXPECT_EQ(GetDB(db), SQLITE_OK); + InsertValue(db, dataMap); + sqlite3_close(db); + } + + void PrepareBasicTable(const std::string &tableName, std::vector &fieldInfoList, + std::vector &remoteDeviceVec, bool createDistributedTable = true) + { + sqlite3 *db = nullptr; + EXPECT_EQ(GetDB(db), SQLITE_OK); + if (fieldInfoList.empty()) { + InsertFieldInfo(fieldInfoList); + } + for (auto &dev : remoteDeviceVec) { + dev->SetLocalFieldInfo(fieldInfoList); + } + EXPECT_EQ(CreateTable(db, fieldInfoList, tableName), SQLITE_OK); + TableInfo tableInfo; + SQLiteUtils::AnalysisSchema(db, tableName, tableInfo); + for (auto &dev : remoteDeviceVec) { + dev->SetTableInfo(tableInfo); + } + if (createDistributedTable) { + EXPECT_EQ(g_rdbDelegatePtr->CreateDistributedTable(tableName), OK); + } + + sqlite3_close(db); + } + + void PrepareEnvironment(std::map &dataMap, + std::vector remoteDeviceVec) + { + PrepareBasicTable(g_tableName, g_fieldInfoList, remoteDeviceVec); + GenerateValue(dataMap, g_fieldInfoList); + InsertValueToDB(dataMap); + } + + void PrepareVirtualEnvironment(std::map &dataMap, const std::string &tableName, + std::vector &fieldInfoList, std::vector remoteDeviceVec, + bool createDistributedTable = true) + { + PrepareBasicTable(tableName, fieldInfoList, remoteDeviceVec, createDistributedTable); + GenerateValue(dataMap, fieldInfoList); + VirtualRowData virtualRowData; + for (const auto &item : dataMap) { + virtualRowData.objectData.PutDataValue(item.first, item.second); + } + virtualRowData.logInfo.timestamp = 1; + g_deviceB->PutData(tableName, {virtualRowData}); + } + + void PrepareVirtualEnvironment(std::map &dataMap, + std::vector remoteDeviceVec, bool createDistributedTable = true) + { + PrepareVirtualEnvironment(dataMap, g_tableName, g_fieldInfoList, remoteDeviceVec, createDistributedTable); + } + + void CheckData(std::map &targetMap, const std::string &tableName, + std::vector fieldInfoList) + { + std::map dataMap; + sqlite3 *db = nullptr; + EXPECT_EQ(GetDB(db), SQLITE_OK); + GetSyncData(db, dataMap, tableName, fieldInfoList); + sqlite3_close(db); + + for (const auto &[fieldName, dataValue] : targetMap) { + ASSERT_TRUE(dataMap.find(fieldName) != dataMap.end()); + EXPECT_TRUE(dataMap[fieldName] == dataValue); + } + } + + void CheckData(std::map &targetMap) + { + CheckData(targetMap, g_tableName, g_fieldInfoList); + } + + void CheckVirtualData(const std::string &tableName, std::map &data) + { + std::vector targetData; + g_deviceB->GetAllSyncData(tableName, targetData); + + ASSERT_EQ(targetData.size(), 1u); + for (auto &[field, value] : data) { + DataValue target; + EXPECT_EQ(targetData[0].objectData.GetDataValue(field, target), E_OK); + LOGD("field %s actual_val[%s] except_val[%s]", field.c_str(), target.ToString().c_str(), + value.ToString().c_str()); + EXPECT_TRUE(target == value); + } + } + + void CheckVirtualData(std::map &data) + { + CheckVirtualData(g_tableName, data); + } + + void GetFieldInfo(std::vector &fieldInfoList, std::vector typeList) + { + fieldInfoList.clear(); + for (size_t index = 0; index < typeList.size(); index++) { + const auto &type = typeList[index]; + FieldInfo fieldInfo; + fieldInfo.SetFieldName("field_" + std::to_string(index)); + fieldInfo.SetColumnId(index); + fieldInfo.SetStorageType(type); + fieldInfoList.push_back(fieldInfo); + } + } + + void InsertValueToDB(std::map &dataMap, + std::vector fieldInfoList, const std::string &tableName) + { + sqlite3 *db = nullptr; + EXPECT_EQ(GetDB(db), SQLITE_OK); + InsertValue(db, dataMap, fieldInfoList, tableName); + sqlite3_close(db); + } + + void PrepareEnvironment(std::map &dataMap, const std::string &tableName, + std::vector &localFieldInfoList, std::vector &remoteFieldInfoList, + std::vector remoteDeviceVec) + { + sqlite3 *db = nullptr; + EXPECT_EQ(GetDB(db), SQLITE_OK); + + EXPECT_EQ(CreateTable(db, remoteFieldInfoList, tableName), SQLITE_OK); + TableInfo tableInfo; + SQLiteUtils::AnalysisSchema(db, tableName, tableInfo); + for (auto &dev : remoteDeviceVec) { + dev->SetTableInfo(tableInfo); + } + + EXPECT_EQ(DropTable(db, tableName), SQLITE_OK); + EXPECT_EQ(CreateTable(db, localFieldInfoList, tableName), SQLITE_OK); + EXPECT_EQ(g_rdbDelegatePtr->CreateDistributedTable(tableName), OK); + + sqlite3_close(db); + + GenerateValue(dataMap, localFieldInfoList); + InsertValueToDB(dataMap, localFieldInfoList, tableName); + for (auto &dev : remoteDeviceVec) { + dev->SetLocalFieldInfo(remoteFieldInfoList); + } + } + + void PrepareEnvironment(std::map &dataMap, + std::vector &localFieldInfoList, std::vector &remoteFieldInfoList, + std::vector remoteDeviceVec) + { + PrepareEnvironment(dataMap, g_tableName, localFieldInfoList, remoteFieldInfoList, remoteDeviceVec); + } + + void CheckIdentify(RelationalStoreObserverUnitTest *observer) + { + ASSERT_NE(observer, nullptr); + StoreProperty property = observer->GetStoreProperty(); + EXPECT_EQ(property.appId, APP_ID); + EXPECT_EQ(property.storeId, STORE_ID_1); + EXPECT_EQ(property.userId, USER_ID); + } +} + +class DistributedDBRelationalVerP2PSyncTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void DistributedDBRelationalVerP2PSyncTest::SetUpTestCase() +{ + /** + * @tc.setup: Init datadir and Virtual Communicator. + */ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_dbDir = g_testDir + "/test.db"; + sqlite3 *db = nullptr; + ASSERT_EQ(GetDB(db), SQLITE_OK); + sqlite3_close(db); + + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); + + g_id = g_mgr.GetRelationalStoreIdentifier(USER_ID, APP_ID, STORE_ID_1); +} + +void DistributedDBRelationalVerP2PSyncTest::TearDownTestCase() +{ + /** + * @tc.teardown: Release virtual Communicator and clear data dir. + */ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); + LOGD("TearDownTestCase FINISH"); +} + +void DistributedDBRelationalVerP2PSyncTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_fieldInfoList.clear(); + /** + * @tc.setup: create virtual device B, and get a KvStoreNbDelegate as deviceA + */ + sqlite3 *db = nullptr; + ASSERT_EQ(GetDB(db), SQLITE_OK); + sqlite3_close(db); + OpenStore(); + g_deviceB = new (std::nothrow) RelationalVirtualDevice(DEVICE_B); + ASSERT_TRUE(g_deviceB != nullptr); + g_deviceC = new (std::nothrow) RelationalVirtualDevice(DEVICE_C); + ASSERT_TRUE(g_deviceC != nullptr); + auto *syncInterfaceB = new (std::nothrow) VirtualRelationalVerSyncDBInterface(); + auto *syncInterfaceC = new (std::nothrow) VirtualRelationalVerSyncDBInterface(); + ASSERT_TRUE(syncInterfaceB != nullptr); + ASSERT_EQ(g_deviceB->Initialize(g_communicatorAggregator, syncInterfaceB), E_OK); + ASSERT_EQ(g_deviceC->Initialize(g_communicatorAggregator, syncInterfaceC), E_OK); + + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + return true; + }; + EXPECT_EQ(RuntimeConfig::SetPermissionCheckCallback(permissionCheckCallback), OK); +} + +void DistributedDBRelationalVerP2PSyncTest::TearDown(void) +{ + /** + * @tc.teardown: Release device A, B, C + */ + if (g_rdbDelegatePtr != nullptr) { + LOGD("CloseStore Start"); + ASSERT_EQ(g_mgr.CloseStore(g_rdbDelegatePtr), OK); + g_rdbDelegatePtr = nullptr; + } + if (g_deviceB != nullptr) { + delete g_deviceB; + g_deviceB = nullptr; + } + if (g_deviceC != nullptr) { + delete g_deviceC; + g_deviceC = nullptr; + } + if (g_observer != nullptr) { + delete g_observer; + g_observer = nullptr; + } + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(RuntimeConfig::SetPermissionCheckCallback(nullCallback), OK); + EXPECT_EQ(DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir), OK); + LOGD("TearDown FINISH"); +} + +/** +* @tc.name: Normal Sync 001 +* @tc.desc: Test normal push sync for add data. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, NormalSync001, TestSize.Level0) +{ + std::map dataMap; + PrepareEnvironment(dataMap, {g_deviceB}); + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + CheckVirtualData(dataMap); +} + +/** +* @tc.name: Normal Sync 002 +* @tc.desc: Test normal pull sync for add data. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, NormalSync002, TestSize.Level0) +{ + std::map dataMap; + PrepareEnvironment(dataMap, {g_deviceB}); + + Query query = Query::Select(g_tableName); + g_deviceB->GenericVirtualDevice::Sync(DistributedDB::SYNC_MODE_PULL_ONLY, query, true); + + CheckVirtualData(dataMap); +} + +/** +* @tc.name: Normal Sync 003 +* @tc.desc: Test normal push sync for update data. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, NormalSync003, TestSize.Level1) +{ + std::map dataMap; + PrepareEnvironment(dataMap, {g_deviceB}); + + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + CheckVirtualData(dataMap); + + GenerateValue(dataMap, g_fieldInfoList); + dataMap["AGE"] = static_cast(1); + InsertValueToDB(dataMap); + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + CheckVirtualData(dataMap); +} + +/** +* @tc.name: Normal Sync 004 +* @tc.desc: Test normal push sync for delete data. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, NormalSync004, TestSize.Level1) +{ + std::map dataMap; + PrepareEnvironment(dataMap, {g_deviceB}); + + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + CheckVirtualData(dataMap); + + sqlite3 *db = nullptr; + EXPECT_EQ(GetDB(db), SQLITE_OK); + std::string sql = "DELETE FROM TEST_TABLE WHERE 1 = 1"; + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(db, sql), E_OK); + sqlite3_close(db); + + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + std::vector dataList; + EXPECT_EQ(g_deviceB->GetAllSyncData(g_tableName, dataList), E_OK); + EXPECT_EQ(static_cast(dataList.size()), 1); + for (const auto &item : dataList) { + EXPECT_EQ(item.logInfo.flag, DataItem::DELETE_FLAG); + } +} + +/** +* @tc.name: Normal Sync 005 +* @tc.desc: Test normal push sync for add data. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, NormalSync005, TestSize.Level0) +{ + std::map dataMap; + PrepareVirtualEnvironment(dataMap, {g_deviceB}); + + Query query = Query::Select(g_tableName); + g_deviceB->GenericVirtualDevice::Sync(SYNC_MODE_PUSH_ONLY, query, true); + + CheckData(dataMap); + + g_rdbDelegatePtr->RemoveDeviceData(DEVICE_B); + + g_deviceB->GenericVirtualDevice::Sync(SYNC_MODE_PUSH_ONLY, query, true); + + CheckData(dataMap); +} + +/** +* @tc.name: Normal Sync 007 +* @tc.desc: Test normal sync for miss query data. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, NormalSync007, TestSize.Level1) +{ + std::map dataMap; + PrepareEnvironment(dataMap, {g_deviceB}); + + Query query = Query::Select(g_tableName).EqualTo("NAME", DEFAULT_TEXT); + BlockSync(query, SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + CheckVirtualData(dataMap); + + sqlite3 *db = nullptr; + EXPECT_EQ(GetDB(db), SQLITE_OK); + std::string sql = "UPDATE TEST_TABLE SET NAME = '' WHERE 1 = 1"; + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(db, sql), E_OK); + sqlite3_close(db); + + BlockSync(query, SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + std::vector dataList; + EXPECT_EQ(g_deviceB->GetAllSyncData(g_tableName, dataList), E_OK); + EXPECT_EQ(static_cast(dataList.size()), 1); + for (const auto &item : dataList) { + EXPECT_EQ(item.logInfo.flag, DataItem::REMOTE_DEVICE_DATA_MISS_QUERY); + } +} + +/** +* @tc.name: Normal Sync 006 +* @tc.desc: Test normal pull sync for add data. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, NormalSync006, TestSize.Level1) +{ + std::map dataMap; + PrepareVirtualEnvironment(dataMap, {g_deviceB}); + + BlockSync(SYNC_MODE_PULL_ONLY, OK, {DEVICE_B}); + + CheckData(dataMap); +} + +/** +* @tc.name: AutoLaunchSync 001 +* @tc.desc: Test rdb autoLaunch success when callback return true. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, AutoLaunchSync001, TestSize.Level3) +{ + /** + * @tc.steps: step1. open rdb store, create distribute table and insert data + */ + std::map dataMap; + PrepareVirtualEnvironment(dataMap, {g_deviceB}); + + /** + * @tc.steps: step2. set auto launch callBack + */ + const AutoLaunchRequestCallback callback = [](const std::string &identifier, AutoLaunchParam ¶m) { + if (g_id != identifier) { + return false; + } + param.path = g_dbDir; + param.appId = APP_ID; + param.userId = USER_ID; + param.storeId = STORE_ID_1; + return true; + }; + g_mgr.SetAutoLaunchRequestCallback(callback); + /** + * @tc.steps: step3. close store ensure communicator has closed + */ + g_mgr.CloseStore(g_rdbDelegatePtr); + g_rdbDelegatePtr = nullptr; + /** + * @tc.steps: step4. RunCommunicatorLackCallback to autolaunch store + */ + LabelType labelType(g_id.begin(), g_id.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(labelType); + std::this_thread::sleep_for(std::chrono::seconds(1)); + /** + * @tc.steps: step5. Call sync expect sync successful + */ + Query query = Query::Select(g_tableName); + EXPECT_EQ(g_deviceB->GenericVirtualDevice::Sync(SYNC_MODE_PUSH_ONLY, query, true), E_OK); + /** + * @tc.steps: step6. check sync data ensure sync successful + */ + CheckData(dataMap); + + OpenStore(); + std::this_thread::sleep_for(std::chrono::minutes(1)); +} + +/** +* @tc.name: AutoLaunchSync 002 +* @tc.desc: Test rdb autoLaunch failed when callback return false. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, AutoLaunchSync002, TestSize.Level3) +{ + /** + * @tc.steps: step1. open rdb store, create distribute table and insert data + */ + std::map dataMap; + PrepareVirtualEnvironment(dataMap, {g_deviceB}); + + /** + * @tc.steps: step2. set auto launch callBack + */ + const AutoLaunchRequestCallback callback = [](const std::string &identifier, AutoLaunchParam ¶m) { + return false; + }; + g_mgr.SetAutoLaunchRequestCallback(callback); + /** + * @tc.steps: step2. close store ensure communicator has closed + */ + g_mgr.CloseStore(g_rdbDelegatePtr); + g_rdbDelegatePtr = nullptr; + /** + * @tc.steps: step3. store can't autoLaunch because callback return false + */ + LabelType labelType(g_id.begin(), g_id.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(labelType); + std::this_thread::sleep_for(std::chrono::seconds(1)); + /** + * @tc.steps: step4. Call sync expect sync fail + */ + Query query = Query::Select(g_tableName); + SyncOperation::UserCallback callBack = [](const std::map &statusMap) { + for (const auto &entry : statusMap) { + EXPECT_EQ(entry.second, static_cast(SyncOperation::OP_COMM_ABNORMAL)); + } + }; + EXPECT_EQ(g_deviceB->GenericVirtualDevice::Sync(SYNC_MODE_PUSH_ONLY, query, callBack, true), E_OK); + + OpenStore(); + std::this_thread::sleep_for(std::chrono::minutes(1)); +} + +/** +* @tc.name: AutoLaunchSync 003 +* @tc.desc: Test rdb autoLaunch failed when callback is nullptr. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, AutoLaunchSync003, TestSize.Level3) +{ + /** + * @tc.steps: step1. open rdb store, create distribute table and insert data + */ + std::map dataMap; + PrepareVirtualEnvironment(dataMap, {g_deviceB}); + + g_mgr.SetAutoLaunchRequestCallback(nullptr); + /** + * @tc.steps: step2. close store ensure communicator has closed + */ + g_mgr.CloseStore(g_rdbDelegatePtr); + g_rdbDelegatePtr = nullptr; + /** + * @tc.steps: step3. store can't autoLaunch because callback is nullptr + */ + LabelType labelType(g_id.begin(), g_id.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(labelType); + std::this_thread::sleep_for(std::chrono::seconds(1)); + /** + * @tc.steps: step4. Call sync expect sync fail + */ + Query query = Query::Select(g_tableName); + SyncOperation::UserCallback callBack = [](const std::map &statusMap) { + for (const auto &entry : statusMap) { + EXPECT_EQ(entry.second, static_cast(SyncOperation::OP_COMM_ABNORMAL)); + } + }; + EXPECT_EQ(g_deviceB->GenericVirtualDevice::Sync(SYNC_MODE_PUSH_ONLY, query, callBack, true), E_OK); + + OpenStore(); + std::this_thread::sleep_for(std::chrono::minutes(1)); +} + +/** +* @tc.name: Ability Sync 001 +* @tc.desc: Test ability sync success when has same schema. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, AbilitySync001, TestSize.Level1) +{ + std::map dataMap; + std::vector localFieldInfo; + GetFieldInfo(localFieldInfo, g_storageType); + + PrepareEnvironment(dataMap, localFieldInfo, localFieldInfo, {g_deviceB}); + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + CheckVirtualData(dataMap); +} + +/** +* @tc.name: Ability Sync 002 +* @tc.desc: Test ability sync failed when has different schema. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, AbilitySync002, TestSize.Level1) +{ + /** + * @tc.steps: step1. set local schema is (BOOL, INTEGER, REAL, TEXT, BLOB, INTEGER) + */ + std::map dataMap; + std::vector localFieldInfo; + std::vector localStorageType = g_storageType; + localStorageType.push_back(StorageType::STORAGE_TYPE_INTEGER); + GetFieldInfo(localFieldInfo, localStorageType); + + /** + * @tc.steps: step2. set remote schema is (BOOL, INTEGER, REAL, TEXT, BLOB, TEXT) + */ + std::vector remoteFieldInfo; + std::vector remoteStorageType = g_storageType; + remoteStorageType.push_back(StorageType::STORAGE_TYPE_TEXT); + GetFieldInfo(remoteFieldInfo, remoteStorageType); + + /** + * @tc.steps: step3. call sync + * @tc.expected: sync fail when abilitySync + */ + PrepareEnvironment(dataMap, localFieldInfo, remoteFieldInfo, {g_deviceB}); + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, SCHEMA_MISMATCH, {DEVICE_B}); +} + +/** +* @tc.name: Ability Sync 003 +* @tc.desc: Test ability sync failed when has different schema. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, AbilitySync003, TestSize.Level1) +{ + /** + * @tc.steps: step1. set local and remote schema is (BOOL, INTEGER, REAL, TEXT, BLOB) + */ + std::map dataMap; + std::vector schema; + std::vector localStorageType = g_storageType; + GetFieldInfo(schema, localStorageType); + + /** + * @tc.steps: step2. create table and insert data + */ + PrepareEnvironment(dataMap, schema, schema, {g_deviceB}); + + /** + * @tc.steps: step3. change local table to (BOOL, INTEGER, REAL, TEXT, BLOB) + * @tc.expected: sync fail + */ + g_communicatorAggregator->RegOnDispatch([](const std::string &target, Message *inMsg) { + if (target != "real_device") { + return; + } + if (inMsg->GetMessageType() != TYPE_NOTIFY || inMsg->GetMessageId() != ABILITY_SYNC_MESSAGE) { + return; + } + sqlite3 *db = nullptr; + EXPECT_EQ(GetDB(db), SQLITE_OK); + ASSERT_NE(db, nullptr); + std::string alterSql = "ALTER TABLE " + g_tableName + " ADD COLUMN NEW_COLUMN TEXT DEFAULT 'DEFAULT_TEXT'"; + EXPECT_EQ(sqlite3_exec(db, alterSql.c_str(), nullptr, nullptr, nullptr), SQLITE_OK); + EXPECT_EQ(sqlite3_close(db), SQLITE_OK); + EXPECT_EQ(g_rdbDelegatePtr->CreateDistributedTable(g_tableName), OK); + }); + + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + g_communicatorAggregator->RegOnDispatch(nullptr); +} + +/** +* @tc.name: Ability Sync 004 +* @tc.desc: Test ability sync failed when one device hasn't distributed table. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, AbilitySync004, TestSize.Level1) +{ + std::map dataMap; + PrepareVirtualEnvironment(dataMap, {g_deviceB}, false); + + Query query = Query::Select(g_tableName); + int res = DB_ERROR; + auto callBack = [&res](std::map resMap) { + if (resMap.find("real_device") != resMap.end()) { + res = resMap["real_device"]; + } + }; + EXPECT_EQ(g_deviceB->GenericVirtualDevice::Sync(DistributedDB::SYNC_MODE_PULL_ONLY, query, callBack, true), E_OK); + EXPECT_EQ(res, static_cast(SyncOperation::Status::OP_SCHEMA_INCOMPATIBLE)); +} + +/** +* @tc.name: WaterMark 001 +* @tc.desc: Test sync success after erase waterMark. +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, WaterMark001, TestSize.Level1) +{ + std::map dataMap; + PrepareEnvironment(dataMap, {g_deviceB}); + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + CheckVirtualData(dataMap); + + EXPECT_EQ(g_rdbDelegatePtr->RemoveDeviceData(g_deviceB->GetDeviceId(), g_tableName), OK); + g_deviceB->EraseSyncData(g_tableName); + + BlockSync(SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + CheckVirtualData(dataMap); +} + +/* +* @tc.name: pressure sync 001 +* @tc.desc: Test rdb sync different table at same time +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhangqiquan +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, PressureSync001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create table A and device A push data to device B + * @tc.expected: step1. all is ok + */ + std::map tableADataMap; + std::vector tableAFieldInfo; + std::vector localStorageType = g_storageType; + localStorageType.push_back(StorageType::STORAGE_TYPE_INTEGER); + GetFieldInfo(tableAFieldInfo, localStorageType); + const std::string tableNameA = "TABLE_A"; + PrepareEnvironment(tableADataMap, tableNameA, tableAFieldInfo, tableAFieldInfo, {g_deviceB}); + + /** + * @tc.steps: step2. create table B and device B push data to device A + * @tc.expected: step2. all is ok + */ + std::map tableBDataMap; + std::vector tableBFieldInfo; + localStorageType = g_storageType; + localStorageType.push_back(StorageType::STORAGE_TYPE_REAL); + GetFieldInfo(tableBFieldInfo, localStorageType); + const std::string tableNameB = "TABLE_B"; + PrepareVirtualEnvironment(tableBDataMap, tableNameB, tableBFieldInfo, {g_deviceB}); + + std::condition_variable cv; + bool subFinish = false; + std::thread subThread = std::thread([&subFinish, &cv, &tableNameA, &tableADataMap]() { + BlockSync(tableNameA, SyncMode::SYNC_MODE_PUSH_ONLY, OK, {DEVICE_B}); + + CheckVirtualData(tableNameA, tableADataMap); + subFinish = true; + cv.notify_all(); + }); + subThread.detach(); + + Query query = Query::Select(tableNameB); + g_deviceB->GenericVirtualDevice::Sync(SYNC_MODE_PUSH_ONLY, query, true); + CheckData(tableBDataMap, tableNameB, tableBFieldInfo); + + std::mutex mutex; + std::unique_lock lock(mutex); + cv.wait(lock, [&subFinish] { return subFinish; }); +} + +/* +* @tc.name: relation observer 001 +* @tc.desc: Test relation observer while normal pull sync +* @tc.type: FUNC +* @tc.require: +* @tc.author: zhuwentao +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, Observer001, TestSize.Level0) +{ + /** + * @tc.steps: step1. device A create table and device B insert data and device C don't insert data + * @tc.expected: step1. create and insert ok + */ + g_observer->ResetToZero(); + std::map dataMap; + PrepareVirtualEnvironment(dataMap, {g_deviceB, g_deviceC}); + /** + * @tc.steps: step2. device A pull sync mode + * @tc.expected: step2. sync ok + */ + BlockSync(SyncMode::SYNC_MODE_PULL_ONLY, OK, {DEVICE_B, DEVICE_C}); + /** + * @tc.steps: step3. device A check observer + * @tc.expected: step2. data change device is deviceB + */ + EXPECT_EQ(g_observer->GetCallCount(), 1u); + EXPECT_EQ(g_observer->GetDataChangeDevice(), DEVICE_B); + CheckIdentify(g_observer); +} + +/** +* @tc.name: relation observer 002 +* @tc.desc: Test rdb observer ok in autolauchCallback scene +* @tc.type: FUNC +* @tc.require: AR000GK58N +* @tc.author: zhuwentao +*/ +HWTEST_F(DistributedDBRelationalVerP2PSyncTest, observer002, TestSize.Level3) +{ + /** + * @tc.steps: step1. open rdb store, create distribute table and insert data + */ + g_observer->ResetToZero(); + std::map dataMap; + PrepareVirtualEnvironment(dataMap, {g_deviceB}); + + /** + * @tc.steps: step2. set auto launch callBack + */ + RelationalStoreObserverUnitTest *observer = new (std::nothrow) RelationalStoreObserverUnitTest(); + const AutoLaunchRequestCallback callback = [observer](const std::string &identifier, AutoLaunchParam ¶m) { + if (g_id != identifier) { + return false; + } + param.path = g_dbDir; + param.appId = APP_ID; + param.userId = USER_ID; + param.storeId = STORE_ID_1; + param.option.storeObserver = observer; + return true; + }; + g_mgr.SetAutoLaunchRequestCallback(callback); + /** + * @tc.steps: step3. close store ensure communicator has closed + */ + g_mgr.CloseStore(g_rdbDelegatePtr); + g_rdbDelegatePtr = nullptr; + /** + * @tc.steps: step4. RunCommunicatorLackCallback to autolaunch store + */ + LabelType labelType(g_id.begin(), g_id.end()); + g_communicatorAggregator->RunCommunicatorLackCallback(labelType); + std::this_thread::sleep_for(std::chrono::seconds(1)); + /** + * @tc.steps: step5. Call sync expect sync successful and device A check observer + */ + Query query = Query::Select(g_tableName); + EXPECT_EQ(g_deviceB->GenericVirtualDevice::Sync(SYNC_MODE_PUSH_ONLY, query, true), E_OK); + EXPECT_EQ(observer->GetCallCount(), 1u); + EXPECT_EQ(observer->GetDataChangeDevice(), DEVICE_B); + CheckIdentify(observer); + std::this_thread::sleep_for(std::chrono::minutes(1)); + delete observer; +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_msg_schedule_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_msg_schedule_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9df55d1bb65c8d80ba25eaa61aa9549aa421e17e --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_msg_schedule_test.cpp @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "distributeddb_tools_unit_test.h" +#include "single_ver_data_message_schedule.h" +#include "single_ver_data_packet.h" +#include "single_ver_kv_sync_task_context.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { +} + +class DistributedDBSingleVerMsgScheduleTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSingleVerMsgScheduleTest::SetUpTestCase(void) +{ +} + +void DistributedDBSingleVerMsgScheduleTest::TearDownTestCase(void) +{ +} + +void DistributedDBSingleVerMsgScheduleTest::SetUp(void) +{ +} + +void DistributedDBSingleVerMsgScheduleTest::TearDown(void) +{ +} + +/** + * @tc.name: MsgSchedule001 + * @tc.desc: Test MsgSchedule function with normal sequenceId + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMsgScheduleTest, MsgSchedule001, TestSize.Level0) +{ + /** + * @tc.steps: step1. put msg sequence_3, sequence_2, sequence_1 + * @tc.expected: put msg ok + */ + SingleVerDataMessageSchedule msgSchedule; + auto *context = new SingleVerKvSyncTaskContext(); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_CURRENT); + DataSyncMessageInfo info; + info.sessionId_ = 10; + bool isNeedHandle = true; + bool isNeedContinue = true; + for (uint32_t i = 3; i >= 1; i--) { + info.sequenceId_ = i; + info.packetId_ = i; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + if (i > 1) { + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg == nullptr); + } + } + /** + * @tc.steps: step2. get msg + * @tc.expected: get msg by sequence_1, sequence_2, sequence_3 + */ + for (uint32_t i = 1; i <= 3; i++) { + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg != nullptr); + EXPECT_EQ(isNeedContinue, true); + EXPECT_EQ(isNeedHandle, true); + EXPECT_EQ(msg->GetSequenceId(), i); + msgSchedule.ScheduleInfoHandle(isNeedHandle, false, msg); + delete msg; + } + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg == nullptr); + RefObject::KillAndDecObjRef(context); + context = nullptr; +} + +/** + * @tc.name: MsgSchedule002 + * @tc.desc: Test MsgSchedule function with by low version + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMsgScheduleTest, MsgSchedule002, TestSize.Level0) +{ + /** + * @tc.steps: step1. put msg session1_sequence1, session2_sequence1 + * @tc.expected: put msg ok + */ + SingleVerDataMessageSchedule msgSchedule; + auto *context = new SingleVerKvSyncTaskContext(); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_RELEASE_2_0); + DataSyncMessageInfo info; + bool isNeedHandle = true; + bool isNeedContinue = true; + for (uint32_t i = 1; i <= 2; i++) { + info.sessionId_ = i; + info.sequenceId_ = 1; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + } + /** + * @tc.steps: step2. get msg + * @tc.expected: get msg by session2_sequence1 + */ + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg != nullptr); + EXPECT_EQ(isNeedContinue, true); + EXPECT_EQ(isNeedHandle, true); + EXPECT_EQ(msg->GetSequenceId(), 1u); + msgSchedule.ScheduleInfoHandle(false, false, msg); + delete msg; + msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg == nullptr); + RefObject::KillAndDecObjRef(context); + context = nullptr; +} + +/** + * @tc.name: MsgSchedule003 + * @tc.desc: Test MsgSchedule function with cross sessionId + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMsgScheduleTest, MsgSchedule003, TestSize.Level0) +{ + /** + * @tc.steps: step1. put msg session1_seq1, session2_seq1, session1_seq2, session2_seq2, and handle session1_seq1 + * @tc.expected: handle ok + */ + SingleVerDataMessageSchedule msgSchedule; + auto *context = new SingleVerKvSyncTaskContext(); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_CURRENT); + DataSyncMessageInfo info; + bool isNeedHandle = true; + bool isNeedContinue = true; + info.sessionId_ = 1; + info.sequenceId_ = 1; + info.packetId_ = 1; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg != nullptr); + EXPECT_EQ(isNeedContinue, true); + EXPECT_EQ(isNeedHandle, true); + msgSchedule.ScheduleInfoHandle(isNeedHandle, false, msg); + delete msg; + info.sessionId_ = 2; + info.sequenceId_ = 1; + info.packetId_ = 1; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + info.sessionId_ = 1; + info.sequenceId_ = 2; + info.packetId_ = 2; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + info.sessionId_ = 2; + info.sequenceId_ = 2; + info.packetId_ = 2; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + + /** + * @tc.steps: step2. get msg + * @tc.expected: get msg by session2_seq1, session2_seq2 + */ + for (uint32_t i = 1; i <= 2; i++) { + msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg != nullptr); + EXPECT_EQ(isNeedContinue, true); + EXPECT_EQ(isNeedHandle, true); + EXPECT_EQ(msg->GetSequenceId(), i); + EXPECT_EQ(msg->GetSessionId(), 2u); + msgSchedule.ScheduleInfoHandle(isNeedHandle, false, msg); + delete msg; + } + msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg == nullptr); + RefObject::KillAndDecObjRef(context); + context = nullptr; +} + +/** + * @tc.name: MsgSchedule004 + * @tc.desc: Test MsgSchedule function with same sessionId with different packetId + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMsgScheduleTest, MsgSchedule004, TestSize.Level0) +{ + /** + * @tc.steps: step1. put msg seq2_packet2, seq3_packet3, seq1_packet4, seq2_packet5, seq3_packet6 + * @tc.expected: put msg ok + */ + SingleVerDataMessageSchedule msgSchedule; + auto *context = new SingleVerKvSyncTaskContext(); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_CURRENT); + DataSyncMessageInfo info; + info.sessionId_ = 10; + bool isNeedHandle = true; + bool isNeedContinue = true; + for (uint32_t i = 2; i <= 3; i++) { + info.sequenceId_ = i; + info.packetId_ = i; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg == nullptr); + } + for (uint32_t i = 1; i <= 3; i++) { + info.sequenceId_ = i; + info.packetId_ = i + 3; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + } + /** + * @tc.steps: step2. get msg + * @tc.expected: drop seq2_packet2, seq3_packet3 and get seq1_packet4, seq2_packet5, seq3_packet6 + */ + isNeedHandle = true; + isNeedContinue = true; + for (uint32_t i = 1; i <= 3; i++) { + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg != nullptr); + EXPECT_EQ(isNeedContinue, true); + EXPECT_EQ(isNeedHandle, true); + EXPECT_EQ(msg->GetSequenceId(), i); + const DataRequestPacket *packet = msg->GetObject(); + EXPECT_EQ(packet->GetPacketId(), i + 3); + msgSchedule.ScheduleInfoHandle(isNeedHandle, false, msg); + delete msg; + } + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg == nullptr); + RefObject::KillAndDecObjRef(context); + context = nullptr; +} + +/** + * @tc.name: MsgSchedule005 + * @tc.desc: Test MsgSchedule function with same sessionId with different packetId + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMsgScheduleTest, MsgSchedule005, TestSize.Level0) +{ + /** + * @tc.steps: step1. put msg seq1_packet4, seq2_packet5, seq2_packet2, seq3_packet3 + * @tc.expected: put msg ok + */ + SingleVerDataMessageSchedule msgSchedule; + auto *context = new SingleVerKvSyncTaskContext(); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_CURRENT); + DataSyncMessageInfo info; + info.sessionId_ = 10; + bool isNeedHandle = true; + bool isNeedContinue = true; + for (uint32_t i = 1; i <= 2; i++) { + info.sequenceId_ = i; + info.packetId_ = i + 3; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + } + for (uint32_t i = 2; i <= 3; i++) { + info.sequenceId_ = i; + info.packetId_ = i; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + } + /** + * @tc.steps: step2. get msg + * @tc.expected: drop seq2_packet2, seq3_packet3 and get seq1_packet4, seq2_packet5 + */ + isNeedHandle = true; + isNeedContinue = true; + for (uint32_t i = 1; i <= 2; i++) { + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg != nullptr); + EXPECT_EQ(isNeedContinue, true); + EXPECT_EQ(isNeedHandle, true); + EXPECT_EQ(msg->GetSequenceId(), i); + const DataRequestPacket *packet = msg->GetObject(); + EXPECT_EQ(packet->GetPacketId(), i + 3); + msgSchedule.ScheduleInfoHandle(isNeedHandle, false, msg); + delete msg; + } + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg == nullptr); + RefObject::KillAndDecObjRef(context); + context = nullptr; +} + +/** + * @tc.name: MsgSchedule006 + * @tc.desc: Test MsgSchedule function with same sessionId and same sequenceId and packetId + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMsgScheduleTest, MsgSchedule006, TestSize.Level0) +{ + /** + * @tc.steps: step1. put msg seq1_packet1, seq2_packet2 + * @tc.expected: put msg ok + */ + SingleVerDataMessageSchedule msgSchedule; + auto *context = new SingleVerKvSyncTaskContext(); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_CURRENT); + DataSyncMessageInfo info; + info.sessionId_ = 10; + bool isNeedHandle = true; + bool isNeedContinue = true; + for (uint32_t i = 1; i <= 2; i++) { + info.sequenceId_ = i; + info.packetId_ = i; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + } + for (uint32_t i = 1; i <= 2; i++) { + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg != nullptr); + EXPECT_EQ(isNeedContinue, true); + EXPECT_EQ(isNeedHandle, true); + EXPECT_EQ(msg->GetSequenceId(), i); + msgSchedule.ScheduleInfoHandle(isNeedHandle, false, msg); + delete msg; + } + /** + * @tc.steps: step2. put msg seq1_packet1, seq2_packet2 again and seq3_packet3 + * @tc.expected: get msg ok and get seq1_packet1, seq2_packet2 and seq3_packet3 + */ + for (uint32_t i = 1; i <= 3; i++) { + info.sequenceId_ = i; + info.packetId_ = i; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + } + isNeedHandle = true; + isNeedContinue = true; + for (uint32_t i = 1; i <= 3; i++) { + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg != nullptr); + EXPECT_EQ(isNeedContinue, true); + EXPECT_EQ(isNeedHandle, (i == 3) ? true : false); + EXPECT_EQ(msg->GetSequenceId(), i); + const DataRequestPacket *packet = msg->GetObject(); + EXPECT_EQ(packet->GetPacketId(), i); + msgSchedule.ScheduleInfoHandle(isNeedHandle, false, msg); + delete msg; + } + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg == nullptr); + RefObject::KillAndDecObjRef(context); + context = nullptr; +} + +/** + * @tc.name: MsgSchedule007 + * @tc.desc: Test MsgSchedule function with same sessionId and duplicate sequenceId and low packetId + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMsgScheduleTest, MsgSchedule007, TestSize.Level0) +{ + /** + * @tc.steps: step1. put msg seq1_packet4, seq2_packet5 + * @tc.expected: put msg ok and get msg seq1_packet4, seq2_packet5 + */ + SingleVerDataMessageSchedule msgSchedule; + auto *context = new SingleVerKvSyncTaskContext(); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_CURRENT); + DataSyncMessageInfo info; + info.sessionId_ = 10; + bool isNeedHandle = true; + bool isNeedContinue = true; + for (uint32_t i = 1; i <= 2; i++) { + info.sequenceId_ = i; + info.packetId_ = i + 3; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + } + for (uint32_t i = 1; i <= 2; i++) { + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg != nullptr); + EXPECT_EQ(isNeedContinue, true); + EXPECT_EQ(isNeedHandle, true); + EXPECT_EQ(msg->GetSequenceId(), i); + const DataRequestPacket *packet = msg->GetObject(); + EXPECT_EQ(packet->GetPacketId(), i + 3); + msgSchedule.ScheduleInfoHandle(isNeedHandle, false, msg); + delete msg; + } + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + ASSERT_TRUE(msg == nullptr); + /** + * @tc.steps: step2. put msg seq1_packet1, seq2_packet2 + * @tc.expected: get nullptr + */ + for (uint32_t i = 1; i <= 2; i++) { + info.sequenceId_ = i; + info.packetId_ = i; + DistributedDB::Message *message = nullptr; + DistributedDBToolsUnitTest::BuildMessage(info, message); + msgSchedule.PutMsg(message); + } + isNeedHandle = true; + isNeedContinue = true; + for (uint32_t i = 1; i <= 3; i++) { + Message *msg = msgSchedule.MoveNextMsg(context, isNeedHandle, isNeedContinue); + EXPECT_EQ(isNeedContinue, true); + ASSERT_TRUE(msg == nullptr); + } + RefObject::KillAndDecObjRef(context); + context = nullptr; +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_multi_user_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_multi_user_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa3614c4b1f1d0f002cd7b97a604dd44f24acac4 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_multi_user_test.cpp @@ -0,0 +1,775 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "kv_store_nb_delegate.h" +#include "kv_virtual_device.h" +#include "platform_specific.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const string STORE_ID = "kv_stroe_sync_test"; + const string USER_ID_1 = "userId1"; + const string USER_ID_2 = "userId2"; + const std::string DEVICE_B = "deviceB"; + const std::string DEVICE_C = "deviceC"; + const int WAIT_TIME = 1000; // 1000ms + const int WAIT_3_SECONDS = 3000; + + KvStoreDelegateManager g_mgr1(APP_ID, USER_ID_1); + KvStoreDelegateManager g_mgr2(APP_ID, USER_ID_2); + KvStoreConfig g_config; + DistributedDBToolsUnitTest g_tool; + KvStoreNbDelegate* g_kvDelegatePtr1 = nullptr; + KvStoreNbDelegate* g_kvDelegatePtr2 = nullptr; + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + KvVirtualDevice *g_deviceB = nullptr; + KvVirtualDevice *g_deviceC = nullptr; + DBStatus g_kvDelegateStatus1 = INVALID_ARGS; + DBStatus g_kvDelegateStatus2 = INVALID_ARGS; + std::string g_identifier; + + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback1 = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus1), std::ref(g_kvDelegatePtr1)); + auto g_kvDelegateCallback2 = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus2), std::ref(g_kvDelegatePtr2)); + auto g_syncActivationCheckCallback1 = [] (const std::string &userId, const std::string &appId, + const std::string &storeId)-> bool { + if (userId == USER_ID_2) { + return true; + } else { + return false; + } + return true; + }; + auto g_syncActivationCheckCallback2 = [] (const std::string &userId, const std::string &appId, + const std::string &storeId)-> bool { + if (userId == USER_ID_1) { + return true; + } else { + return false; + } + return true; + }; +} + +class DistributedDBSingleVerMultiUserTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSingleVerMultiUserTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Init datadir and Virtual Communicator. + */ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr1.SetKvStoreConfig(g_config); + g_mgr2.SetKvStoreConfig(g_config); + + string dir = g_testDir + "/single_ver"; + DIR* dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); +} + +void DistributedDBSingleVerMultiUserTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Release virtual Communicator and clear data dir. + */ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); +} + +void DistributedDBSingleVerMultiUserTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: create virtual device B + */ + g_deviceB = new (std::nothrow) KvVirtualDevice(DEVICE_B); + ASSERT_TRUE(g_deviceB != nullptr); + VirtualSingleVerSyncDBInterface *syncInterfaceB = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(syncInterfaceB != nullptr); + ASSERT_EQ(g_deviceB->Initialize(g_communicatorAggregator, syncInterfaceB), E_OK); + g_deviceC = new (std::nothrow) KvVirtualDevice(DEVICE_C); + ASSERT_TRUE(g_deviceC != nullptr); + VirtualSingleVerSyncDBInterface *syncInterfaceC = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(syncInterfaceC != nullptr); + ASSERT_EQ(g_deviceC->Initialize(g_communicatorAggregator, syncInterfaceC), E_OK); +} + +void DistributedDBSingleVerMultiUserTest::TearDown(void) +{ + /** + * @tc.teardown: Release device A, B, C + */ + if (g_deviceB != nullptr) { + delete g_deviceB; + g_deviceB = nullptr; + } + if (g_deviceC != nullptr) { + delete g_deviceC; + g_deviceC = nullptr; + } + SyncActivationCheckCallback callback = nullptr; + g_mgr1.SetSyncActivationCheckCallback(callback); +} + +namespace { +void OpenStore1(bool syncDualTupleMode = true) +{ + KvStoreNbDelegate::Option option; + option.syncDualTupleMode = syncDualTupleMode; + g_mgr1.GetKvStore(STORE_ID, option, g_kvDelegateCallback1); + ASSERT_TRUE(g_kvDelegateStatus1 == OK); + ASSERT_TRUE(g_kvDelegatePtr1 != nullptr); +} + +void OpenStore2(bool syncDualTupleMode = true) +{ + KvStoreNbDelegate::Option option; + option.syncDualTupleMode = syncDualTupleMode; + g_mgr2.GetKvStore(STORE_ID, option, g_kvDelegateCallback2); + ASSERT_TRUE(g_kvDelegateStatus2 == OK); + ASSERT_TRUE(g_kvDelegatePtr2 != nullptr); +} + +void CloseStore() +{ + if (g_kvDelegatePtr1 != nullptr) { + ASSERT_EQ(g_mgr1.CloseKvStore(g_kvDelegatePtr1), OK); + g_kvDelegatePtr1 = nullptr; + DBStatus status = g_mgr1.DeleteKvStore(STORE_ID); + LOGD("delete kv store status %d", status); + ASSERT_TRUE(status == OK); + } + if (g_kvDelegatePtr2 != nullptr) { + ASSERT_EQ(g_mgr2.CloseKvStore(g_kvDelegatePtr2), OK); + g_kvDelegatePtr2 = nullptr; + DBStatus status = g_mgr2.DeleteKvStore(STORE_ID); + LOGD("delete kv store status %d", status); + ASSERT_TRUE(status == OK); + } +} + +void CheckSyncTest(DBStatus status1, DBStatus status2, std::vector &devices) +{ + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr1, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == status1); + if (status == OK) { + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + } + result.clear(); + status = g_tool.SyncTest(g_kvDelegatePtr2, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == status2); + if (status == OK) { + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + } +} + +bool AutoLaunchCallBack(const std::string &identifier, AutoLaunchParam ¶m, KvStoreObserverUnitTest *observer, + bool ret) +{ + LOGD("int AutoLaunchCallBack"); + EXPECT_TRUE(identifier == g_identifier); + param.appId = APP_ID; + param.storeId = STORE_ID; + CipherPassword passwd; + param.option = {true, false, CipherType::DEFAULT, passwd, "", false, g_testDir, observer, + 0, nullptr}; + param.notifier = nullptr; + param.option.syncDualTupleMode = true; + return ret; +} + +void TestSyncWithUserChange(bool wait) +{ + /** + * @tc.steps: step1. set SyncActivationCheckCallback and only userId1 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback2); + /** + * @tc.steps: step2. openstore1 in dual tuple sync mode and openstore2 in normal sync mode + * @tc.expected: step2. only user2 sync mode is active + */ + OpenStore1(true); + OpenStore2(true); + /** + * @tc.steps: step3. set SyncActivationCheckCallback and only userId2 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback1); + + /** + * @tc.steps: step4. call NotifyUserChanged and block sync db concurrently + * @tc.expected: step4. return OK + */ + CipherPassword passwd; + bool startSync = false; + std::condition_variable cv; + thread subThread([&]() { + std::mutex notifyLock; + std::unique_lock lck(notifyLock); + cv.wait(lck, [&startSync]() { return startSync; }); + EXPECT_TRUE(KvStoreDelegateManager::NotifyUserChanged() == OK); + }); + subThread.detach(); + g_communicatorAggregator->RegOnDispatch([&](const std::string&, Message *inMsg) { + if (!startSync) { + startSync = true; + cv.notify_all(); + } + }); + + /** + * @tc.steps: step5. deviceA call sync and wait + * @tc.expected: step5. sync should return OK. + */ + std::map result; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr1, devices, SYNC_MODE_PUSH_ONLY, result, wait); + EXPECT_EQ(status, OK); + g_communicatorAggregator->RegOnDispatch(nullptr); + /** + * @tc.expected: step6. onComplete should be called, and status is USER_CHANGED + */ + EXPECT_EQ(result.size(), devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_EQ(pair.second, USER_CHANGED); + } + CloseStore(); +} +} + +/** + * @tc.name: multi user 001 + * @tc.desc: Test multi user change + * @tc.type: FUNC + * @tc.require: AR000CQS3S SR000CQE0B + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser001, TestSize.Level0) +{ + /** + * @tc.steps: step1. set SyncActivationCheckCallback and only userId2 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback1); + /** + * @tc.steps: step2. openstore1 and openstore2 + * @tc.expected: step2. only user2 sync mode is active + */ + OpenStore1(); + OpenStore2(); + /** + * @tc.steps: step3. g_kvDelegatePtr1 and g_kvDelegatePtr2 put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + Value value2 = {'2'}; + EXPECT_TRUE(g_kvDelegatePtr1->Put(key, value2) == OK); + EXPECT_TRUE(g_kvDelegatePtr2->Put(key, value) == OK); + /** + * @tc.steps: step4. g_kvDelegatePtr1 and g_kvDelegatePtr2 call sync + * @tc.expected: step4. g_kvDelegatePtr2 call success + */ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + CheckSyncTest(NOT_ACTIVE, OK, devices); + /** + * @tc.steps: step5. g_kvDelegatePtr1 support some pragma cmd call + * @tc.expected: step5. Pragma call success + */ + int pragmaData = 1; + PragmaData input = static_cast(&pragmaData); + EXPECT_TRUE(g_kvDelegatePtr1->Pragma(AUTO_SYNC, input) == OK); + pragmaData = 100; + input = static_cast(&pragmaData); + EXPECT_TRUE(g_kvDelegatePtr1->Pragma(SET_QUEUED_SYNC_LIMIT, input) == OK); + EXPECT_TRUE(g_kvDelegatePtr1->Pragma(GET_QUEUED_SYNC_LIMIT, input) == OK); + EXPECT_TRUE(input == static_cast(&pragmaData)); + pragmaData = 1; + input = static_cast(&pragmaData); + EXPECT_TRUE(g_kvDelegatePtr1->Pragma(SET_WIPE_POLICY, input) == OK); + EXPECT_TRUE(g_kvDelegatePtr1->Pragma(SET_SYNC_RETRY, input) == OK); + /** + * @tc.expected: step6. onComplete should be called, DeviceB have {k1,v1} + */ + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); + /** + * @tc.expected: step7. user change + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback2); + KvStoreDelegateManager::NotifyUserChanged(); + /** + * @tc.steps: step8. g_kvDelegatePtr1 and g_kvDelegatePtr2 call sync + * @tc.expected: step8. g_kvDelegatePtr1 call success + */ + devices.clear(); + devices.push_back(g_deviceC->GetDeviceId()); + CheckSyncTest(OK, NOT_ACTIVE, devices); + /** + * @tc.expected: step9. onComplete should be called, DeviceC have {k1,v1} + */ + g_deviceC->GetData(key, item); + EXPECT_TRUE(item.value == value2); + CloseStore(); +} + +/** + * @tc.name: multi user 002 + * @tc.desc: Test multi user not change + * @tc.type: FUNC + * @tc.require: AR000CQS3S SR000CQE0B + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser002, TestSize.Level0) +{ + /** + * @tc.steps: step1. set SyncActivationCheckCallback and only userId2 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback1); + /** + * @tc.steps: step2. openstore1 and openstore2 + * @tc.expected: step2. only user2 sync mode is active + */ + OpenStore1(); + OpenStore2(); + /** + * @tc.steps: step3. g_kvDelegatePtr1 and g_kvDelegatePtr2 put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + EXPECT_TRUE(g_kvDelegatePtr1->Put(key, value) == OK); + EXPECT_TRUE(g_kvDelegatePtr2->Put(key, value) == OK); + /** + * @tc.steps: step4. GetKvStoreIdentifier success when userId is invalid + */ + std::string userId; + EXPECT_TRUE(g_mgr1.GetKvStoreIdentifier(userId, APP_ID, USER_ID_2, true) != ""); + userId.resize(130); + EXPECT_TRUE(g_mgr1.GetKvStoreIdentifier(userId, APP_ID, USER_ID_2, true) != ""); + /** + * @tc.steps: step5. g_kvDelegatePtr1 and g_kvDelegatePtr2 call sync + * @tc.expected: step5. g_kvDelegatePtr2 call success + */ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + CheckSyncTest(NOT_ACTIVE, OK, devices); + /** + * @tc.expected: step6. onComplete should be called, DeviceB have {k1,v1} + */ + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); + /** + * @tc.expected: step7. user change + */ + KvStoreDelegateManager::NotifyUserChanged(); + /** + * @tc.steps: step8. g_kvDelegatePtr1 and g_kvDelegatePtr2 put {k2, v2} + */ + key = {'2'}; + value = {'2'}; + EXPECT_TRUE(g_kvDelegatePtr1->Put(key, value) == OK); + EXPECT_TRUE(g_kvDelegatePtr2->Put(key, value) == OK); + /** + * @tc.steps: step9. g_kvDelegatePtr1 and g_kvDelegatePtr2 call sync + * @tc.expected: step9. g_kvDelegatePtr2 call success + */ + devices.clear(); + devices.push_back(g_deviceB->GetDeviceId()); + CheckSyncTest(NOT_ACTIVE, OK, devices); + /** + * @tc.expected: step10. onComplete should be called, DeviceB have {k2,v2} + */ + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); + CloseStore(); +} + +/** + * @tc.name: multi user 003 + * @tc.desc: enhancement callback return true in multiuser mode + * @tc.type: FUNC + * @tc.require: AR000EPARJ + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser003, TestSize.Level3) +{ + /** + * @tc.steps: step1. set SyncActivationCheckCallback and only userId2 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback1); + + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + EXPECT_TRUE(observer != nullptr); + /** + * @tc.steps: step2. SetAutoLaunchRequestCallback + * @tc.expected: step2. success. + */ + g_mgr1.SetAutoLaunchRequestCallback( + std::bind(AutoLaunchCallBack, std::placeholders::_1, std::placeholders::_2, observer, true)); + + /** + * @tc.steps: step2. RunCommunicatorLackCallback + * @tc.expected: step2. success. + */ + g_identifier = g_mgr1.GetKvStoreIdentifier(USER_ID_2, APP_ID, STORE_ID, true); + EXPECT_TRUE(g_identifier == g_mgr1.GetKvStoreIdentifier(USER_ID_1, APP_ID, STORE_ID, true)); + std::vector label(g_identifier.begin(), g_identifier.end()); + g_communicatorAggregator->SetCurrentUserId(USER_ID_2); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step3. device B put {k1, v1} + * @tc.expected: step3. success. + */ + Key key = {'1'}; + Value value = {'1'}; + Timestamp currentTime; + (void)OS::GetCurrentSysTimeInMicrosecond(currentTime); + EXPECT_TRUE(g_deviceB->PutData(key, value, currentTime, 0) == OK); + /** + * @tc.steps: step4. device B push sync to A + * @tc.expected: step4. success. + */ + EXPECT_TRUE(g_deviceB->Sync(SYNC_MODE_PUSH_ONLY, true) == OK); + EXPECT_TRUE(observer->GetCallCount() == 1); // only A + /** + * @tc.steps: step5. deviceA have {k1,v1} + * @tc.expected: step5. success. + */ + OpenStore2(); + Value actualValue; + g_kvDelegatePtr2->Get(key, actualValue); + EXPECT_EQ(actualValue, value); + std::this_thread::sleep_for(std::chrono::seconds(70)); + g_mgr1.SetAutoLaunchRequestCallback(nullptr); + CloseStore(); + delete observer; +} + +/** + * @tc.name: MultiUser004 + * @tc.desc: CommunicatorLackCallback in multi user mode + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser004, TestSize.Level0) +{ + /** + * @tc.steps: step1. set SyncActivationCheckCallback and only userId2 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback1); + + /** + * @tc.steps: step2. right param A B enable + * @tc.expected: step2. success. + */ + AutoLaunchNotifier notifier = nullptr; + KvStoreObserverUnitTest *observer = new (std::nothrow) KvStoreObserverUnitTest; + EXPECT_TRUE(observer != nullptr); + AutoLaunchOption option; + CipherPassword passwd; + option = {true, false, CipherType::DEFAULT, passwd, "", false, g_testDir, observer, + 0, nullptr}; + option.notifier = nullptr; + option.observer = observer; + option.syncDualTupleMode = true; + EXPECT_TRUE(g_mgr1.EnableKvStoreAutoLaunch(USER_ID_2, APP_ID, STORE_ID, option, notifier) == OK); + EXPECT_TRUE(g_mgr1.EnableKvStoreAutoLaunch(USER_ID_1, APP_ID, STORE_ID, option, notifier) == OK); + + /** + * @tc.steps: step3. RunCommunicatorLackCallback + * @tc.expected: step3. userId2 open db successfully. + */ + g_identifier = g_mgr1.GetKvStoreIdentifier(USER_ID_2, APP_ID, STORE_ID, true); + std::vector label(g_identifier.begin(), g_identifier.end()); + g_communicatorAggregator->SetCurrentUserId(USER_ID_2); + g_communicatorAggregator->RunCommunicatorLackCallback(label); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step5. device B put {k1, v1} + * @tc.expected: step5. success. + */ + Key key = {'1'}; + Value value = {'1'}; + Timestamp currentTime; + (void)OS::GetCurrentSysTimeInMicrosecond(currentTime); + EXPECT_TRUE(g_deviceB->PutData(key, value, currentTime, 0) == OK); + /** + * @tc.steps: step6. device B push sync to A + * @tc.expected: step6. success. + */ + EXPECT_TRUE(g_deviceB->Sync(SYNC_MODE_PUSH_ONLY, true) == OK); + EXPECT_TRUE(observer->GetCallCount() == 1); // only A + /** + * @tc.steps: step7. deviceA have {k1,v1} + * @tc.expected: step7. success. + */ + OpenStore2(); + Value actualValue; + g_kvDelegatePtr2->Get(key, actualValue); + EXPECT_EQ(actualValue, value); + /** + * @tc.steps: step8. param A B disable + * @tc.expected: step8. notifier WRITE_CLOSED + */ + EXPECT_TRUE(g_mgr1.DisableKvStoreAutoLaunch(USER_ID_2, APP_ID, STORE_ID) == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + EXPECT_TRUE(g_mgr1.DisableKvStoreAutoLaunch(USER_ID_1, APP_ID, STORE_ID) == OK); + CloseStore(); + delete observer; +} + +/** + * @tc.name: MultiUser005 + * @tc.desc: test NotifyUserChanged func when all db in normal sync mode + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser005, TestSize.Level0) +{ + /** + * @tc.steps: step1. openstore1 and openstore2 in normal sync mode + * @tc.expected: step1. only user2 sync mode is active + */ + OpenStore1(false); + OpenStore2(false); + /** + * @tc.steps: step2. call NotifyUserChanged + * @tc.expected: step2. return OK + */ + EXPECT_TRUE(KvStoreDelegateManager::NotifyUserChanged() == OK); + CloseStore(); + /** + * @tc.steps: step3. openstore1 open normal sync mode and and openstore2 in dual tuple + * @tc.expected: step3. only user2 sync mode is active + */ + OpenStore1(false); + OpenStore2(); + /** + * @tc.steps: step4. call NotifyUserChanged + * @tc.expected: step4. return OK + */ + EXPECT_TRUE(KvStoreDelegateManager::NotifyUserChanged() == OK); + CloseStore(); +} + +/** + * @tc.name: MultiUser006 + * @tc.desc: test NotifyUserChanged and close db concurrently + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser006, TestSize.Level0) +{ + /** + * @tc.steps: step1. set SyncActivationCheckCallback and only userId1 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback2); + /** + * @tc.steps: step2. openstore1 in dual tuple sync mode and openstore2 in normal sync mode + * @tc.expected: step2. only user2 sync mode is active + */ + OpenStore1(true); + OpenStore2(false); + /** + * @tc.steps: step3. set SyncActivationCheckCallback and only userId2 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback1); + /** + * @tc.steps: step4. call NotifyUserChanged and close db concurrently + * @tc.expected: step4. return OK + */ + thread subThread([&]() { + EXPECT_TRUE(KvStoreDelegateManager::NotifyUserChanged() == OK); + }); + subThread.detach(); + EXPECT_EQ(g_mgr1.CloseKvStore(g_kvDelegatePtr1), OK); + g_kvDelegatePtr1 = nullptr; + CloseStore(); +} + +/** + * @tc.name: MultiUser007 + * @tc.desc: test NotifyUserChanged and rekey db concurrently + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser007, TestSize.Level0) +{ + /** + * @tc.steps: step1. set SyncActivationCheckCallback and only userId1 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback2); + /** + * @tc.steps: step2. openstore1 in dual tuple sync mode and openstore2 in normal sync mode + * @tc.expected: step2. only user2 sync mode is active + */ + OpenStore1(true); + OpenStore2(false); + /** + * @tc.steps: step3. set SyncActivationCheckCallback and only userId2 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback1); + /** + * @tc.steps: step2. call NotifyUserChanged and close db concurrently + * @tc.expected: step2. return OK + */ + CipherPassword passwd; + thread subThread([&]() { + EXPECT_TRUE(KvStoreDelegateManager::NotifyUserChanged() == OK); + }); + subThread.detach(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + EXPECT_TRUE(g_kvDelegatePtr1->Rekey(passwd) == OK); + CloseStore(); +} + +/** + * @tc.name: MultiUser008 + * @tc.desc: test NotifyUserChanged and block sync concurrently + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser008, TestSize.Level0) +{ + TestSyncWithUserChange(true); +} + +/** + * @tc.name: MultiUser009 + * @tc.desc: test NotifyUserChanged and non-block sync concurrently + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser009, TestSize.Level0) +{ + TestSyncWithUserChange(false); +} + +/** + * @tc.name: MultiUser010 + * @tc.desc: test NotifyUserChanged and non-block sync with multi devices concurrently + * @tc.type: FUNC + * @tc.require: AR000E8S2T + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerMultiUserTest, MultiUser010, TestSize.Level3) +{ + /** + * @tc.steps: step1. set SyncActivationCheckCallback and only userId1 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback2); + /** + * @tc.steps: step2. openstore1 and openstore2 in dual tuple sync mode + * @tc.expected: step2. only userId1 sync mode is active + */ + OpenStore1(true); + OpenStore2(true); + /** + * @tc.steps: step3. set SyncActivationCheckCallback and only userId2 can active + */ + g_mgr1.SetSyncActivationCheckCallback(g_syncActivationCheckCallback1); + /** + * @tc.steps: step4. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + EXPECT_TRUE(g_kvDelegatePtr1->Put(key, value) == OK); + + /** + * @tc.steps: step5. deviceB set sava data dely 5s + */ + g_deviceC->SetSaveDataDelayTime(WAIT_3_SECONDS); + /** + * @tc.steps: step6. call NotifyUserChanged and block sync db concurrently + * @tc.expected: step6. return OK + */ + CipherPassword passwd; + thread subThread([&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + EXPECT_TRUE(KvStoreDelegateManager::NotifyUserChanged() == OK); + }); + subThread.detach(); + /** + * @tc.steps: step7. deviceA call sync and wait + * @tc.expected: step7. sync should return OK. + */ + std::map result; + std::vector devices = {g_deviceB->GetDeviceId(), g_deviceC->GetDeviceId()}; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr1, devices, SYNC_MODE_PUSH_ONLY, result, false); + EXPECT_TRUE(status == OK); + + /** + * @tc.expected: step8. onComplete should be called, and status is USER_CHANGED + */ + EXPECT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + if (pair.first == g_deviceB->GetDeviceId()) { + EXPECT_TRUE(pair.second == OK); + } else { + EXPECT_TRUE(pair.second == USER_CHANGED); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_3_SECONDS)); + CloseStore(); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_query_sync_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_query_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9939f468dc4bb9142cf844041a4e6caf19853ad7 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_query_sync_test.cpp @@ -0,0 +1,1641 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_common.h" +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "generic_single_ver_kv_entry.h" +#include "kv_store_nb_delegate.h" +#include "kv_virtual_device.h" +#include "platform_specific.h" +#include "query.h" +#include "query_sync_object.h" +#include "single_ver_data_sync.h" +#include "single_ver_serialize_manager.h" +#include "sync_types.h" +#include "virtual_communicator.h" +#include "virtual_communicator_aggregator.h" +#include "virtual_single_ver_sync_db_Interface.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const string STORE_ID = "kv_store_sync_test"; + const string SCHEMA_STORE_ID = "kv_store_sync_schema_test"; + const std::string DEVICE_B = "deviceB"; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreDelegateManager g_schemaMgr(SCHEMA_APP_ID, USER_ID); + KvStoreConfig g_config; + DistributedDBToolsUnitTest g_tool; + DBStatus g_kvDelegateStatus = INVALID_ARGS; + DBStatus g_schemaKvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate* g_kvDelegatePtr = nullptr; + KvStoreNbDelegate* g_schemaKvDelegatePtr = nullptr; + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + KvVirtualDevice *g_deviceB = nullptr; + + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); + auto g_schemaKvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_schemaKvDelegateStatus), std::ref(g_schemaKvDelegatePtr)); + const string SCHEMA_STRING = + "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"BOOL\"," + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, NOT NULL, DEFAULT '3.1415'\"," + "\"field_name7\":\"LONG, DEFAULT 100\"," + "\"field_name8\":\"LONG, DEFAULT 100\"," + "\"field_name9\":\"LONG, DEFAULT 100\"," + "\"field_name10\":\"LONG, DEFAULT 100\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2\"]}"; + + const std::string SCHEMA_VALUE1 = + "{\"field_name1\":true," + "\"field_name2\":false," + "\"field_name3\":10," + "\"field_name4\":20," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415\"," + "\"field_name7\":100," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; + + const std::string SCHEMA_VALUE2 = + "{\"field_name1\":false," + "\"field_name2\":true," + "\"field_name3\":100," + "\"field_name4\":200," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415\"," + "\"field_name7\":100," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; +} + +class DistributedDBSingleVerP2PQuerySyncTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSingleVerP2PQuerySyncTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Init datadir and Virtual Communicator. + */ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + string dir = g_testDir + "/single_ver"; + DIR* dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); +} + +void DistributedDBSingleVerP2PQuerySyncTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Release virtual Communicator and clear data dir. + */ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); +} + +void DistributedDBSingleVerP2PQuerySyncTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: create virtual device B and get a KvStoreNbDelegate as deviceA + */ + g_deviceB = new (std::nothrow) KvVirtualDevice(DEVICE_B); + ASSERT_TRUE(g_deviceB != nullptr); + VirtualSingleVerSyncDBInterface *syncInterfaceB = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(syncInterfaceB != nullptr); + ASSERT_EQ(g_deviceB->Initialize(g_communicatorAggregator, syncInterfaceB), E_OK); +} + +void DistributedDBSingleVerP2PQuerySyncTest::TearDown(void) +{ + /** + * @tc.teardown: Release device A, B + */ + if (g_kvDelegatePtr != nullptr) { + ASSERT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + DBStatus status = g_mgr.DeleteKvStore(STORE_ID); + LOGD("delete kv store status %d", status); + ASSERT_TRUE(status == OK); + } + if (g_schemaKvDelegatePtr != nullptr) { + ASSERT_EQ(g_schemaMgr.CloseKvStore(g_schemaKvDelegatePtr), OK); + g_schemaKvDelegatePtr = nullptr; + DBStatus status = g_schemaMgr.DeleteKvStore(SCHEMA_STORE_ID); + LOGD("delete kv store status %d", status); + ASSERT_TRUE(status == OK); + } + if (g_deviceB != nullptr) { + delete g_deviceB; + g_deviceB = nullptr; + } + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +void InitNormalDb() +{ + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + KvStoreNbDelegate::Option option; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); +} + +void InitSchemaDb() +{ + g_config.dataDir = g_testDir; + g_schemaMgr.SetKvStoreConfig(g_config); + KvStoreNbDelegate::Option option; + option.schema = SCHEMA_STRING; + g_schemaMgr.GetKvStore(SCHEMA_STORE_ID, option, g_schemaKvDelegateCallback); + ASSERT_TRUE(g_schemaKvDelegateStatus == OK); + ASSERT_TRUE(g_schemaKvDelegatePtr != nullptr); +} + +/** + * @tc.name: Normal Sync 001 + * @tc.desc: Test normal push sync for keyprefix data. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, NormalSync001, TestSize.Level1) +{ + InitNormalDb(); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k0, v0} - {k9, v9} + */ + Key key = {'1'}; + Value value = {'1'}; + const int dataSize = 10; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + value.push_back(i); + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + key.pop_back(); + value.pop_back(); + } + Key key2 = {'2'}; + Value value2 = {'2'}; + status = g_kvDelegatePtr->Put(key2, value2); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceA call query sync and wait + * @tc.expected: step2. sync should return OK. + */ + Query query = Query::Select().PrefixKey(key); + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, query); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceB have {k1,v1} - {k9, v9} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + value.push_back(i); + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); + key.pop_back(); + value.pop_back(); + } + EXPECT_TRUE(g_deviceB->GetData(key2, item) != E_OK); +} + +/** + * @tc.name: Normal Sync 002 + * @tc.desc: Test normal push sync for limit and offset. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, NormalSync002, TestSize.Level1) +{ + InitNormalDb(); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k0, v0} - {k9, v9} + */ + Key key = {'1'}; + Value value = {'1'}; + const int dataSize = 10; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + value.push_back(i); + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + key.pop_back(); + value.pop_back(); + } + + /** + * @tc.steps: step2. deviceA call sync and wait + * @tc.expected: step2. sync should return OK. + */ + const int limit = 5; + const int offset = 4; + Query query = Query::Select().PrefixKey(key).Limit(limit, offset); + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, query); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceB have {k4,v4} {k8, v8} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + + VirtualDataItem item; + for (int i = limit - 1; i < limit + offset; i++) { + key.push_back(i); + value.push_back(i); + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); + key.pop_back(); + value.pop_back(); + } +} + +/** + * @tc.name: Normal Sync 001 + * @tc.desc: Test normal push_and_pull sync for keyprefix data. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, NormalSync003, TestSize.Level1) +{ + InitNormalDb(); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k, v}, {b, v} + */ + Key key = {'1'}; + Value value = {'1'}; + const int dataSize = 10; + status = g_kvDelegatePtr->Put(key, value); + Key key2 = {'2'}; + Value value2 = {'2'}; + status = g_kvDelegatePtr->Put(key2, value2); + + /** + * @tc.steps: step2. deviceB put {b0, v0} - {b9, v9}, {c, v} + */ + for (int i = 0; i < dataSize; i++) { + key2.push_back(i); + value2.push_back(i); + g_deviceB->PutData(key2, value2, 10 + i, 0); + key2.pop_back(); + value2.pop_back(); + } + Key key3 = {'3'}; + Value value3 = {'3'}; + g_deviceB->PutData(key3, value3, 20, 0); + + /** + * @tc.steps: step2. deviceA call query sync and wait + * @tc.expected: step2. sync should return OK. + */ + Query query = Query::Select().PrefixKey(key2); + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result, query); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceA have {b0, v0} - {b9, v9}, DeviceB have {b, v} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + Value tmpValue; + for (int i = 0; i < dataSize; i++) { + key2.push_back(i); + value2.push_back(i); + g_kvDelegatePtr->Get(key2, tmpValue); + EXPECT_TRUE(tmpValue == value2); + key2.pop_back(); + value2.pop_back(); + } + EXPECT_TRUE(g_deviceB->GetData(key, item) != E_OK); + EXPECT_TRUE(g_deviceB->GetData(key2, item) == E_OK); + g_kvDelegatePtr->Get(key3, tmpValue); + EXPECT_TRUE(tmpValue != value3); +} + +/** + * @tc.name: Normal Sync 001 + * @tc.desc: Test normal pull sync for keyprefix data. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, NormalSync004, TestSize.Level1) +{ + InitNormalDb(); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + /** + * @tc.steps: step1. deviceB put {k1, v1} - {k9, k9}, {b0, v0} - {b9, v9} + */ + Key key = {'1'}; + Value value = {'1'}; + const int dataSize = 10; + Key key2 = {'2'}; + Value value2 = {'2'}; + vector> key1Vec; + vector> key2Vec; + for (int i = 0; i < dataSize; i++) { + Key tmpKey(key); + Value tmpValue(value); + tmpKey.push_back(i); + tmpValue.push_back(i); + key1Vec.push_back(pair {tmpKey, tmpValue}); + } + for (int i = 0; i < dataSize; i++) { + Key tmpKey(key2); + Value tmpValue(value2); + tmpKey.push_back(i); + tmpValue.push_back(i); + key2Vec.push_back(pair {tmpKey, tmpValue}); + } + for (int i = 0; i < dataSize; i++) { + g_deviceB->PutData(key2Vec[i].first, key2Vec[i].second, 20 + i, 0); + g_deviceB->PutData(key1Vec[i].first, key1Vec[i].second, 10 + i, 0); + } + + /** + * @tc.steps: step2. deviceA call query sync and wait + * @tc.expected: step2. sync should return OK. + */ + Query query = Query::Select().PrefixKey(key2); + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result, query); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceA have {b0, v0} - {b9, v9} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + Value tmpValue; + for (int i = 0; i < dataSize; i++) { + g_kvDelegatePtr->Get(key2Vec[i].first, tmpValue); + EXPECT_TRUE(tmpValue == key2Vec[i].second); + g_kvDelegatePtr->Get(key1Vec[i].first, tmpValue); + EXPECT_TRUE(tmpValue != key1Vec[i].second); + } +} + +/** + * @tc.name: NormalSync005 + * @tc.desc: Test normal push sync for inkeys query. + * @tc.type: FUNC + * @tc.require: AR000GOHO7 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, NormalSync005, TestSize.Level1) +{ + InitNormalDb(); + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put K1-K5 + */ + ASSERT_EQ(g_kvDelegatePtr->PutBatch( + {{KEY_1, VALUE_1}, {KEY_2, VALUE_2}, {KEY_3, VALUE_3}, {KEY_4, VALUE_4}, {KEY_5, VALUE_5}}), OK); + + /** + * @tc.steps: step2. deviceA sync K2,K4 and wait + * @tc.expected: step2. sync should return OK. + */ + Query query = Query::Select().InKeys({KEY_2, KEY_4}); + std::map result; + ASSERT_EQ(g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, query), OK); + + /** + * @tc.expected: step3. onComplete should be called. + */ + ASSERT_EQ(result.size(), devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_EQ(pair.second, OK); + } + + /** + * @tc.steps: step4. deviceB have K2K4 and have no K1K3K5. + * @tc.expected: step4. sync should return OK. + */ + VirtualDataItem item; + EXPECT_EQ(g_deviceB->GetData(KEY_2, item), E_OK); + EXPECT_EQ(item.value, VALUE_2); + EXPECT_EQ(g_deviceB->GetData(KEY_4, item), E_OK); + EXPECT_EQ(item.value, VALUE_4); + EXPECT_EQ(g_deviceB->GetData(KEY_1, item), -E_NOT_FOUND); + EXPECT_EQ(g_deviceB->GetData(KEY_3, item), -E_NOT_FOUND); + EXPECT_EQ(g_deviceB->GetData(KEY_5, item), -E_NOT_FOUND); + + /** + * @tc.steps: step5. deviceA sync with invalid inkeys query + * @tc.expected: step5. sync failed and the rc is right. + */ + query = Query::Select().InKeys({}); + result.clear(); + ASSERT_EQ(g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, query), INVALID_ARGS); + + std::set keys; + for (uint8_t i = 0; i < DBConstant::MAX_BATCH_SIZE + 1; i++) { + Key key = { i }; + keys.emplace(key); + } + query = Query::Select().InKeys(keys); + result.clear(); + ASSERT_EQ(g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, query), OVER_MAX_LIMITS); + + query = Query::Select().InKeys({{}}); + result.clear(); + ASSERT_EQ(g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, query), INVALID_ARGS); +} + +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, QueryRequestPacketTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. prepare a QuerySyncRequestPacket. + */ + auto packet = new (std::nothrow) DataRequestPacket; + ASSERT_TRUE(packet != nullptr); + auto kvEntry = new (std::nothrow) GenericSingleVerKvEntry; + ASSERT_TRUE(kvEntry != nullptr); + kvEntry->SetTimestamp(1); + SyncEntry syncData {.entries = {kvEntry}}; +#ifndef OMIT_ZLIB + ASSERT_TRUE(GenericSingleVerKvEntry::Compress(syncData.entries, syncData.compressedEntries, + {CompressAlgorithm::ZLIB, SOFTWARE_VERSION_CURRENT}) == E_OK); + packet->SetCompressAlgo(CompressAlgorithm::ZLIB); + packet->SetFlag(4); // set IS_COMPRESS_DATA flag true +#endif + packet->SetBasicInfo(-E_NOT_SUPPORT, SOFTWARE_VERSION_CURRENT, SyncModeType::QUERY_PUSH_PULL); + packet->SetData(syncData.entries); + packet->SetCompressData(syncData.compressedEntries); + packet->SetEndWaterMark(INT8_MAX); + packet->SetWaterMark(INT16_MAX, INT32_MAX, INT64_MAX); + QuerySyncObject syncQuery(Query::Select().PrefixKey({'2'})); + packet->SetQuery(syncQuery); + packet->SetQueryId(syncQuery.GetIdentify()); + packet->SetReserved(std::vector {INT8_MAX}); + + /** + * @tc.steps: step2. put the QuerySyncRequestPacket into a message. + */ + Message msg; + msg.SetExternalObject(packet); + msg.SetMessageId(QUERY_SYNC_MESSAGE); + msg.SetMessageType(TYPE_REQUEST); + + /** + * @tc.steps: step3. Serialization the message to a buffer. + */ + int len = SingleVerSerializeManager::CalculateLen(&msg); + vector buffer(len); + ASSERT_EQ(SingleVerSerializeManager::Serialization(buffer.data(), buffer.size(), &msg), E_OK); + + /** + * @tc.steps: step4. DeSerialization the buffer to a message. + */ + Message outMsg(QUERY_SYNC_MESSAGE); + outMsg.SetMessageType(TYPE_REQUEST); + ASSERT_EQ(SingleVerSerializeManager::DeSerialization(buffer.data(), buffer.size(), &outMsg), E_OK); + + /** + * @tc.steps: step5. checkout the outMsg. + * @tc.expected: step5. outMsg equal the the in msg + */ + auto outPacket = outMsg.GetObject(); + EXPECT_EQ(outPacket->GetVersion(), SOFTWARE_VERSION_CURRENT); + EXPECT_EQ(outPacket->GetMode(), SyncModeType::QUERY_PUSH_PULL); + EXPECT_EQ(outPacket->GetEndWaterMark(), static_cast(INT8_MAX)); + EXPECT_EQ(outPacket->GetLocalWaterMark(), static_cast(INT16_MAX)); + EXPECT_EQ(outPacket->GetPeerWaterMark(), static_cast(INT32_MAX)); + EXPECT_EQ(outPacket->GetDeletedWaterMark(), static_cast(INT64_MAX)); +#ifndef OMIT_ZLIB + EXPECT_EQ(outPacket->GetFlag(), static_cast(4)); // check IS_COMPRESS_DATA flag true +#endif + EXPECT_EQ(outPacket->GetQueryId(), syncQuery.GetIdentify()); + EXPECT_EQ(outPacket->GetReserved(), std::vector {INT8_MAX}); + EXPECT_EQ(outPacket->GetSendCode(), -E_NOT_SUPPORT); + EXPECT_EQ(outPacket->GetData()[0]->GetTimestamp(), 1u); +} + +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, QueryAckPacketTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. prepare a QuerySyncAckPacket. + */ + DataAckPacket packet; + packet.SetVersion(SOFTWARE_VERSION_CURRENT); + packet.SetData(INT64_MAX); + packet.SetRecvCode(-E_NOT_SUPPORT); + std::vector reserved = {INT8_MAX}; + packet.SetReserved(reserved); + + /** + * @tc.steps: step2. put the QuerySyncAckPacket into a message. + */ + Message msg; + msg.SetCopiedObject(packet); + msg.SetMessageId(QUERY_SYNC_MESSAGE); + msg.SetMessageType(TYPE_RESPONSE); + + /** + * @tc.steps: step3. Serialization the message to a buffer. + */ + int len = SingleVerSerializeManager::CalculateLen(&msg); + LOGE("test leng = %d", len); + uint8_t *buffer = new (nothrow) uint8_t[len]; + ASSERT_TRUE(buffer != nullptr); + int errCode = SingleVerSerializeManager::Serialization(buffer, len, &msg); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step4. DeSerialization the buffer to a message. + */ + Message outMsg; + outMsg.SetMessageId(QUERY_SYNC_MESSAGE); + outMsg.SetMessageType(TYPE_RESPONSE); + errCode = SingleVerSerializeManager::DeSerialization(buffer, len, &outMsg); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step5. checkout the outMsg. + * @tc.expected: step5. outMsg equal the the in msg + */ + auto outPacket = outMsg.GetObject(); + EXPECT_EQ(outPacket->GetVersion(), SOFTWARE_VERSION_CURRENT); + EXPECT_EQ(outPacket->GetData(), static_cast(INT64_MAX)); + std::vector reserved2 = {INT8_MAX}; + EXPECT_EQ(outPacket->GetReserved(), reserved2); + EXPECT_EQ(outPacket->GetRecvCode(), -E_NOT_SUPPORT); + delete[] buffer; +} + +/** + * @tc.name: GetQueryWaterMark 001 + * @tc.desc: Test metaData save and get queryWaterMark. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, GetQueryWaterMark001, TestSize.Level1) +{ + VirtualSingleVerSyncDBInterface storage; + Metadata meta; + + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + int errCode = meta.Initialize(&storage); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. save receive and send watermark + * @tc.expected: step2. E_OK + */ + WaterMark w1 = 1; + EXPECT_EQ(meta.SetRecvQueryWaterMark("Q1", "D1", w1), E_OK); + EXPECT_EQ(meta.SetSendQueryWaterMark("Q1", "D1", w1), E_OK); + + /** + * @tc.steps: step3. get receive and send watermark + * @tc.expected: step3. E_OK and get the latest value + */ + WaterMark w = 0; + EXPECT_EQ(meta.GetRecvQueryWaterMark("Q1", "D1", w), E_OK); + EXPECT_EQ(w1, w); + EXPECT_EQ(meta.GetSendQueryWaterMark("Q1", "D1", w), E_OK); + EXPECT_EQ(w1, w); + + /** + * @tc.steps: step4. set peer and local watermark + * @tc.expected: step4. E_OK + */ + WaterMark w2 = 2; + EXPECT_EQ(meta.SaveLocalWaterMark("D1", w2), E_OK); + EXPECT_EQ(meta.SavePeerWaterMark("D1", w2, true), E_OK); + + /** + * @tc.steps: step5. get receive and send watermark + * @tc.expected: step5. E_OK and get the w1 + */ + EXPECT_EQ(meta.GetRecvQueryWaterMark("Q1", "D1", w), E_OK); + EXPECT_EQ(w2, w); + EXPECT_EQ(meta.GetSendQueryWaterMark("Q1", "D1", w), E_OK); + EXPECT_EQ(w2, w); + + /** + * @tc.steps: step6. set peer and local watermark + * @tc.expected: step6. E_OK + */ + WaterMark w3 = 3; + EXPECT_EQ(meta.SaveLocalWaterMark("D2", w3), E_OK); + EXPECT_EQ(meta.SavePeerWaterMark("D2", w3, true), E_OK); + + /** + * @tc.steps: step7. get receive and send watermark + * @tc.expected: step7. E_OK and get the w3 + */ + EXPECT_EQ(meta.GetRecvQueryWaterMark("Q2", "D2", w), E_OK); + EXPECT_EQ(w3, w); + EXPECT_EQ(meta.GetSendQueryWaterMark("Q2", "D2", w), E_OK); + EXPECT_EQ(w3, w); + + /** + * @tc.steps: step8. get not exit receive and send watermark + * @tc.expected: step8. E_OK and get the 0 + */ + EXPECT_EQ(meta.GetRecvQueryWaterMark("Q3", "D3", w), E_OK); + EXPECT_EQ(w, 0u); + EXPECT_EQ(meta.GetSendQueryWaterMark("Q3", "D3", w), E_OK); + EXPECT_EQ(w, 0u); +} + +/** + * @tc.name: GetQueryWaterMark 002 + * @tc.desc: Test metaData save and get queryWaterMark after push or pull mode. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, GetQueryWaterMark002, TestSize.Level1) +{ + VirtualSingleVerSyncDBInterface storage; + Metadata meta; + + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + int errCode = meta.Initialize(&storage); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. set peer and local watermark + * @tc.expected: step2. E_OK + */ + WaterMark w1 = 2; + EXPECT_EQ(meta.SaveLocalWaterMark("D1", w1), E_OK); + EXPECT_EQ(meta.SavePeerWaterMark("D1", w1, true), E_OK); + + /** + * @tc.steps: step2. save receive and send watermark + * @tc.expected: step2. E_OK + */ + WaterMark w2 = 1; + EXPECT_EQ(meta.SetRecvQueryWaterMark("Q1", "D1", w2), E_OK); + EXPECT_EQ(meta.SetSendQueryWaterMark("Q1", "D1", w2), E_OK); + + /** + * @tc.steps: step3. get receive and send watermark + * @tc.expected: step3. E_OK and get the bigger value + */ + WaterMark w = 0; + EXPECT_EQ(meta.GetRecvQueryWaterMark("Q1", "D1", w), E_OK); + EXPECT_EQ(w1, w); + EXPECT_EQ(meta.GetSendQueryWaterMark("Q1", "D1", w), E_OK); + EXPECT_EQ(w1, w); +} + +/** + * @tc.name: ClearQueryWaterMark 001 + * @tc.desc: Test metaData clear watermark function. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, ClearQueryWaterMark001, TestSize.Level1) +{ + VirtualSingleVerSyncDBInterface storage; + Metadata meta; + + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + int errCode = meta.Initialize(&storage); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. save receive watermark + * @tc.expected: step2. E_OK + */ + WaterMark w1 = 1; + EXPECT_EQ(meta.SetRecvQueryWaterMark("Q1", "D1", w1), E_OK); + + /** + * @tc.steps: step3. erase peer watermark + * @tc.expected: step3. E_OK + */ + EXPECT_EQ(meta.EraseDeviceWaterMark("D1", true), E_OK); + + /** + * @tc.steps: step4. get receive watermark + * @tc.expected: step4. E_OK receive watermark is zero + */ + WaterMark w2 = -1; + EXPECT_EQ(meta.GetRecvQueryWaterMark("Q1", "D1", w2), E_OK); + EXPECT_EQ(w2, 0u); + + /** + * @tc.steps: step5. set peer watermark + * @tc.expected: step5. E_OK + */ + WaterMark w3 = 2; + EXPECT_EQ(meta.SavePeerWaterMark("D1", w3, true), E_OK); + + /** + * @tc.steps: step6. get receive watermark + * @tc.expected: step6. E_OK receive watermark is peer watermark + */ + WaterMark w4 = -1; + EXPECT_EQ(meta.GetRecvQueryWaterMark("Q1", "D1", w4), E_OK); + EXPECT_EQ(w4, w3); +} + +/** + * @tc.name: ClearQueryWaterMark 002 + * @tc.desc: Test metaData clear watermark function. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, ClearQueryWaterMark002, TestSize.Level1) +{ + VirtualSingleVerSyncDBInterface storage; + Metadata meta; + + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + int errCode = meta.Initialize(&storage); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. save receive watermark + * @tc.expected: step2. E_OK + */ + WaterMark w1 = 1; + EXPECT_EQ(meta.SetRecvQueryWaterMark("Q1", "D1", w1), E_OK); + EXPECT_EQ(meta.SetRecvQueryWaterMark("Q2", "D1", w1), E_OK); + EXPECT_EQ(meta.SetRecvQueryWaterMark("Q1", "D2", w1), E_OK); + + /** + * @tc.steps: step3. erase peer watermark, make sure data remove in db + * @tc.expected: step3. E_OK + */ + Metadata anotherMeta; + ASSERT_EQ(anotherMeta.Initialize(&storage), E_OK); + EXPECT_EQ(anotherMeta.EraseDeviceWaterMark("D1", true), E_OK); + + /** + * @tc.steps: step4. get receive watermark + * @tc.expected: step4. E_OK receive watermark is zero + */ + WaterMark w2 = -1; + EXPECT_EQ(anotherMeta.GetRecvQueryWaterMark("Q1", "D1", w2), E_OK); + EXPECT_EQ(w2, 0u); + w2 = -1; + EXPECT_EQ(anotherMeta.GetRecvQueryWaterMark("Q2", "D1", w2), E_OK); + EXPECT_EQ(w2, 0u); + w2 = -1; + EXPECT_EQ(anotherMeta.GetRecvQueryWaterMark("Q1", "D2", w2), E_OK); + EXPECT_EQ(w2, w1); +} + +/** + * @tc.name: GetDeleteKeyWaterMark 001 + * @tc.desc: Test metaData save and get deleteWaterMark. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, GetDeleteKeyWaterMark001, TestSize.Level1) +{ + VirtualSingleVerSyncDBInterface storage; + Metadata meta; + + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + int errCode = meta.Initialize(&storage); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. save receive and send watermark + * @tc.expected: step2. E_OK + */ + WaterMark w1 = 1; + EXPECT_EQ(meta.SetRecvDeleteSyncWaterMark("D1", w1), E_OK); + EXPECT_EQ(meta.SetSendDeleteSyncWaterMark("D1", w1), E_OK); + + /** + * @tc.steps: step3. get receive and send watermark + * @tc.expected: step3. E_OK and get the latest value + */ + WaterMark w = 0; + EXPECT_EQ(meta.GetRecvDeleteSyncWaterMark("D1", w), E_OK); + EXPECT_EQ(w1, w); + EXPECT_EQ(meta.GetSendDeleteSyncWaterMark("D1", w), E_OK); + EXPECT_EQ(w1, w); + + /** + * @tc.steps: step4. set peer and local watermark + * @tc.expected: step4. E_OK + */ + WaterMark w2 = 2; + EXPECT_EQ(meta.SaveLocalWaterMark("D1", w2), E_OK); + EXPECT_EQ(meta.SavePeerWaterMark("D1", w2, true), E_OK); + + /** + * @tc.steps: step5. get receive and send watermark + * @tc.expected: step5. E_OK and get the w1 + */ + EXPECT_EQ(meta.GetRecvDeleteSyncWaterMark("D1", w), E_OK); + EXPECT_EQ(w2, w); + EXPECT_EQ(meta.GetSendDeleteSyncWaterMark("D1", w), E_OK); + EXPECT_EQ(w2, w); + + /** + * @tc.steps: step6. set peer and local watermark + * @tc.expected: step6. E_OK + */ + WaterMark w3 = 3; + EXPECT_EQ(meta.SaveLocalWaterMark("D2", w3), E_OK); + EXPECT_EQ(meta.SavePeerWaterMark("D2", w3, true), E_OK); + + /** + * @tc.steps: step7. get receive and send watermark + * @tc.expected: step7. E_OK and get the w3 + */ + EXPECT_EQ(meta.GetRecvDeleteSyncWaterMark("D2", w), E_OK); + EXPECT_EQ(w3, w); + EXPECT_EQ(meta.GetSendDeleteSyncWaterMark("D2", w), E_OK); + EXPECT_EQ(w3, w); + + /** + * @tc.steps: step8. get not exit receive and send watermark + * @tc.expected: step8. E_OK and get the 0 + */ + EXPECT_EQ(meta.GetRecvDeleteSyncWaterMark("D3", w), E_OK); + EXPECT_EQ(w, 0u); + EXPECT_EQ(meta.GetSendDeleteSyncWaterMark("D3", w), E_OK); + EXPECT_EQ(w, 0u); +} + +/** + * @tc.name: GetDeleteKeyWaterMark 002 + * @tc.desc: Test metaData save and get deleteWaterMark after push or pull mode. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, GetDeleteKeyWaterMark002, TestSize.Level1) +{ + VirtualSingleVerSyncDBInterface storage; + Metadata meta; + + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + int errCode = meta.Initialize(&storage); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. set peer and local watermark + * @tc.expected: step2. E_OK + */ + WaterMark w1 = 3; + EXPECT_EQ(meta.SaveLocalWaterMark("D1", w1), E_OK); + EXPECT_EQ(meta.SavePeerWaterMark("D1", w1, true), E_OK); + + /** + * @tc.steps: step2. save receive and send watermark + * @tc.expected: step2. E_OK + */ + WaterMark w2 = 1; + EXPECT_EQ(meta.SetRecvDeleteSyncWaterMark("D1", w2), E_OK); + EXPECT_EQ(meta.SetSendDeleteSyncWaterMark("D1", w2), E_OK); + + /** + * @tc.steps: step3. get receive and send watermark + * @tc.expected: step3. E_OK and get the bigger value + */ + WaterMark w = 0; + EXPECT_EQ(meta.GetRecvDeleteSyncWaterMark("D1", w), E_OK); + EXPECT_EQ(w1, w); + EXPECT_EQ(meta.GetSendDeleteSyncWaterMark("D1", w), E_OK); + EXPECT_EQ(w1, w); +} + +/** + * @tc.name: ClearDeleteKeyWaterMark 001 + * @tc.desc: Test metaData clear watermark function. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, ClearDeleteKeyWaterMark001, TestSize.Level1) +{ + VirtualSingleVerSyncDBInterface storage; + Metadata meta; + + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + int errCode = meta.Initialize(&storage); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step2. save receive watermark + * @tc.expected: step2. E_OK + */ + WaterMark w1 = 1; + EXPECT_EQ(meta.SetRecvDeleteSyncWaterMark("D1", w1), E_OK); + + /** + * @tc.steps: step3. erase peer watermark + * @tc.expected: step3. E_OK + */ + EXPECT_EQ(meta.EraseDeviceWaterMark("D1", true), E_OK); + + /** + * @tc.steps: step4. get receive watermark + * @tc.expected: step4. E_OK receive watermark is zero + */ + WaterMark w2 = -1; + EXPECT_EQ(meta.GetRecvDeleteSyncWaterMark("D1", w2), E_OK); + EXPECT_EQ(w2, 0u); + + /** + * @tc.steps: step5. set peer watermark + * @tc.expected: step5. E_OK + */ + WaterMark w3 = 2; + EXPECT_EQ(meta.SavePeerWaterMark("D1", w3, true), E_OK); + + /** + * @tc.steps: step6. get receive watermark + * @tc.expected: step6. E_OK receive watermark is peer watermark + */ + WaterMark w4 = -1; + EXPECT_EQ(meta.GetRecvDeleteSyncWaterMark("D1", w4), E_OK); + EXPECT_EQ(w4, w3); +} + +/** + * @tc.name: VerifyCacheAndDb 001 + * @tc.desc: Test metaData watermark cache and db are consistent and correct. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, VerifyMetaDataQuerySync001, TestSize.Level1) +{ + Metadata meta; + VirtualSingleVerSyncDBInterface storage; + + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + int errCode = meta.Initialize(&storage); + ASSERT_EQ(errCode, E_OK); + + const std::string deviceId = "D1"; + const std::string queryId = "Q1"; + + /** + * @tc.steps: step2. save deleteSync watermark + * @tc.expected: step2. E_OK + */ + WaterMark deleteWaterMark = 1; + EXPECT_EQ(meta.SetRecvDeleteSyncWaterMark(deviceId, deleteWaterMark), E_OK); + EXPECT_EQ(meta.SetSendDeleteSyncWaterMark(deviceId, deleteWaterMark), E_OK); + + /** + * @tc.steps: step3. save querySync watermark + * @tc.expected: step2. E_OK + */ + WaterMark queryWaterMark = 2; + EXPECT_EQ(meta.SetRecvQueryWaterMark(queryId, deviceId, queryWaterMark), E_OK); + EXPECT_EQ(meta.SetSendQueryWaterMark(queryId, deviceId, queryWaterMark), E_OK); + + /** + * @tc.steps: step4. initialize meta with storage + * @tc.expected: step4. E_OK + */ + Metadata anotherMeta; + ASSERT_EQ(anotherMeta.Initialize(&storage), E_OK); + + /** + * @tc.steps: step5. verify delete sync data + * @tc.expected: step5. E_OK and waterMark equal to deleteWaterMark + */ + WaterMark waterMark; + EXPECT_EQ(anotherMeta.GetRecvDeleteSyncWaterMark(deviceId, waterMark), E_OK); + EXPECT_EQ(waterMark, deleteWaterMark); + EXPECT_EQ(anotherMeta.GetSendDeleteSyncWaterMark(deviceId, waterMark), E_OK); + EXPECT_EQ(waterMark, deleteWaterMark); + + /** + * @tc.steps: step6. verify query sync data + * @tc.expected: step6. E_OK and waterMark equal to queryWaterMark + */ + EXPECT_EQ(anotherMeta.GetRecvQueryWaterMark(queryId, deviceId, waterMark), E_OK); + EXPECT_EQ(waterMark, queryWaterMark); + EXPECT_EQ(anotherMeta.GetSendQueryWaterMark(queryId, deviceId, waterMark), E_OK); + EXPECT_EQ(waterMark, queryWaterMark); +} + +/** + * @tc.name: VerifyLruMap 001 + * @tc.desc: Test metaData watermark cache lru ability. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, VerifyLruMap001, TestSize.Level1) +{ + LruMap lruMap; + const int maxCacheItems = 200; + + /** + * @tc.steps: step1. fill items to LruMap + * @tc.expected: step1. E_OK + */ + const int startCount = 0; + for (int i = startCount; i < maxCacheItems; i++) { + std::string key = std::to_string(i); + QueryWaterMark value; + value.recvWaterMark = i + 1; + EXPECT_EQ(lruMap.Put(key, value), E_OK); + } + + /** + * @tc.steps: step2. get the first item + * @tc.expected: step2. E_OK first item will move to last + */ + std::string firstItemKey = std::to_string(startCount); + QueryWaterMark firstItemValue; + EXPECT_EQ(lruMap.Get(firstItemKey, firstItemValue), E_OK); + EXPECT_EQ(firstItemValue.recvWaterMark, 1u); + + /** + * @tc.steps: step3. insert new items to LruMap + * @tc.expected: step3. the second items was removed + */ + std::string key = std::to_string(maxCacheItems); + QueryWaterMark value; + value.recvWaterMark = maxCacheItems; + EXPECT_EQ(lruMap.Put(key, value), E_OK); + + /** + * @tc.steps: step4. get the second item + * @tc.expected: step4. E_NOT_FOUND it was removed by algorithm + */ + std::string secondItemKey = std::to_string(startCount + 1); + QueryWaterMark secondItemValue; + EXPECT_EQ(lruMap.Get(secondItemKey, secondItemValue), -E_NOT_FOUND); + EXPECT_EQ(secondItemValue.recvWaterMark, 0u); +} + +/** + * @tc.name: VerifyMetaDataInit 001 + * @tc.desc: Test metaData init correctly + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, VerifyMetaDataInit001, TestSize.Level1) +{ + Metadata meta; + VirtualSingleVerSyncDBInterface storage; + + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + ASSERT_EQ(meta.Initialize(&storage), E_OK); + + DeviceID deviceA = "DeviceA"; + DeviceID deviceB = "DeviceA"; + WaterMark setWaterMark = 1; + + /** + * @tc.steps: step2. meta save and get waterMark + * @tc.expected: step2. expect get the same waterMark + */ + EXPECT_EQ(meta.SaveLocalWaterMark(deviceA, setWaterMark), E_OK); + EXPECT_EQ(meta.SaveLocalWaterMark(deviceB, setWaterMark), E_OK); + WaterMark getWaterMark = 0; + meta.GetLocalWaterMark(deviceA, getWaterMark); + EXPECT_EQ(getWaterMark, setWaterMark); + meta.GetLocalWaterMark(deviceB, getWaterMark); + EXPECT_EQ(getWaterMark, setWaterMark); + + + /** + * @tc.steps: step3. init again + * @tc.expected: step3. E_OK + */ + Metadata anotherMeta; + ASSERT_EQ(anotherMeta.Initialize(&storage), E_OK); + + /** + * @tc.steps: step4. get waterMark again + * @tc.expected: step4. expect get the same waterMark + */ + anotherMeta.GetLocalWaterMark(deviceA, getWaterMark); + EXPECT_EQ(getWaterMark, setWaterMark); + anotherMeta.GetLocalWaterMark(deviceB, getWaterMark); + EXPECT_EQ(getWaterMark, setWaterMark); +} + +namespace { +void InitVerifyStorageEnvironment(Metadata &meta, VirtualSingleVerSyncDBInterface &storage, + const std::string &deviceId, const int &startCount, const uint32_t &maxStoreItems) +{ + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + ASSERT_EQ(meta.Initialize(&storage), E_OK); + + /** + * @tc.steps: step2. fill items to metadata + * @tc.expected: step2. E_OK + */ + for (uint32_t i = startCount; i < maxStoreItems; i++) { + std::string queryId = std::to_string(i); + WaterMark recvWaterMark = i + 1; + EXPECT_EQ(meta.SetRecvQueryWaterMark(queryId, deviceId, recvWaterMark), E_OK); + } +} +} + +/** + * @tc.name: VerifyManagerQuerySyncStorage 001 + * @tc.desc: Test metaData remove least used querySync storage items. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, VerifyManagerQuerySyncStorage001, TestSize.Level3) +{ + Metadata meta; + VirtualSingleVerSyncDBInterface storage; + const uint32_t maxStoreItems = 100000; + const int startCount = 0; + const std::string deviceId = "Device"; + + InitVerifyStorageEnvironment(meta, storage, deviceId, startCount, maxStoreItems); + + /** + * @tc.steps: step3. insert new items to metadata + * @tc.expected: step3. E_OK + */ + std::string newQueryId = std::to_string(maxStoreItems); + WaterMark newWaterMark = maxStoreItems + 1; + EXPECT_EQ(meta.SetRecvQueryWaterMark(newQueryId, deviceId, newWaterMark), E_OK); + + /** + * @tc.steps: step4. touch the first item + * @tc.expected: step4. E_OK update first item used time + */ + std::string firstItemKey = std::to_string(startCount); + WaterMark firstWaterMark = 11u; + EXPECT_EQ(meta.SetRecvQueryWaterMark(firstItemKey, deviceId, firstWaterMark), E_OK); + + /** + * @tc.steps: step5. initialize new meta with storage + * @tc.expected: step5. the second item will be removed + */ + Metadata newMeta; + ASSERT_EQ(newMeta.Initialize(&storage), E_OK); + + /** + * @tc.steps: step6. touch the first item + * @tc.expected: step6. E_OK it still exist + */ + WaterMark exceptWaterMark; + EXPECT_EQ(newMeta.GetRecvQueryWaterMark(firstItemKey, deviceId, exceptWaterMark), E_OK); + EXPECT_EQ(exceptWaterMark, firstWaterMark); + + /** + * @tc.steps: step7. get the second item + * @tc.expected: step7. NOT_FOUND secondWaterMark is zero + */ + WaterMark secondWaterMark; + std::string secondQueryId = std::to_string(startCount + 1); + EXPECT_EQ(newMeta.GetRecvQueryWaterMark(secondQueryId, deviceId, secondWaterMark), E_OK); + EXPECT_EQ(secondWaterMark, 0u); +} + +/** + * @tc.name: VerifyMetaDbCreateTime 001 + * @tc.desc: Test metaData get and set cbCreateTime. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, VerifyMetaDbCreateTime001, TestSize.Level1) +{ + Metadata meta; + VirtualSingleVerSyncDBInterface storage; + /** + * @tc.steps: step1. initialize meta with storage + * @tc.expected: step1. E_OK + */ + int errCode = meta.Initialize(&storage); + ASSERT_EQ(errCode, E_OK); + /** + * @tc.steps: step2. set local and peer watermark and dbCreateTime + * @tc.expected: step4. E_OK + */ + WaterMark value = 2; + EXPECT_EQ(meta.SaveLocalWaterMark("D1", value), E_OK); + EXPECT_EQ(meta.SavePeerWaterMark("D1", value, true), E_OK); + EXPECT_EQ(meta.SetDbCreateTime("D1", 10u, true), E_OK); + /** + * @tc.steps: step3. check peer and local watermark and dbCreateTime + * @tc.expected: step4. E_OK + */ + WaterMark curValue = 0; + meta.GetLocalWaterMark("D1", curValue); + EXPECT_EQ(value, curValue); + meta.GetPeerWaterMark("D1", curValue); + EXPECT_EQ(value, curValue); + uint64_t curDbCreatTime = 0; + meta.GetDbCreateTime("D1", curDbCreatTime); + EXPECT_EQ(curDbCreatTime, 10u); + /** + * @tc.steps: step3. change dbCreateTime and check + * @tc.expected: step4. E_OK + */ + EXPECT_EQ(meta.SetDbCreateTime("D1", 20u, true), E_OK); + uint64_t clearDeviceDataMark = INT_MAX; + meta.GetRemoveDataMark("D1", clearDeviceDataMark); + EXPECT_EQ(clearDeviceDataMark, 1u); + EXPECT_EQ(meta.ResetMetaDataAfterRemoveData("D1"), E_OK); + meta.GetRemoveDataMark("D1", clearDeviceDataMark); + EXPECT_EQ(clearDeviceDataMark, 0u); + meta.GetDbCreateTime("D1", curDbCreatTime); + EXPECT_EQ(curDbCreatTime, 20u); +} + +/** + * @tc.name: VerifyManagerQuerySyncStorage 002 + * @tc.desc: Test metaData remove least used querySync storage items when exit wrong data. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, VerifyManagerQuerySyncStorage002, TestSize.Level3) +{ + Metadata meta; + VirtualSingleVerSyncDBInterface storage; + const uint32_t maxStoreItems = 100000; + const int startCount = 0; + const std::string deviceId = "Device"; + + InitVerifyStorageEnvironment(meta, storage, deviceId, startCount, maxStoreItems); + + /** + * @tc.steps: step3. insert a wrong Value + * @tc.expected: step3. E_OK + */ + std::string newQueryId = std::to_string(maxStoreItems); + Key dbKey; + DBCommon::StringToVector(QuerySyncWaterMarkHelper::GetQuerySyncPrefixKey() + + DBCommon::TransferHashString(deviceId) + newQueryId, dbKey); + Value wrongValue; + EXPECT_EQ(storage.PutMetaData(dbKey, wrongValue), E_OK); + + /** + * @tc.steps: step4. initialize new meta with storage + * @tc.expected: step4. E_OK + */ + Metadata newMeta; + ASSERT_EQ(newMeta.Initialize(&storage), E_OK); + + /** + * @tc.steps: step5. touch the first item + * @tc.expected: step5. E_OK still exit + */ + std::string firstItemKey = std::to_string(startCount); + WaterMark exceptWaterMark; + EXPECT_EQ(newMeta.GetRecvQueryWaterMark(firstItemKey, deviceId, exceptWaterMark), E_OK); + EXPECT_EQ(exceptWaterMark, 1u); +} + +/** + * @tc.name: AllPredicateQuerySync001 + * @tc.desc: Test normal push sync for AllPredicate data. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, AllPredicateQuerySync001, TestSize.Level1) +{ + /** + * @tc.steps: step1. InitSchemaDb + */ + InitSchemaDb(); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step2. deviceA put {key11, SCHEMA_VALUE1} - {key19, SCHEMA_VALUE1} + {key21, SCHEMA_VALUE2} - {key29, SCHEMA_VALUE2} + */ + Value value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end()); + Value value2(SCHEMA_VALUE2.begin(), SCHEMA_VALUE2.end()); + Key key = {'1'}; + Key key2 = {'2'}; + const int dataSize = 4000; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + key2.push_back(i); + status = g_schemaKvDelegatePtr->Put(key, value); + status = g_schemaKvDelegatePtr->Put(key2, value2); + ASSERT_TRUE(status == OK); + key.pop_back(); + key2.pop_back(); + } + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call query sync and wait + * @tc.expected: step3. sync should return OK. + */ + Query query = Query::Select().EqualTo("$.field_name1", 1); + std::map result; + status = g_tool.SyncTest(g_schemaKvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, query); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step4. onComplete should be called, DeviceB have {key11, SCHEMA_VALUE1} - {key19, SCHEMA_VALUE1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + VirtualDataItem item2; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + key2.push_back(i); + g_deviceB->GetData(key, item); + EXPECT_TRUE(g_deviceB->GetData(key2, item2) != E_OK); + EXPECT_TRUE(item.value == value); + key.pop_back(); + key2.pop_back(); + } +} + +/** + * @tc.name: AllPredicateQuerySync002 + * @tc.desc: Test wrong query param push sync for AllPredicate data. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, AllPredicateQuerySync002, TestSize.Level1) +{ + /** + * @tc.steps: step1. InitSchemaDb + */ + InitSchemaDb(); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step2. deviceA call query sync and wait + * @tc.expected: step2. sync should return INVALID_QUERY_FIELD + */ + Query query = Query::Select().GreaterThan("field_name11", 10); + std::map result; + status = g_tool.SyncTest(g_schemaKvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, query); + ASSERT_TRUE(status == INVALID_QUERY_FIELD); + status = g_tool.SyncTest(g_schemaKvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result, query); + ASSERT_TRUE(status == INVALID_QUERY_FIELD); + status = g_tool.SyncTest(g_schemaKvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result, query); + ASSERT_TRUE(status == INVALID_QUERY_FIELD); +} + +/** + * @tc.name: AllPredicateQuerySync003 + * @tc.desc: Test normal push sync for AllPredicate data with limit + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, AllPredicateQuerySync003, TestSize.Level1) +{ + /** + * @tc.steps: step1. InitSchemaDb + */ + InitSchemaDb(); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step2. deviceA put {key1, SCHEMA_VALUE1} - {key9, SCHEMA_VALUE1} + */ + Value value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end()); + Value value2(SCHEMA_VALUE2.begin(), SCHEMA_VALUE2.end()); + Key key = {'1'}; + Key key2 = {'2'}; + const int dataSize = 10; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + key2.push_back(i); + status = g_schemaKvDelegatePtr->Put(key, value); + status = g_schemaKvDelegatePtr->Put(key2, value2); + ASSERT_TRUE(status == OK); + key.pop_back(); + key2.pop_back(); + } + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call query sync with limit and wait + * @tc.expected: step3. sync should return OK. + */ + Query query = Query::Select().EqualTo("$.field_name1", 1).Limit(20, 0); + std::map result; + status = g_tool.SyncTest(g_schemaKvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, query); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step4. onComplete should be called, DeviceB have {key1, SCHEMA_VALUE1} - {key9, SCHEMA_VALUE1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + VirtualDataItem item2; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + key2.push_back(i); + g_deviceB->GetData(key, item); + EXPECT_TRUE(g_deviceB->GetData(key2, item2) != E_OK); + EXPECT_TRUE(item.value == value); + key.pop_back(); + key2.pop_back(); + } +} + +/** + * @tc.name: AllPredicateQuerySync004 + * @tc.desc: Test normal pull sync for AllPredicate data. + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PQuerySyncTest, AllPredicateQuerySync004, TestSize.Level1) +{ + /** + * @tc.steps: step1. InitSchemaDb + */ + InitSchemaDb(); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step2. deviceB put {key11, SCHEMA_VALUE1} - {key19, SCHEMA_VALUE1} + */ + Value value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end()); + Key key = {'1'}; + const int dataSize = 10; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + g_deviceB->PutData(key, value, 10 + i, 0); + ASSERT_TRUE(status == OK); + key.pop_back(); + } + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call query sync and wait + * @tc.expected: step3. sync should return OK. + */ + Query query = Query::Select().EqualTo("$.field_name1", 1); + std::map result; + status = g_tool.SyncTest(g_schemaKvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result, query); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step4. onComplete should be called, DeviceA have {key11, SCHEMA_VALUE1} - {key19, SCHEMA_VALUE1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + Value item; + Value item2; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + g_schemaKvDelegatePtr->Get(key, item); + EXPECT_TRUE(item == value); + key.pop_back(); + } +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_subsribe_sync_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_subsribe_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d7037fbcc28f9ec0c7bca20582a071faef20be6 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_subsribe_sync_test.cpp @@ -0,0 +1,881 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "kv_store_nb_delegate.h" +#include "kv_virtual_device.h" +#include "platform_specific.h" +#include "query.h" +#include "query_sync_object.h" +#include "single_ver_data_sync.h" +#include "single_ver_serialize_manager.h" +#include "subscribe_manager.h" +#include "sync_types.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const string SCHEMA_STORE_ID = "kv_store_sync_schema_test"; + const std::string DEVICE_A = "deviceA"; + const std::string DEVICE_B = "deviceB"; + + KvStoreDelegateManager g_schemaMgr(SCHEMA_APP_ID, USER_ID); + KvStoreConfig g_config; + DistributedDBToolsUnitTest g_tool; + DBStatus g_schemaKvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate* g_schemaKvDelegatePtr = nullptr; + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + KvVirtualDevice* g_deviceB = nullptr; + + // the type of g_kvDelegateCallback is function + auto g_schemaKvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_schemaKvDelegateStatus), std::ref(g_schemaKvDelegatePtr)); + const string SCHEMA_STRING = + "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_DEFINE\":{" + "\"field_name1\":\"BOOL\"," + "\"field_name2\":\"BOOL\"," + "\"field_name3\":\"INTEGER, NOT NULL\"," + "\"field_name4\":\"LONG, DEFAULT 100\"," + "\"field_name5\":\"DOUBLE, NOT NULL, DEFAULT 3.14\"," + "\"field_name6\":\"STRING, NOT NULL, DEFAULT '3.1415'\"," + "\"field_name7\":\"LONG, DEFAULT 100\"," + "\"field_name8\":\"LONG, DEFAULT 100\"," + "\"field_name9\":\"LONG, DEFAULT 100\"," + "\"field_name10\":\"LONG, DEFAULT 100\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.field_name1\", \"$.field_name2\"]}"; + + const std::string SCHEMA_VALUE1 = + "{\"field_name1\":true," + "\"field_name2\":false," + "\"field_name3\":10," + "\"field_name4\":20," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415\"," + "\"field_name7\":100," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; + + const std::string SCHEMA_VALUE2 = + "{\"field_name1\":false," + "\"field_name2\":true," + "\"field_name3\":100," + "\"field_name4\":200," + "\"field_name5\":3.14," + "\"field_name6\":\"3.1415\"," + "\"field_name7\":100," + "\"field_name8\":100," + "\"field_name9\":100," + "\"field_name10\":100}"; +} + +class DistributedDBSingleVerP2PSubscribeSyncTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSingleVerP2PSubscribeSyncTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Init datadir and Virtual Communicator. + */ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_schemaMgr.SetKvStoreConfig(g_config); + + string dir = g_testDir + "/single_ver"; + DIR* dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); +} + +void DistributedDBSingleVerP2PSubscribeSyncTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Release virtual Communicator and clear data dir. + */ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); +} + +void DistributedDBSingleVerP2PSubscribeSyncTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: create virtual device B and get a KvStoreNbDelegate as deviceA + */ + g_deviceB = new (std::nothrow) KvVirtualDevice(DEVICE_B); + ASSERT_TRUE(g_deviceB != nullptr); + VirtualSingleVerSyncDBInterface *syncInterfaceB = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(syncInterfaceB != nullptr); + ASSERT_EQ(g_deviceB->Initialize(g_communicatorAggregator, syncInterfaceB), E_OK); +} + +void DistributedDBSingleVerP2PSubscribeSyncTest::TearDown(void) +{ + /** + * @tc.teardown: Release device A, B + */ + if (g_schemaKvDelegatePtr != nullptr) { + ASSERT_EQ(g_schemaMgr.CloseKvStore(g_schemaKvDelegatePtr), OK); + g_schemaKvDelegatePtr = nullptr; + DBStatus status = g_schemaMgr.DeleteKvStore(SCHEMA_STORE_ID); + LOGD("delete kv store status %d", status); + ASSERT_TRUE(status == OK); + } + if (g_deviceB != nullptr) { + delete g_deviceB; + g_deviceB = nullptr; + } + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_schemaMgr.SetPermissionCheckCallback(nullCallback), OK); +} + +void InitSubSchemaDb() +{ + g_config.dataDir = g_testDir; + g_schemaMgr.SetKvStoreConfig(g_config); + KvStoreNbDelegate::Option option; + option.schema = SCHEMA_STRING; + g_schemaMgr.GetKvStore(SCHEMA_STORE_ID, option, g_schemaKvDelegateCallback); + ASSERT_TRUE(g_schemaKvDelegateStatus == OK); + ASSERT_TRUE(g_schemaKvDelegatePtr != nullptr); +} + +void CheckUnFinishedMap(uint32_t sizeA, uint32_t sizeB, std::vector &deviceAQueies, + std::vector &deviceBQueies, SubscribeManager &subManager) +{ + std::map> allSyncQueries; + subManager.GetAllUnFinishSubQueries(allSyncQueries); + ASSERT_TRUE(allSyncQueries[DEVICE_A].size() == sizeA); + ASSERT_TRUE(allSyncQueries[DEVICE_B].size() == sizeB); + for (auto &item : allSyncQueries[DEVICE_A]) { + std::string queryId = item.GetIdentify(); + ASSERT_TRUE(std::find(deviceAQueies.begin(), deviceAQueies.end(), queryId) != deviceAQueies.end()); + } + for (auto &item : allSyncQueries[DEVICE_B]) { + std::string queryId = item.GetIdentify(); + ASSERT_TRUE(std::find(deviceBQueies.begin(), deviceBQueies.end(), queryId) != deviceBQueies.end()); + } +} + +void InitLocalSubscribeMap(QuerySyncObject &queryCommonObj, std::map &queryMap, + std::vector &deviceAQueies, std::vector &deviceBQueies, SubscribeManager &subManager) +{ + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(DEVICE_A, queryCommonObj) == E_OK); + ASSERT_TRUE(subManager.ActiveLocalSubscribeQuery(DEVICE_A, queryCommonObj) == E_OK); + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(DEVICE_B, queryCommonObj) == E_OK); + ASSERT_TRUE(subManager.ActiveLocalSubscribeQuery(DEVICE_B, queryCommonObj) == E_OK); + queryMap[queryCommonObj.GetIdentify()] = queryCommonObj; + deviceAQueies.push_back(queryCommonObj.GetIdentify()); + deviceBQueies.push_back(queryCommonObj.GetIdentify()); + for (int i = 0; i < 3; i++) { // 3 subscribe + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + i)})); + deviceAQueies.push_back(querySyncObj.GetIdentify()); + queryMap[querySyncObj.GetIdentify()] = querySyncObj; + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(DEVICE_A, querySyncObj) == E_OK); + ASSERT_TRUE(subManager.ActiveLocalSubscribeQuery(DEVICE_A, querySyncObj) == E_OK); + } + for (int i = 0; i < 1; i++) { + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('b' + i)})); + deviceBQueies.push_back(querySyncObj.GetIdentify()); + queryMap[querySyncObj.GetIdentify()] = querySyncObj; + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(DEVICE_B, querySyncObj) == E_OK); + ASSERT_TRUE(subManager.ActiveLocalSubscribeQuery(DEVICE_B, querySyncObj) == E_OK); + } +} +/** + * @tc.name: SubscribeRequestTest001 + * @tc.desc: test Serialize/DoSerialize SubscribeRequest + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, SubscribeRequestTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. prepare a SubscribeRequest. + */ + auto packet = new (std::nothrow) SubscribeRequest; + ASSERT_TRUE(packet != nullptr); + packet->SetPacketHead(100, SOFTWARE_VERSION_CURRENT, SUBSCRIBE_QUERY_CMD, 1); + Query query = Query::Select().EqualTo("$.field_name1", 1); + QuerySyncObject syncQuery(query); + packet->SetQuery(syncQuery); + + /** + * @tc.steps: step2. put the SubscribeRequest Packet into a message. + */ + Message msg; + msg.SetExternalObject(packet); + msg.SetMessageId(CONTROL_SYNC_MESSAGE); + msg.SetMessageType(TYPE_REQUEST); + + /** + * @tc.steps: step3. Serialization the message to a buffer. + */ + int len = SingleVerSerializeManager::CalculateLen(&msg); + LOGE("test leng = %d", len); + uint8_t *buffer = new (nothrow) uint8_t[len]; + ASSERT_TRUE(buffer != nullptr); + ASSERT_EQ(SingleVerSerializeManager::Serialization(buffer, len, &msg), E_OK); + + /** + * @tc.steps: step4. DeSerialization the buffer to a message. + */ + Message outMsg; + outMsg.SetMessageId(CONTROL_SYNC_MESSAGE); + outMsg.SetMessageType(TYPE_REQUEST); + ASSERT_EQ(SingleVerSerializeManager::DeSerialization(buffer, len, &outMsg), E_OK); + + /** + * @tc.steps: step5. checkout the outMsg. + * @tc.expected: step5. outMsg equal the the in msg + */ + auto outPacket = outMsg.GetObject(); + EXPECT_EQ(outPacket->GetVersion(), SOFTWARE_VERSION_CURRENT); + EXPECT_EQ(outPacket->GetSendCode(), 100); + EXPECT_EQ(outPacket->GetcontrolCmdType(), SUBSCRIBE_QUERY_CMD); + EXPECT_EQ(outPacket->GetFlag(), 1u); + EXPECT_EQ(outPacket->GetQuery().GetIdentify(), syncQuery.GetIdentify()); + delete[] buffer; +} + +/** + * @tc.name: ControlAckTest001 + * @tc.desc: test Serialize/DoSerialize ControlAckPacket + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, ControlAckTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. prepare a ControlAckPacket. + */ + ControlAckPacket packet; + packet.SetPacketHead(-E_NOT_SUPPORT, SOFTWARE_VERSION_CURRENT, SUBSCRIBE_QUERY_CMD, 1); + + /** + * @tc.steps: step2. put the QuerySyncAckPacket into a message. + */ + Message msg; + msg.SetCopiedObject(packet); + msg.SetMessageId(CONTROL_SYNC_MESSAGE); + msg.SetMessageType(TYPE_RESPONSE); + + /** + * @tc.steps: step3. Serialization the message to a buffer. + */ + int len = SingleVerSerializeManager::CalculateLen(&msg); + LOGE("test leng = %d", len); + uint8_t *buffer = new (nothrow) uint8_t[len]; + ASSERT_TRUE(buffer != nullptr); + int errCode = SingleVerSerializeManager::Serialization(buffer, len, &msg); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step4. DeSerialization the buffer to a message. + */ + Message outMsg; + outMsg.SetMessageId(CONTROL_SYNC_MESSAGE); + outMsg.SetMessageType(TYPE_RESPONSE); + errCode = SingleVerSerializeManager::DeSerialization(buffer, len, &outMsg); + ASSERT_EQ(errCode, E_OK); + + /** + * @tc.steps: step5. checkout the outMsg. + * @tc.expected: step5. outMsg equal the the in msg + */ + auto outPacket = outMsg.GetObject(); + EXPECT_EQ(outPacket->GetVersion(), SOFTWARE_VERSION_CURRENT); + EXPECT_EQ(outPacket->GetRecvCode(), -E_NOT_SUPPORT); + EXPECT_EQ(outPacket->GetcontrolCmdType(), SUBSCRIBE_QUERY_CMD); + EXPECT_EQ(outPacket->GetFlag(), 1u); + delete[] buffer; +} + +/** + * @tc.name: subscribeManager001 + * @tc.desc: test subscribe class subscribe local function with one device + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeManager001, TestSize.Level1) +{ + SubscribeManager subManager; + std::string device = "device_A"; + /** + * @tc.steps: step1. test one device limit four subscribe queries in local map + */ + LOGI("============step 1============"); + for (int i = 0; i < 4; i++) { + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + i)})); + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(device, querySyncObj) == E_OK); + ASSERT_TRUE(subManager.ActiveLocalSubscribeQuery(device, querySyncObj) == E_OK); + } + std::vector subscribeQueries; + subManager.GetLocalSubscribeQueries(device, subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 4); + subscribeQueries.clear(); + QuerySyncObject querySyncObj1(Query::Select().PrefixKey({'a', static_cast('a' + 4)})); + int errCode = subManager.ReserveLocalSubscribeQuery(device, querySyncObj1); + ASSERT_TRUE(errCode != E_OK); + /** + * @tc.steps: step2. allow to subscribe existed query + */ + LOGI("============step 2============"); + QuerySyncObject querySyncObj2(Query::Select().PrefixKey({'a', static_cast('a' + 3)})); + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(device, querySyncObj2) == E_OK); + ASSERT_TRUE(subManager.ActiveLocalSubscribeQuery(device, querySyncObj2) == E_OK); + subManager.GetLocalSubscribeQueries(device, subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 4); + subscribeQueries.clear(); + /** + * @tc.steps: step3. unsubscribe no existed queries + */ + LOGI("============step 3============"); + subManager.RemoveLocalSubscribeQuery(device, querySyncObj1); + subManager.GetLocalSubscribeQueries(device, subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 4); + subscribeQueries.clear(); + /** + * @tc.steps: step4. unsubscribe queries + */ + LOGI("============step 4============"); + for (int i = 0; i < 4; i++) { + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + i)})); + subManager.RemoveLocalSubscribeQuery(device, querySyncObj); + } + subManager.GetLocalSubscribeQueries(device, subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 0); + + /** + * @tc.steps: step5. reserve twice while subscribe queries + */ + LOGI("============step 5============"); + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(device, querySyncObj2) == E_OK); + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(device, querySyncObj2) == E_OK); + ASSERT_TRUE(subManager.ActiveLocalSubscribeQuery(device, querySyncObj2) == E_OK); + subManager.GetLocalSubscribeQueries(device, subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 1); + subscribeQueries.clear(); + subManager.RemoveLocalSubscribeQuery(device, querySyncObj2); + subManager.GetLocalSubscribeQueries(device, subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 0); +} + +/** + * @tc.name: subscribeManager002 + * @tc.desc: test subscribe class subscribe remote function with one device + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeManager002, TestSize.Level1) +{ + SubscribeManager subManager; + std::string device = "device_A"; + /** + * @tc.steps: step1. test one device limit four subscribe queries in remote map + */ + LOGI("============step 1============"); + for (int i = 0; i < 4; i++) { + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + i)})); + ASSERT_TRUE(subManager.ReserveRemoteSubscribeQuery(device, querySyncObj) == E_OK); + ASSERT_TRUE(subManager.ActiveRemoteSubscribeQuery(device, querySyncObj) == E_OK); + } + QuerySyncObject querySyncObj1(Query::Select().PrefixKey({'a', static_cast('a' + 4)})); + ASSERT_TRUE(subManager.ReserveRemoteSubscribeQuery(device, querySyncObj1) != E_OK); + std::vector subscribeQueryId; + subManager.GetRemoteSubscribeQueryIds(device, subscribeQueryId); +ASSERT_TRUE(subscribeQueryId.size() == 4); + subscribeQueryId.clear(); + /** + * @tc.steps: step2. allow to subscribe existed query + */ + LOGI("============step 2============"); + QuerySyncObject querySyncObj2(Query::Select().PrefixKey({'a', static_cast('a' + 3)})); + ASSERT_TRUE(subManager.ReserveRemoteSubscribeQuery(device, querySyncObj2) == E_OK); + ASSERT_TRUE(subManager.ActiveRemoteSubscribeQuery(device, querySyncObj2) == E_OK); + subManager.GetRemoteSubscribeQueryIds(device, subscribeQueryId); + ASSERT_TRUE(subscribeQueryId.size() == 4); + subscribeQueryId.clear(); + /** + * @tc.steps: step3. unsubscribe no existed queries + */ + LOGI("============step 3============"); + subManager.RemoveRemoteSubscribeQuery(device, querySyncObj1); + subManager.GetRemoteSubscribeQueryIds(device, subscribeQueryId); + ASSERT_TRUE(subscribeQueryId.size() == 4); + subscribeQueryId.clear(); + /** + * @tc.steps: step4. unsubscribe queries + */ + LOGI("============step 4============"); + for (int i = 0; i < 4; i++) { + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + i)})); + subManager.RemoveRemoteSubscribeQuery(device, querySyncObj); + } + subManager.GetRemoteSubscribeQueryIds(device, subscribeQueryId); + ASSERT_TRUE(subscribeQueryId.size() == 0); +} + +/** + * @tc.name: subscribeManager003 + * @tc.desc: test subscribe class subscribe remote function with multi device + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeManager003, TestSize.Level1) +{ + SubscribeManager subManager; + std::string device = "device_"; + std::vector subscribeQueries; + /** + * @tc.steps: step1. test mutil device limit 32 devices in remote map and check each device has one subscribe + */ + LOGI("============step 1============"); + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + 1)})); + for (int i = 0; i < 32; i++) { + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(device + std::to_string(i), querySyncObj) == E_OK); + ASSERT_TRUE(subManager.ActiveLocalSubscribeQuery(device + std::to_string(i), querySyncObj) == E_OK); + } + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(device + std::to_string(33), querySyncObj) != E_OK); + for (int i = 0; i < 32; i++) { + subManager.GetLocalSubscribeQueries(device + std::to_string(i), subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 1); + subscribeQueries.clear(); + } + /** + * @tc.steps: step2. clear remote subscribe query map and check each device has no subscribe + */ + LOGI("============step 2============"); + for (int i = 0; i < 32; i++) { + subManager.ClearLocalSubscribeQuery(device + std::to_string(i)); + subManager.GetLocalSubscribeQueries(device + std::to_string(i), subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 0); + subscribeQueries.clear(); + } + /** + * @tc.steps: step3. test mutil device limit 8 queries in db and check each device has one subscribe + */ + LOGI("============step 3============"); + for (int i = 0; i < 8; i++) { + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + i)})); + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(device + std::to_string(i), querySyncObj) == E_OK); + ASSERT_TRUE(subManager.ActiveLocalSubscribeQuery(device + std::to_string(i), querySyncObj) == E_OK); + } + QuerySyncObject querySyncObj1(Query::Select().PrefixKey({'a', static_cast('a' + 8)})); + ASSERT_TRUE(subManager.ReserveLocalSubscribeQuery(device + std::to_string(8), querySyncObj1) != E_OK); +} + +/** + * @tc.name: subscribeManager004 + * @tc.desc: test subscribe class subscribe remote function with multi device + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeManager004, TestSize.Level1) +{ + SubscribeManager subManager; + std::string device = "device_"; + std::vector subscribeQueryId; + /** + * @tc.steps: step1. test mutil device limit 32 devices in remote map and check each device has one subscribe + */ + LOGI("============step 1============"); + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + 1)})); + for (int i = 0; i < 32; i++) { + ASSERT_TRUE(subManager.ReserveRemoteSubscribeQuery(device + std::to_string(i), querySyncObj) == E_OK); + ASSERT_TRUE(subManager.ActiveRemoteSubscribeQuery(device + std::to_string(i), querySyncObj) == E_OK); + } + ASSERT_TRUE(subManager.ReserveRemoteSubscribeQuery(device + std::to_string(33), querySyncObj) != E_OK); + for (int i = 0; i < 32; i++) { + subManager.GetRemoteSubscribeQueryIds(device + std::to_string(i), subscribeQueryId); + ASSERT_TRUE(subscribeQueryId.size() == 1); + subscribeQueryId.clear(); + } + /** + * @tc.steps: step2. clear remote subscribe query map and check each device has no subscribe + */ + LOGI("============step 2============"); + for (int i = 0; i < 32; i++) { + subManager.ClearRemoteSubscribeQuery(device + std::to_string(i)); + subManager.GetRemoteSubscribeQueryIds(device + std::to_string(i), subscribeQueryId); + ASSERT_TRUE(subscribeQueryId.size() == 0); + subscribeQueryId.clear(); + } + subManager.ClearRemoteSubscribeQuery(device); + /** + * @tc.steps: step3. test mutil device limit 8 queries in db and check each device has one subscribe + */ + LOGI("============step 3============"); + for (int i = 0; i < 8; i++) { + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + i)})); + ASSERT_TRUE(subManager.ReserveRemoteSubscribeQuery(device + std::to_string(i), querySyncObj) == E_OK); + ASSERT_TRUE(subManager.ActiveRemoteSubscribeQuery(device + std::to_string(i), querySyncObj) == E_OK); + } + QuerySyncObject querySyncObj1(Query::Select().PrefixKey({'a', static_cast('a' + 8)})); + ASSERT_TRUE(subManager.ReserveRemoteSubscribeQuery(device + std::to_string(8), querySyncObj1) != E_OK); +} + +/** + * @tc.name: subscribeManager005 + * @tc.desc: test subscribe class subscribe remote function with put into unfinished map + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeManager005, TestSize.Level1) +{ + SubscribeManager subManager; + std::vector subscribeQueries; + std::map queryMap; + std::vector deviceAQueies; + std::vector deviceBQueies; + QuerySyncObject queryCommonObj(Query::Select().PrefixKey({'a'})); + /** + * @tc.steps: step1. test one devices has 4 subscribes and another has 2 in local map, put into unfinished map + */ + LOGI("============step 1============"); + InitLocalSubscribeMap(queryCommonObj, queryMap, deviceAQueies, deviceBQueies, subManager); + /** + * @tc.steps: step2. check all device unFinished subscribe queries and put into unfinished map + */ + LOGI("============step 2============"); + subManager.GetLocalSubscribeQueries(DEVICE_A, subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 4); + subManager.PutLocalUnFiniedSubQueries(DEVICE_A, subscribeQueries); + subscribeQueries.clear(); + subManager.GetLocalSubscribeQueries(DEVICE_B, subscribeQueries); + ASSERT_TRUE(subscribeQueries.size() == 2); + subManager.PutLocalUnFiniedSubQueries(DEVICE_B, subscribeQueries); + subscribeQueries.clear(); + /** + * @tc.steps: step3. get all device unFinished subscribe queries and check + */ + LOGI("============step 3============"); + CheckUnFinishedMap(4, 2, deviceAQueies, deviceBQueies, subManager); + /** + * @tc.steps: step4. active some subscribe queries + */ + LOGI("============step 4============"); + subManager.ActiveLocalSubscribeQuery(DEVICE_A, queryCommonObj); + subManager.ActiveLocalSubscribeQuery(DEVICE_A, queryMap[deviceAQueies[3]]); + subManager.ActiveLocalSubscribeQuery(DEVICE_B, queryMap[deviceBQueies[1]]); + deviceAQueies.erase(deviceAQueies.begin() + 3); + deviceAQueies.erase(deviceAQueies.begin()); + queryMap.erase(queryMap[deviceBQueies[1]].GetIdentify()); + deviceBQueies.erase(deviceBQueies.begin() + 1); + /** + * @tc.steps: step5. get all device unFinished subscribe queries and check + */ + LOGI("============step 5============"); + CheckUnFinishedMap(2, 1, deviceAQueies, deviceBQueies, subManager); + /** + * @tc.steps: step6. remove left subscribe queries + */ + LOGI("============step 6============"); + for (int i = 0; i < 2; i++) { + QuerySyncObject querySyncObj(Query::Select().PrefixKey({'a', static_cast('a' + i)})); + subManager.RemoveLocalSubscribeQuery(DEVICE_A, querySyncObj); + } + subManager.RemoveLocalSubscribeQuery(DEVICE_A, queryCommonObj); + subManager.RemoveLocalSubscribeQuery(DEVICE_B, queryCommonObj); + /** + * @tc.steps: step7. get all device unFinished subscribe queries and check + */ + LOGI("============step 7============"); + CheckUnFinishedMap(0, 0, deviceAQueies, deviceBQueies, subManager); +} + +/** + * @tc.name: subscribeSync001 + * @tc.desc: test subscribe normal sync + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeSync001, TestSize.Level1) +{ + /** + * @tc.steps: step1. InitSchemaDb + */ + LOGI("============step 1============"); + InitSubSchemaDb(); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + Query query = Query::Select().EqualTo("$.field_name1", 1); + QuerySyncObject querySyncObj(query); + + /** + * @tc.steps: step2. deviceB subscribe query to deviceA + */ + LOGI("============step 2============"); + g_deviceB->Subscribe(querySyncObj, true, 1); + + /** + * @tc.steps: step3. deviceA put {key1, SCHEMA_VALUE1} and wait 1s + */ + LOGI("============step 3============"); + Value value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end()); + Key key = {'1'}; + status = g_schemaKvDelegatePtr->Put(key, value); + EXPECT_EQ(status, OK); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + /** + * @tc.steps: step4. deviceB has {key11, SCHEMA_VALUE1} + */ + LOGI("============step 4============"); + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); + + /** + * @tc.steps: step5. deviceB unsubscribe query to deviceA + */ + g_deviceB->UnSubscribe(querySyncObj, true, 2); + + /** + * @tc.steps: step5. deviceA put {key2, SCHEMA_VALUE1} and wait 1s + */ + LOGI("============step 5============"); + Value value2(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end()); + Key key2 = {'2'}; + status = g_schemaKvDelegatePtr->Put(key2, value2); + EXPECT_EQ(status, OK); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + /** + * @tc.steps: step6. deviceB don't has {key2, SCHEMA_VALUE1} + */ + LOGI("============step 6============"); + VirtualDataItem item2; + EXPECT_TRUE(g_deviceB->GetData(key2, item2) != E_OK); +} + +/** + * @tc.name: subscribeSync002 + * @tc.desc: test subscribe sync over 32 devices,limit,orderBy + * @tc.type: FUNC + * @tc.require: AR000FN6G9 + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeSync002, TestSize.Level1) +{ + /** + * @tc.steps: step1. InitSchemaDb + */ + LOGI("============step 1============"); + InitSubSchemaDb(); + std::vector devices; + std::string device = "device_"; + Query query = Query::Select().EqualTo("$.field_name1", 1); + + /** + * @tc.steps: step2. deviceA subscribe query to 33 devices, and return overlimit + */ + LOGI("============step 2============"); + for (int i = 0; i < 33; i++) { + devices.push_back(device + std::to_string(i)); + } + EXPECT_TRUE(g_schemaKvDelegatePtr->SubscribeRemoteQuery(devices, nullptr, query, true) == OVER_MAX_LIMITS); + + /** + * @tc.steps: step3. deviceA subscribe query with limit + */ + LOGI("============step 3============"); + devices.clear(); + devices.push_back("device_B"); + Query query2 = Query::Select().EqualTo("$.field_name1", 1).Limit(20, 0); + EXPECT_TRUE(g_schemaKvDelegatePtr->SubscribeRemoteQuery(devices, nullptr, query2, true) == NOT_SUPPORT); + + /** + * @tc.steps: step4. deviceA subscribe query with orderBy + */ + LOGI("============step 4============"); + Query query3 = Query::Select().EqualTo("$.field_name1", 1).OrderBy("$.field_name7"); + EXPECT_TRUE(g_schemaKvDelegatePtr->SubscribeRemoteQuery(devices, nullptr, query3, true) == NOT_SUPPORT); +} + +/** + * @tc.name: subscribeSync003 + * @tc.desc: test subscribe sync with inkeys query + * @tc.type: FUNC + * @tc.require: AR000GOHO7 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeSync003, TestSize.Level1) +{ + /** + * @tc.steps: step1. InitSchemaDb + */ + LOGI("============step 1============"); + InitSubSchemaDb(); + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step2. deviceB subscribe inkeys(k2k4) query to deviceA + */ + LOGI("============step 2============"); + Query query = Query::Select().InKeys({KEY_2, KEY_4}); + g_deviceB->Subscribe(QuerySyncObject(query), true, 1); + + /** + * @tc.steps: step3. deviceA put k1-k5 and wait + */ + LOGI("============step 3============"); + EXPECT_EQ(OK, g_schemaKvDelegatePtr->PutBatch({ + {KEY_1, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())}, + {KEY_2, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())}, + {KEY_3, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())}, + {KEY_4, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())}, + {KEY_5, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())}, + })); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + /** + * @tc.steps: step4. deviceB has k2k4, has no k1k3k5 + */ + LOGI("============step 4============"); + VirtualDataItem item; + EXPECT_EQ(g_deviceB->GetData(KEY_2, item), E_OK); + EXPECT_EQ(item.value, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())); + EXPECT_EQ(g_deviceB->GetData(KEY_4, item), E_OK); + EXPECT_EQ(item.value, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())); + EXPECT_EQ(g_deviceB->GetData(KEY_1, item), -E_NOT_FOUND); + EXPECT_EQ(g_deviceB->GetData(KEY_3, item), -E_NOT_FOUND); + EXPECT_EQ(g_deviceB->GetData(KEY_5, item), -E_NOT_FOUND); +} + +/** + * @tc.name: subscribeSync004 + * @tc.desc: test subscribe sync with inkeys query + * @tc.type: FUNC + * @tc.require: AR000GOHO7 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeSync004, TestSize.Level1) +{ + /** + * @tc.steps: step1. InitSchemaDb + */ + LOGI("============step 1============"); + InitSubSchemaDb(); + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step2. deviceB subscribe inkeys(k3k5) and equal to query to deviceA + */ + LOGI("============step 2============"); + Query query = Query::Select().InKeys({KEY_3, KEY_5}).EqualTo("$.field_name3", 100); // 100 for test. + g_deviceB->Subscribe(QuerySyncObject(query), true, 2); + + /** + * @tc.steps: step3. deviceA put k1v2,k3v2,k5v1 and wait + */ + LOGI("============step 3============"); + EXPECT_EQ(OK, g_schemaKvDelegatePtr->PutBatch({ + {KEY_1, Value(SCHEMA_VALUE2.begin(), SCHEMA_VALUE2.end())}, + {KEY_3, Value(SCHEMA_VALUE2.begin(), SCHEMA_VALUE2.end())}, + {KEY_5, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())}, + })); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + /** + * @tc.steps: step4. deviceB has k3, has no k1k5 + */ + LOGI("============step 4============"); + VirtualDataItem item; + EXPECT_EQ(g_deviceB->GetData(KEY_3, item), E_OK); + EXPECT_EQ(item.value, Value(SCHEMA_VALUE2.begin(), SCHEMA_VALUE2.end())); + EXPECT_EQ(g_deviceB->GetData(KEY_1, item), -E_NOT_FOUND); + EXPECT_EQ(g_deviceB->GetData(KEY_5, item), -E_NOT_FOUND); +} + +/** + * @tc.name: subscribeSync005 + * @tc.desc: test subscribe sync with inkeys query + * @tc.type: FUNC + * @tc.require: AR000GOHO7 + * @tc.author: lidongwei + */ +HWTEST_F(DistributedDBSingleVerP2PSubscribeSyncTest, subscribeSync005, TestSize.Level1) +{ + /** + * @tc.steps: step1. InitSchemaDb + */ + LOGI("============step 1============"); + InitSubSchemaDb(); + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step2. deviceB subscribe inkeys(k1, key6) and prefix key "k" query to deviceA + */ + LOGI("============step 2============"); + Key key6 { 'k', '6' }; + Query query = Query::Select().InKeys({KEY_1, key6}).PrefixKey({ 'k' }); + g_deviceB->Subscribe(QuerySyncObject(query), true, 3); + + /** + * @tc.steps: step3. deviceA put k1,key6 and wait + */ + LOGI("============step 3============"); + EXPECT_EQ(OK, g_schemaKvDelegatePtr->PutBatch({ + {key6, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())}, + {KEY_1, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())}, + })); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + /** + * @tc.steps: step4. deviceB has key6, has no k1 + */ + LOGI("============step 4============"); + VirtualDataItem item; + EXPECT_EQ(g_deviceB->GetData(key6, item), E_OK); + EXPECT_EQ(item.value, Value(SCHEMA_VALUE1.begin(), SCHEMA_VALUE1.end())); + EXPECT_EQ(g_deviceB->GetData(KEY_1, item), -E_NOT_FOUND); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_check_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_check_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7fc131bef704df645b141f17dc61489865905c25 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_check_test.cpp @@ -0,0 +1,1273 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "kv_virtual_device.h" +#include "platform_specific.h" +#include "process_system_api_adapter_impl.h" +#include "single_ver_data_packet.h" +#include "virtual_communicator_aggregator.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const string STORE_ID = "kv_stroe_sync_check_test"; + const std::string DEVICE_B = "deviceB"; + const std::string DEVICE_C = "deviceC"; + const int LOCAL_WATER_MARK_NOT_INIT = 0xaa; + const int EIGHT_HUNDRED = 800; + const int NORMAL_SYNC_SEND_REQUEST_CNT = 3; + const int TWO_CNT = 2; + const int SLEEP_MILLISECONDS = 500; + const int TEN_SECONDS = 10; + const int THREE_HUNDRED = 300; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + DistributedDBToolsUnitTest g_tool; + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate* g_kvDelegatePtr = nullptr; + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + KvVirtualDevice* g_deviceB = nullptr; + KvVirtualDevice* g_deviceC = nullptr; + VirtualSingleVerSyncDBInterface *g_syncInterfaceB = nullptr; + VirtualSingleVerSyncDBInterface *g_syncInterfaceC = nullptr; + + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); +#ifndef LOW_LEVEL_MEM_DEV + const int KEY_LEN = 20; // 20 Bytes + const int VALUE_LEN = 4 * 1024 * 1024; // 4MB + const int ENTRY_NUM = 2; // 16 entries +#endif +} + +class DistributedDBSingleVerP2PSyncCheckTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSingleVerP2PSyncCheckTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Init datadir and Virtual Communicator. + */ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + string dir = g_testDir + "/single_ver"; + DIR* dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); + + std::shared_ptr g_adapter = std::make_shared(); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(g_adapter); +} + +void DistributedDBSingleVerP2PSyncCheckTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Release virtual Communicator and clear data dir. + */ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); +} + +void DistributedDBSingleVerP2PSyncCheckTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: create virtual device B and C, and get a KvStoreNbDelegate as deviceA + */ + KvStoreNbDelegate::Option option; + option.secOption.securityLabel = SecurityLabel::S3; + option.secOption.securityFlag = SecurityFlag::SECE; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + g_deviceB = new (std::nothrow) KvVirtualDevice(DEVICE_B); + ASSERT_TRUE(g_deviceB != nullptr); + g_syncInterfaceB = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(g_syncInterfaceB != nullptr); + ASSERT_EQ(g_deviceB->Initialize(g_communicatorAggregator, g_syncInterfaceB), E_OK); + + g_deviceC = new (std::nothrow) KvVirtualDevice(DEVICE_C); + ASSERT_TRUE(g_deviceC != nullptr); + g_syncInterfaceC = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(g_syncInterfaceC != nullptr); + ASSERT_EQ(g_deviceC->Initialize(g_communicatorAggregator, g_syncInterfaceC), E_OK); +} + +void DistributedDBSingleVerP2PSyncCheckTest::TearDown(void) +{ + /** + * @tc.teardown: Release device A, B, C + */ + if (g_kvDelegatePtr != nullptr) { + ASSERT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + DBStatus status = g_mgr.DeleteKvStore(STORE_ID); + LOGD("delete kv store status %d", status); + ASSERT_TRUE(status == OK); + } + if (g_deviceB != nullptr) { + delete g_deviceB; + g_deviceB = nullptr; + } + if (g_deviceC != nullptr) { + delete g_deviceC; + g_deviceC = nullptr; + } + if (g_communicatorAggregator != nullptr) { + g_communicatorAggregator->RegOnDispatch(nullptr); + } +} + +/** + * @tc.name: sec option check Sync 001 + * @tc.desc: if sec option not equal, forbid sync + * @tc.type: FUNC + * @tc.require: AR000EV1G6 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, SecOptionCheck001, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + ASSERT_TRUE(g_syncInterfaceB != nullptr); + ASSERT_TRUE(g_syncInterfaceC != nullptr); + SecurityOption secOption{SecurityLabel::S4, SecurityFlag::ECE}; + g_syncInterfaceB->SetSecurityOption(secOption); + g_syncInterfaceC->SetSecurityOption(secOption); + + /** + * @tc.steps: step2. deviceA call sync and wait + * @tc.expected: step2. sync should return SECURITY_OPTION_CHECK_ERROR. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == SECURITY_OPTION_CHECK_ERROR); + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value.empty()); + g_deviceC->GetData(key, item); + EXPECT_TRUE(item.value.empty()); +} + +/** + * @tc.name: sec option check Sync 002 + * @tc.desc: if sec option not equal, forbid sync + * @tc.type: FUNC + * @tc.require: AR000EV1G6 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, SecOptionCheck002, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + ASSERT_TRUE(g_syncInterfaceC != nullptr); + SecurityOption secOption{SecurityLabel::S4, SecurityFlag::ECE}; + g_syncInterfaceC->SetSecurityOption(secOption); + secOption.securityLabel = SecurityLabel::S3; + secOption.securityFlag = SecurityFlag::SECE; + g_syncInterfaceB->SetSecurityOption(secOption); + + /** + * @tc.steps: step2. deviceA call sync and wait + * @tc.expected: step2. sync should return SECURITY_OPTION_CHECK_ERROR. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + if (pair.first == DEVICE_B) { + EXPECT_TRUE(pair.second == OK); + } else { + EXPECT_TRUE(pair.second == SECURITY_OPTION_CHECK_ERROR); + } + } + VirtualDataItem item; + g_deviceC->GetData(key, item); + EXPECT_TRUE(item.value.empty()); + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); +} + +#ifndef LOW_LEVEL_MEM_DEV +/** + * @tc.name: BigDataSync001 + * @tc.desc: big data sync push mode. + * @tc.type: FUNC + * @tc.require: AR000F3OOU + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, BigDataSync001, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put 16 bigData + */ + std::vector entries; + std::vector keys; + DistributedDBUnitTest::GenerateRecords(ENTRY_NUM, entries, keys, KEY_LEN, VALUE_LEN); + for (const auto &entry : entries) { + status = g_kvDelegatePtr->Put(entry.key, entry.value); + ASSERT_TRUE(status == OK); + } + + /** + * @tc.steps: step2. deviceA call sync and wait + * @tc.expected: step2. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step2. onComplete should be called, DeviceB,C have {k1,v1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + for (const auto &entry : entries) { + item.value.clear(); + g_deviceB->GetData(entry.key, item); + EXPECT_TRUE(item.value == entry.value); + item.value.clear(); + g_deviceC->GetData(entry.key, item); + EXPECT_TRUE(item.value == entry.value); + } +} + +/** + * @tc.name: BigDataSync002 + * @tc.desc: big data sync pull mode. + * @tc.type: FUNC + * @tc.require: AR000F3OOU + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, BigDataSync002, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA deviceB put bigData + */ + std::vector entries; + std::vector keys; + DistributedDBUnitTest::GenerateRecords(ENTRY_NUM, entries, keys, KEY_LEN, VALUE_LEN); + + for (uint32_t i = 0; i < entries.size(); i++) { + if (i % 2 == 0) { + g_deviceB->PutData(entries[i].key, entries[i].value, 0, 0); + } else { + g_deviceC->PutData(entries[i].key, entries[i].value, 0, 0); + } + } + + /** + * @tc.steps: step3. deviceA call pull sync + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceA have all bigData + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + for (const auto &entry : entries) { + Value value; + EXPECT_EQ(g_kvDelegatePtr->Get(entry.key, value), OK); + EXPECT_EQ(value, entry.value); + } +} + +/** + * @tc.name: BigDataSync003 + * @tc.desc: big data sync pushAndPull mode. + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, BigDataSync003, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA deviceB put bigData + */ + std::vector entries; + std::vector keys; + DistributedDBUnitTest::GenerateRecords(ENTRY_NUM, entries, keys, KEY_LEN, VALUE_LEN); + + for (uint32_t i = 0; i < entries.size(); i++) { + if (i % 3 == 0) { // 0 3 6 9 12 15 for deivec B + g_deviceB->PutData(entries[i].key, entries[i].value, 0, 0); + } else if (i % 3 == 1) { // 1 4 7 10 13 16 for device C + g_deviceC->PutData(entries[i].key, entries[i].value, 0, 0); + } else { // 2 5 8 11 14 for device A + status = g_kvDelegatePtr->Put(entries[i].key, entries[i].value); + ASSERT_TRUE(status == OK); + } + } + + /** + * @tc.steps: step3. deviceA call pushAndpull sync + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceA have all bigData + * deviceB and deviceC has deviceA data + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + + VirtualDataItem item; + for (uint32_t i = 0; i < entries.size(); i++) { + Value value; + EXPECT_EQ(g_kvDelegatePtr->Get(entries[i].key, value), OK); + EXPECT_EQ(value, entries[i].value); + + if (i % 3 == 2) { // 2 5 8 11 14 for device A + item.value.clear(); + g_deviceB->GetData(entries[i].key, item); + EXPECT_TRUE(item.value == entries[i].value); + item.value.clear(); + g_deviceC->GetData(entries[i].key, item); + EXPECT_TRUE(item.value == entries[i].value); + } + } +} +#endif + +/** + * @tc.name: PushFinishedNotify 001 + * @tc.desc: Test remote device push finished notify function. + * @tc.type: FUNC + * @tc.require: AR000CQS3S + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, PushFinishedNotify001, TestSize.Level1) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA call SetRemotePushFinishedNotify + * @tc.expected: step1. set should return OK. + */ + int pushfinishedFlag = 0; + DBStatus status = g_kvDelegatePtr->SetRemotePushFinishedNotify( + [&pushfinishedFlag](const RemotePushNotifyInfo &info) { + EXPECT_TRUE(info.deviceId == DEVICE_B); + pushfinishedFlag = 1; + }); + ASSERT_EQ(status, OK); + + /** + * @tc.steps: step2. deviceB put k2, v2, and deviceA pull from deviceB + * @tc.expected: step2. deviceA can not receive push finished notify + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_2, VALUE_2), OK); + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result); + EXPECT_TRUE(status == OK); + EXPECT_EQ(pushfinishedFlag, 0); + pushfinishedFlag = 0; + + /** + * @tc.steps: step3. deviceB put k3, v3, and deviceA push and pull to deviceB + * @tc.expected: step3. deviceA can not receive push finished notify + */ + EXPECT_EQ(g_kvDelegatePtr->Put(KEY_3, VALUE_3), OK); + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result); + EXPECT_TRUE(status == OK); + EXPECT_EQ(pushfinishedFlag, 0); + pushfinishedFlag = 0; + + /** + * @tc.steps: step4. deviceA call SetRemotePushFinishedNotify to reset notify + * @tc.expected: step4. set should return OK. + */ + status = g_kvDelegatePtr->SetRemotePushFinishedNotify([&pushfinishedFlag](const RemotePushNotifyInfo &info) { + EXPECT_TRUE(info.deviceId == DEVICE_B); + pushfinishedFlag = 2; + }); + ASSERT_EQ(status, OK); + + /** + * @tc.steps: step5. deviceA call SetRemotePushFinishedNotify set null to unregist + * @tc.expected: step5. set should return OK. + */ + status = g_kvDelegatePtr->SetRemotePushFinishedNotify(nullptr); + ASSERT_EQ(status, OK); +} + +namespace { +void RegOnDispatchWithDelayAck(bool &errCodeAck, bool &afterErrAck) +{ + // just delay the busy ack + g_communicatorAggregator->RegOnDispatch([&errCodeAck, &afterErrAck](const std::string &dev, Message *inMsg) { + if (dev != g_deviceB->GetDeviceId()) { + return; + } + auto *packet = inMsg->GetObject(); + if (packet->GetRecvCode() == -E_BUSY) { + errCodeAck = true; + while (!afterErrAck) { + } + LOGW("NOW SEND BUSY ACK"); + } else if (errCodeAck) { + afterErrAck = true; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + }); +} + +void RegOnDispatchWithOffline(bool &offlineFlag, bool &invalid, condition_variable &conditionOffline) +{ + g_communicatorAggregator->RegOnDispatch([&offlineFlag, &invalid, &conditionOffline]( + const std::string &dev, Message *inMsg) { + auto *packet = inMsg->GetObject(); + if (dev != DEVICE_B) { + if (packet->GetRecvCode() == LOCAL_WATER_MARK_NOT_INIT) { + offlineFlag = true; + conditionOffline.notify_all(); + LOGW("[Dispatch] NOTIFY OFFLINE"); + std::this_thread::sleep_for(std::chrono::microseconds(EIGHT_HUNDRED)); + } + } else if (!invalid && inMsg->GetMessageType() == TYPE_REQUEST) { + LOGW("[Dispatch] NOW INVALID THIS MSG"); + inMsg->SetMessageType(TYPE_INVALID); + inMsg->SetMessageId(INVALID_MESSAGE_ID); + invalid = true; + } + }); +} + +void RegOnDispatchWithInvalidMsg(bool &invalid) +{ + g_communicatorAggregator->RegOnDispatch([&invalid]( + const std::string &dev, Message *inMsg) { + if (dev == DEVICE_B && !invalid && inMsg->GetMessageType() == TYPE_REQUEST) { + LOGW("[Dispatch] NOW INVALID THIS MSG"); + inMsg->SetMessageType(TYPE_INVALID); + inMsg->SetMessageId(INVALID_MESSAGE_ID); + invalid = true; + } + }); +} + +void PrepareEnv(vector &devices, Key &key, Query &query) +{ + /** + * @tc.steps: step1. ensure the watermark is no zero and finish timeSync and abilitySync + * @tc.expected: step1. should return OK. + */ + Value value = {'1'}; + std::map result; + ASSERT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, DistributedDB::SYNC_MODE_PUSH_ONLY, result, query); + EXPECT_TRUE(status == OK); + ASSERT_TRUE(result[g_deviceB->GetDeviceId()] == OK); +} + +void Sync(vector &devices, const DBStatus &targetStatus) +{ + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, DistributedDB::SYNC_MODE_PUSH_ONLY, result); + EXPECT_TRUE(status == OK); + for (const auto &deviceId : devices) { + ASSERT_TRUE(result[deviceId] == targetStatus); + } +} + +void SyncWithQuery(vector &devices, const Query &query, const DBStatus &targetStatus) +{ + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, DistributedDB::SYNC_MODE_PUSH_ONLY, result, query); + EXPECT_TRUE(status == OK); + for (const auto &deviceId : devices) { + ASSERT_TRUE(result[deviceId] == targetStatus); + } +} + +void SyncWithDeviceOffline(vector &devices, Key &key, Query &query) +{ + Value value = {'2'}; + ASSERT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + + /** + * @tc.steps: step2. invalid the sync msg + * @tc.expected: step2. should return TIME_OUT. + */ + SyncWithQuery(devices, query, TIME_OUT); + + /** + * @tc.steps: step3. device offline when sync + * @tc.expected: step3. should return COMM_FAILURE. + */ + SyncWithQuery(devices, query, COMM_FAILURE); +} +} + +/** + * @tc.name: AckSessionCheck 001 + * @tc.desc: Test ack session check function. + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, AckSessionCheck001, TestSize.Level3) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. deviceB sync to deviceA just for timeSync and abilitySync + * @tc.expected: step1. should return OK. + */ + std::map result; + ASSERT_TRUE(g_deviceB->Sync(SYNC_MODE_PUSH_ONLY, true) == OK); + + /** + * @tc.steps: step2. deviceA StartTransaction for prevent other sync action deviceB sync will fail + * @tc.expected: step2. should return OK. + */ + ASSERT_TRUE(g_kvDelegatePtr->StartTransaction() == OK); + + bool errCodeAck = false; + bool afterErrAck = false; + RegOnDispatchWithDelayAck(errCodeAck, afterErrAck); + + Key key = {'1'}; + Value value = {'1'}; + Timestamp currentTime; + (void)OS::GetCurrentSysTimeInMicrosecond(currentTime); + EXPECT_TRUE(g_deviceB->PutData(key, value, currentTime, 0) == OK); + EXPECT_TRUE(g_deviceB->Sync(SYNC_MODE_PUSH_ONLY, true) == OK); + + Value outValue; + EXPECT_TRUE(g_kvDelegatePtr->Get(key, outValue) == NOT_FOUND); + + /** + * @tc.steps: step3. release the writeHandle and try again, sync success + * @tc.expected: step3. should return OK. + */ + EXPECT_TRUE(g_kvDelegatePtr->Commit() == OK); + EXPECT_TRUE(g_deviceB->Sync(SYNC_MODE_PUSH_ONLY, true) == OK); + + EXPECT_TRUE(g_kvDelegatePtr->Get(key, outValue) == E_OK); + EXPECT_EQ(outValue, value); +} + +/** + * @tc.name: AckSafeCheck001 + * @tc.desc: Test ack session check filter all bad ack in device offline scene. + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, AckSafeCheck001, TestSize.Level3) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + g_deviceB->Online(); + + Key key = {'1'}; + Query query = Query::Select().PrefixKey(key); + PrepareEnv(devices, key, query); + + std::condition_variable conditionOnline; + std::condition_variable conditionOffline; + bool onlineFlag = false; + bool invalid = false; + bool offlineFlag = false; + thread subThread([&onlineFlag, &conditionOnline, &offlineFlag, &conditionOffline]() { + LOGW("[Dispatch] NOW DEVICES IS OFFLINE"); + std::mutex offlineMtx; + std::unique_lock lck(offlineMtx); + conditionOffline.wait(lck, [&offlineFlag]{ return offlineFlag; }); + g_deviceB->Offline(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + g_deviceB->Online(); + onlineFlag = true; + conditionOnline.notify_all(); + LOGW("[Dispatch] NOW DEVICES IS ONLINE"); + }); + subThread.detach(); + + RegOnDispatchWithOffline(offlineFlag, invalid, conditionOffline); + + SyncWithDeviceOffline(devices, key, query); + + std::mutex onlineMtx; + std::unique_lock lck(onlineMtx); + conditionOnline.wait(lck, [&onlineFlag]{ return onlineFlag; }); + + /** + * @tc.steps: step4. sync again if has problem it will sync never end + * @tc.expected: step4. should return OK. + */ + SyncWithQuery(devices, query, OK); +} + + +/** + * @tc.name: WaterMarkCheck001 + * @tc.desc: Test waterMark work correct in lost package scene. + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, WaterMarkCheck001, TestSize.Level1) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + g_deviceB->Online(); + + Key key = {'1'}; + Query query = Query::Select().PrefixKey(key); + PrepareEnv(devices, key, query); + + /** + * @tc.steps: step2. query sync and set queryWaterMark + * @tc.expected: step2. should return OK. + */ + Value value = {'2'}; + ASSERT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + SyncWithQuery(devices, query, OK); + + /** + * @tc.steps: step3. sync and invalid msg for set local device waterMark + * @tc.expected: step3. should return TIME_OUT. + */ + bool invalidMsg = false; + RegOnDispatchWithInvalidMsg(invalidMsg); + value = {'3'}; + ASSERT_TRUE(g_kvDelegatePtr->Put(key, value) == OK); + Sync(devices, TIME_OUT); + + /** + * @tc.steps: step4. sync again see it work correct + * @tc.expected: step4. should return OK. + */ + SyncWithQuery(devices, query, OK); +} + +void RegOnDispatchToGetSyncCount(int &sendRequestCount, int sleepMs = 0) +{ + g_communicatorAggregator->RegOnDispatch([sleepMs, &sendRequestCount]( + const std::string &dev, Message *inMsg) { + if (dev == DEVICE_B && inMsg->GetMessageType() == TYPE_REQUEST) { + std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs)); + sendRequestCount++; + LOGD("sendRequestCount++..."); + } + }); +} + +void TestDifferentSyncMode(SyncMode mode) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + DBStatus status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + int sendRequestCount = 0; + RegOnDispatchToGetSyncCount(sendRequestCount); + + /** + * @tc.steps: step2. deviceA call sync and wait + * @tc.expected: step2. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, mode, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step2. onComplete should be called, DeviceB have {k1,v1}, send request message 3 times + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); + + EXPECT_EQ(sendRequestCount, NORMAL_SYNC_SEND_REQUEST_CNT); + + /** + * @tc.steps: step3. reset sendRequestCount to 0, deviceA call sync and wait again without any change in db + * @tc.expected: step3. sync should return OK, and sendRequestCount should be 1, because this merge can not + * be skipped + */ + sendRequestCount = 0; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + EXPECT_EQ(sendRequestCount, 1); +} + +/** + * @tc.name: PushSyncMergeCheck001 + * @tc.desc: Test push sync task merge, task can not be merged when the two sync task is not in the queue + * at the same time. + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, SyncMergeCheck001, TestSize.Level1) +{ + TestDifferentSyncMode(SYNC_MODE_PUSH_ONLY); +} + +/** + * @tc.name: PushSyncMergeCheck002 + * @tc.desc: Test push_pull sync task merge, task can not be merged when the two sync task is not in the queue + * at the same time. + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, SyncMergeCheck002, TestSize.Level1) +{ + TestDifferentSyncMode(SYNC_MODE_PUSH_PULL); +} + +void PrepareForSyncMergeTest(std::vector &devices, int &sendRequestCount) +{ + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + DBStatus status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + RegOnDispatchToGetSyncCount(sendRequestCount, SLEEP_MILLISECONDS); + + /** + * @tc.steps: step2. deviceA call sync and don't wait + * @tc.expected: step2. sync should return OK. + */ + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, + [&sendRequestCount, devices, key, value](const std::map& statusMap) { + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &pair : statusMap) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_EQ(item.value, value); + EXPECT_EQ(sendRequestCount, NORMAL_SYNC_SEND_REQUEST_CNT); + + // reset sendRequestCount to 0 + sendRequestCount = 0; + }); + ASSERT_TRUE(status == OK); +} + +/** + * @tc.name: PushSyncMergeCheck003 + * @tc.desc: Test push sync task merge, task can not be merged when there is change in db since last push sync + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, SyncMergeCheck003, TestSize.Level3) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + int sendRequestCount = 0; + PrepareForSyncMergeTest(devices, sendRequestCount); + + /** + * @tc.steps: step3. deviceA call sync and don't wait + * @tc.expected: step3. sync should return OK. + */ + Key key = {'1'}; + Value value = {'2'}; + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, + [&sendRequestCount, devices, key, value, this](const std::map& statusMap) { + /** + * @tc.expected: when the second sync task return, sendRequestCount should be 1, because this merge can not be + * skipped, but it is no need to do time sync and ability sync, only need to do data sync + */ + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &pair : statusMap) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_EQ(item.value, value); + }); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step4. deviceA put {k1, v2} + */ + while (sendRequestCount < TWO_CNT) { + std::this_thread::sleep_for(std::chrono::milliseconds(THREE_HUNDRED)); + } + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + // wait for the second sync task finish + std::this_thread::sleep_for(std::chrono::seconds(TEN_SECONDS)); + EXPECT_EQ(sendRequestCount, 1); +} + +/** + * @tc.name: PushSyncMergeCheck004 + * @tc.desc: Test push sync task merge, task can be merged when there is no change in db since last push sync + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, SyncMergeCheck004, TestSize.Level3) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + int sendRequestCount = 0; + PrepareForSyncMergeTest(devices, sendRequestCount); + + /** + * @tc.steps: step3. deviceA call sync and don't wait + * @tc.expected: step3. sync should return OK. + */ + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, + [devices, this](const std::map& statusMap) { + /** + * @tc.expected: when the second sync task return, sendRequestCount should be 0, because this merge can be + * skipped + */ + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &pair : statusMap) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + }); + ASSERT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::seconds(TEN_SECONDS)); + EXPECT_EQ(sendRequestCount, 0); +} + +void RegOnDispatchWithInvalidMsgAndCnt(int &sendRequestCount, int sleepMs, bool &invalid) +{ + g_communicatorAggregator->RegOnDispatch([&sendRequestCount, sleepMs, &invalid]( + const std::string &dev, Message *inMsg) { + if (dev == DEVICE_B && !invalid && inMsg->GetMessageType() == TYPE_REQUEST) { + inMsg->SetMessageType(TYPE_INVALID); + inMsg->SetMessageId(INVALID_MESSAGE_ID); + sendRequestCount++; + invalid = true; + LOGW("[Dispatch]invalid THIS MSG, sendRequestCount = %d", sendRequestCount); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs)); + } + }); +} + +/** + * @tc.name: PushSyncMergeCheck005 + * @tc.desc: Test push sync task merge, task cannot be merged when the last push sync is failed + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, SyncMergeCheck005, TestSize.Level3) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + int sendRequestCount = 0; + bool invalid = false; + RegOnDispatchWithInvalidMsgAndCnt(sendRequestCount, SLEEP_MILLISECONDS, invalid); + + /** + * @tc.steps: step2. deviceA call sync and don't wait + * @tc.expected: step2. sync should return TIME_OUT. + */ + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, + [&sendRequestCount, devices, this](const std::map& statusMap) { + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &deviceId : devices) { + ASSERT_EQ(statusMap.at(deviceId), TIME_OUT); + } + }); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call sync and don't wait + * @tc.expected: step3. sync should return OK. + */ + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, + [key, value, &sendRequestCount, devices, this](const std::map& statusMap) { + /** + * @tc.expected: when the second sync task return, sendRequestCount should be 3, because this merge can not be + * skipped, deviceB should have {k1, v1}. + */ + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &pair : statusMap) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_EQ(pair.second, OK); + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_EQ(item.value, value); + }); + ASSERT_TRUE(status == OK); + while (sendRequestCount < 1) { + std::this_thread::sleep_for(std::chrono::milliseconds(THREE_HUNDRED)); + } + sendRequestCount = 0; + RegOnDispatchToGetSyncCount(sendRequestCount, SLEEP_MILLISECONDS); + + // wait for the second sync task finish + std::this_thread::sleep_for(std::chrono::seconds(TEN_SECONDS)); + EXPECT_EQ(sendRequestCount, NORMAL_SYNC_SEND_REQUEST_CNT); +} + +void PrePareForQuerySyncMergeTest(bool isQuerySync, std::vector &devices, + Key &key, Value &value, int &sendRequestCount) +{ + DBStatus status = OK; + /** + * @tc.steps: step1. deviceA put {k1, v1}...{k10, v10} + */ + Query query = Query::Select().PrefixKey(key); + const int dataSize = 10; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + value.push_back(i); + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + key.pop_back(); + value.pop_back(); + } + + RegOnDispatchToGetSyncCount(sendRequestCount, SLEEP_MILLISECONDS); + /** + * @tc.steps: step2. deviceA call query sync and don't wait + * @tc.expected: step2. sync should return OK. + */ + auto completeCallBack = [&sendRequestCount, &key, &value, dataSize, devices] + (const std::map& statusMap) { + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &pair : statusMap) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_EQ(pair.second, OK); + } + // when first sync finish, DeviceB have {k1,v1}, {k3,v3}, {k5,v5} .. send request message 3 times + VirtualDataItem item; + for (int i = 0; i < dataSize; i++) { + key.push_back(i); + value.push_back(i); + g_deviceB->GetData(key, item); + EXPECT_EQ(item.value, value); + key.pop_back(); + value.pop_back(); + } + EXPECT_EQ(sendRequestCount, NORMAL_SYNC_SEND_REQUEST_CNT); + // reset sendRequestCount to 0 + sendRequestCount = 0; + }; + if (isQuerySync) { + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, completeCallBack, query, false); + } else { + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, completeCallBack); + } + ASSERT_TRUE(status == OK); +} + +/** + * @tc.name: QuerySyncMergeCheck001 + * @tc.desc: Test query push sync task merge, task can be merged when there is no change in db since last query sync + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, QuerySyncMergeCheck001, TestSize.Level3) +{ + std::vector devices; + int sendRequestCount = 0; + devices.push_back(g_deviceB->GetDeviceId()); + + Key key{'1'}; + Value value{'1'}; + Query query = Query::Select().PrefixKey(key); + PrePareForQuerySyncMergeTest(true, devices, key, value, sendRequestCount); + + /** + * @tc.steps: step3. deviceA call query sync and don't wait + * @tc.expected: step3. sync should return OK. + */ + DBStatus status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, + [devices, this](const std::map& statusMap) { + /** + * @tc.expected: when the second sync task return, sendRequestCount should be 0, because this merge can be + * skipped because there is no change in db since last query sync + */ + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &pair : statusMap) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + }, query, false); + ASSERT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::seconds(TEN_SECONDS)); + EXPECT_EQ(sendRequestCount, 0); +} + +/** + * @tc.name: QuerySyncMergeCheck002 + * @tc.desc: Test query push sync task merge, task can not be merged when there is change in db since last sync + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, QuerySyncMergeCheck002, TestSize.Level3) +{ + std::vector devices; + int sendRequestCount = 0; + devices.push_back(g_deviceB->GetDeviceId()); + + Key key{'1'}; + Value value{'1'}; + Query query = Query::Select().PrefixKey(key); + PrePareForQuerySyncMergeTest(true, devices, key, value, sendRequestCount); + + /** + * @tc.steps: step3. deviceA call query sync and don't wait + * @tc.expected: step3. sync should return OK. + */ + Value value3{'3'}; + DBStatus status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, + [&sendRequestCount, devices, key, value3, this](const std::map& statusMap) { + /** + * @tc.expected: when the second sync task return, sendRequestCount should be 1, because this merge can not be + * skipped when there is change in db since last query sync, deviceB have {k1, v1'} + */ + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &pair : statusMap) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value3); + EXPECT_EQ(sendRequestCount, 1); + }, query, false); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step4. deviceA put {k1, v1'} + * @tc.steps: step4. reset sendRequestCount to 0, deviceA call sync and wait + * @tc.expected: step4. sync should return OK, and sendRequestCount should be 1, because this merge can not + * be skipped + */ + while (sendRequestCount < TWO_CNT) { + std::this_thread::sleep_for(std::chrono::milliseconds(THREE_HUNDRED)); + } + g_kvDelegatePtr->Put(key, value3); + std::this_thread::sleep_for(std::chrono::seconds(TEN_SECONDS)); +} + +/** + * @tc.name: QuerySyncMergeCheck003 + * @tc.desc: Test query push sync task merge, task can not be merged when then query id is different + * @tc.type: FUNC + * @tc.require: AR000F3OOV + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, QuerySyncMergeCheck003, TestSize.Level3) +{ + std::vector devices; + int sendRequestCount = 0; + devices.push_back(g_deviceB->GetDeviceId()); + + Key key{'1'}; + Value value{'1'}; + PrePareForQuerySyncMergeTest(true, devices, key, value, sendRequestCount); + + /** + * @tc.steps: step3. deviceA call another query sync + * @tc.expected: step3. sync should return OK. + */ + Key key2 = {'2'}; + Value value2 = {'2'}; + DBStatus status = g_kvDelegatePtr->Put(key2, value2); + ASSERT_TRUE(status == OK); + Query query2 = Query::Select().PrefixKey(key2); + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, + [&sendRequestCount, key2, value2, devices, this](const std::map& statusMap) { + /** + * @tc.expected: when the second sync task return, sendRequestCount should be 1, because this merge can not be + * skipped, deviceB have {k2,v2} + */ + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &pair : statusMap) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + g_deviceB->GetData(key2, item); + EXPECT_TRUE(item.value == value2); + EXPECT_EQ(sendRequestCount, 1); + }, query2, false); + ASSERT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::seconds(TEN_SECONDS)); +} + +/** +* @tc.name: QuerySyncMergeCheck004 +* @tc.desc: Test query push sync task merge, task can be merged when there is no change in db since last push sync +* @tc.type: FUNC +* @tc.require: AR000F3OOV +* @tc.author: zhangshijie +*/ +HWTEST_F(DistributedDBSingleVerP2PSyncCheckTest, QuerySyncMergeCheck004, TestSize.Level3) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + Key key{'1'}; + Value value{'1'}; + int sendRequestCount = 0; + PrePareForQuerySyncMergeTest(false, devices, key, value, sendRequestCount); + + /** + * @tc.steps: step3. deviceA call query sync without any change in db + * @tc.expected: step3. sync should return OK, and sendRequestCount should be 0, because this merge can be skipped + */ + Query query = Query::Select().PrefixKey(key); + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, + [devices, this](const std::map& statusMap) { + /** + * @tc.expected step3: when the second sync task return, sendRequestCount should be 0, because this merge + * can be skipped because there is no change in db since last push sync + */ + ASSERT_TRUE(statusMap.size() == devices.size()); + for (const auto &pair : statusMap) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + }, query, false); + ASSERT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::seconds(TEN_SECONDS)); + EXPECT_EQ(sendRequestCount, 0); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bcc74326c5fe4a54c4bac2bc8434491d9fe3ad8b --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_test.cpp @@ -0,0 +1,2290 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "db_constant.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "kv_store_nb_delegate.h" +#include "kv_virtual_device.h" +#include "platform_specific.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { + string g_testDir; + const string STORE_ID = "kv_stroe_sync_test"; + const int64_t TIME_OFFSET = 5000000; + const int WAIT_TIME = 1000; + const int WAIT_5_SECONDS = 5000; + const int WAIT_30_SECONDS = 30000; + const int WAIT_36_SECONDS = 36000; + const std::string DEVICE_B = "deviceB"; + const std::string DEVICE_C = "deviceC"; + + KvStoreDelegateManager g_mgr(APP_ID, USER_ID); + KvStoreConfig g_config; + DistributedDBToolsUnitTest g_tool; + DBStatus g_kvDelegateStatus = INVALID_ARGS; + KvStoreNbDelegate* g_kvDelegatePtr = nullptr; + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + KvVirtualDevice *g_deviceB = nullptr; + KvVirtualDevice *g_deviceC = nullptr; + + // the type of g_kvDelegateCallback is function + auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback, + placeholders::_1, placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr)); +} + +class DistributedDBSingleVerP2PSyncTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSingleVerP2PSyncTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Init datadir and Virtual Communicator. + */ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + g_config.dataDir = g_testDir; + g_mgr.SetKvStoreConfig(g_config); + + string dir = g_testDir + "/single_ver"; + DIR* dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + OS::MakeDBDirectory(dir); + } else { + closedir(dirTmp); + } + + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); +} + +void DistributedDBSingleVerP2PSyncTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: Release virtual Communicator and clear data dir. + */ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); +} + +void DistributedDBSingleVerP2PSyncTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: create virtual device B and C, and get a KvStoreNbDelegate as deviceA + */ + KvStoreNbDelegate::Option option; + g_mgr.GetKvStore(STORE_ID, option, g_kvDelegateCallback); + ASSERT_TRUE(g_kvDelegateStatus == OK); + ASSERT_TRUE(g_kvDelegatePtr != nullptr); + g_deviceB = new (std::nothrow) KvVirtualDevice(DEVICE_B); + ASSERT_TRUE(g_deviceB != nullptr); + VirtualSingleVerSyncDBInterface *syncInterfaceB = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(syncInterfaceB != nullptr); + ASSERT_EQ(g_deviceB->Initialize(g_communicatorAggregator, syncInterfaceB), E_OK); + + g_deviceC = new (std::nothrow) KvVirtualDevice(DEVICE_C); + ASSERT_TRUE(g_deviceC != nullptr); + VirtualSingleVerSyncDBInterface *syncInterfaceC = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(syncInterfaceC != nullptr); + ASSERT_EQ(g_deviceC->Initialize(g_communicatorAggregator, syncInterfaceC), E_OK); + + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + return true;}; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); +} + +void DistributedDBSingleVerP2PSyncTest::TearDown(void) +{ + /** + * @tc.teardown: Release device A, B, C + */ + if (g_kvDelegatePtr != nullptr) { + ASSERT_EQ(g_mgr.CloseKvStore(g_kvDelegatePtr), OK); + g_kvDelegatePtr = nullptr; + DBStatus status = g_mgr.DeleteKvStore(STORE_ID); + LOGD("delete kv store status %d", status); + ASSERT_TRUE(status == OK); + } + if (g_deviceB != nullptr) { + delete g_deviceB; + g_deviceB = nullptr; + } + if (g_deviceC != nullptr) { + delete g_deviceC; + g_deviceC = nullptr; + } + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** + * @tc.name: Normal Sync 001 + * @tc.desc: Test normal push sync for add data. + * @tc.type: FUNC + * @tc.require: AR000CQS3S SR000CQE0B + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, NormalSync001, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceA call sync and wait + * @tc.expected: step2. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step2. onComplete should be called, DeviceB,C have {k1,v1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); + g_deviceC->GetData(key, item); + EXPECT_TRUE(item.value == value); +} + +/** + * @tc.name: Normal Sync 002 + * @tc.desc: Test normal push sync for update data. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, NormalSync002, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceA put {k1, v2} + */ + Value value2; + value2.push_back('2'); + status = g_kvDelegatePtr->Put(key, value2); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call sync and wait + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceB,C have {k1,v2} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + g_deviceC->GetData(key, item); + EXPECT_TRUE(item.value == value2); + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value2); +} + +/** + * @tc.name: Normal Sync 003 + * @tc.desc: Test normal push sync for delete data. + * @tc.type: FUNC + * @tc.require: AR000CQS3S + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, NormalSync003, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceA delete k1 + */ + status = g_kvDelegatePtr->Delete(key); + ASSERT_TRUE(status == OK); + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call sync and wait + * @tc.expected: step3. sync should return OK. + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + + /** + * @tc.expected: step3. onComplete should be called, DeviceB,C have {k1, delete} + */ + VirtualDataItem item; + Key hashKey; + DistributedDBToolsUnitTest::CalcHash(key, hashKey); + EXPECT_EQ(g_deviceB->GetData(hashKey, item), E_OK); + EXPECT_TRUE(item.flag != 0); + g_deviceC->GetData(hashKey, item); + EXPECT_TRUE(item.flag != 0); +} + +/** + * @tc.name: Normal Sync 004 + * @tc.desc: Test normal pull sync for add data. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, NormalSync004, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceB put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + g_deviceB->PutData(key, value, 0, 0); + + /** + * @tc.steps: step2. deviceB put {k2, v2} + */ + Key key2 = {'2'}; + Value value2 = {'2'}; + g_deviceC->PutData(key2, value2, 0, 0); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call pull sync + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceA have {k1, VALUE_1}, {K2. VALUE_2} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + Value value3; + EXPECT_EQ(g_kvDelegatePtr->Get(key, value3), OK); + EXPECT_EQ(value3, value); + EXPECT_EQ(g_kvDelegatePtr->Get(key2, value3), OK); + EXPECT_EQ(value3, value2); +} + +/** + * @tc.name: Normal Sync 005 + * @tc.desc: Test normal pull sync for update data. + * @tc.type: FUNC + * @tc.require: AR000CCPOM SR000CQE10 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, NormalSync005, TestSize.Level2) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1}, {k2, v2} t1 + */ + Key key1 = {'1'}; + Value value1 = {'1'}; + status = g_kvDelegatePtr->Put(key1, value1); + ASSERT_TRUE(status == OK); + Key key2 = {'2'}; + Value value2 = {'2'}; + status = g_kvDelegatePtr->Put(key2, value2); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceB put {k1, v3} t2, t2 > t1 + */ + Value value3; + value3.push_back('3'); + g_deviceB->PutData(key1, value3, + TimeHelper::GetSysCurrentTime() + g_deviceB->GetLocalTimeOffset() + TIME_OFFSET, 0); + + /** + * @tc.steps: step3. deviceC put {k2, v4} t2, t4 < t1 + */ + Value value4; + value4.push_back('4'); + g_deviceC->PutData(key2, value4, + TimeHelper::GetSysCurrentTime() + g_deviceC->GetLocalTimeOffset() - TIME_OFFSET, 0); + + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step4. deviceA call pull sync + * @tc.expected: step4. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step4. onComplete should be called, DeviceA have {k1, v3}, {k2. v2} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + + Value value5; + g_kvDelegatePtr->Get(key1, value5); + EXPECT_TRUE(value5 == value3); + g_kvDelegatePtr->Get(key2, value5); + EXPECT_TRUE(value5 == value2); +} + +/** + * @tc.name: Normal Sync 006 + * @tc.desc: Test normal pull sync for delete data. + * @tc.type: FUNC + * @tc.require: AR000CQS3S + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, NormalSync006, TestSize.Level2) +{ + /** + * @tc.steps: step1. deviceA put {k1, v1}, {k2, v2} t1 + */ + Key key1 = {'1'}; + Value value1 = {'1'}; + DBStatus status = g_kvDelegatePtr->Put(key1, value1); + ASSERT_TRUE(status == OK); + Key key2 = {'2'}; + Value value2 = {'2'}; + status = g_kvDelegatePtr->Put(key2, value2); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceA put {k1, delete} t2, t2 PutData(hashKey1, value1, + TimeHelper::GetSysCurrentTime() + g_deviceB->GetLocalTimeOffset() + TIME_OFFSET, 1); + + /** + * @tc.steps: step3. deviceA put {k1, delete} t3, t3 < t1 + */ + Key hashKey2; + DistributedDBToolsUnitTest::CalcHash(key2, hashKey2); + g_deviceC->PutData(hashKey2, value1, + TimeHelper::GetSysCurrentTime() + g_deviceC->GetLocalTimeOffset() - TIME_OFFSET, 0); + + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step4. deviceA call pull sync + * @tc.expected: step4. sync should return OK. + */ + std::map result; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step4. onComplete should be called, DeviceA have {k2. v2} don't have k1 + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + Value value5; + g_kvDelegatePtr->Get(key1, value5); + EXPECT_TRUE(value5.empty()); + g_kvDelegatePtr->Get(key2, value5); + EXPECT_TRUE(value5 == value2); +} + +/** + * @tc.name: Normal Sync 007 + * @tc.desc: Test normal push_pull sync for add data. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, NormalSync007, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key1 = {'1'}; + Value value1 = {'1'}; + status = g_kvDelegatePtr->Put(key1, value1); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step1. deviceB put {k2, v2} + */ + Key key2 = {'2'}; + Value value2 = {'2'}; + g_deviceB->PutData(key2, value2, 0, 0); + + /** + * @tc.steps: step1. deviceB put {k3, v3} + */ + Key key3 = {'3'}; + Value value3 = {'3'}; + g_deviceC->PutData(key3, value3, 0, 0); + + /** + * @tc.steps: step4. deviceA call push_pull sync + * @tc.expected: step4. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result); + ASSERT_TRUE(status == OK); + + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + + /** + * @tc.expected: step4. onComplete should be called, DeviceA have {k1. v1}, {k2, v2}, {k3, v3} + * deviceB received {k1. v1}, don't received k3, deviceC received {k1. v1}, don't received k2 + */ + Value value4; + g_kvDelegatePtr->Get(key2, value4); + EXPECT_TRUE(value4 == value2); + g_kvDelegatePtr->Get(key3, value4); + EXPECT_TRUE(value4 == value3); + + VirtualDataItem item1; + g_deviceB->GetData(key1, item1); + EXPECT_TRUE(item1.value == value1); + item1.value.clear(); + g_deviceB->GetData(key3, item1); + EXPECT_TRUE(item1.value.empty()); + + VirtualDataItem item2; + g_deviceC->GetData(key1, item2); + EXPECT_TRUE(item2.value == value1); + item2.value.clear(); + g_deviceC->GetData(key2, item2); + EXPECT_TRUE(item2.value.empty()); +} + +/** + * @tc.name: Normal Sync 008 + * @tc.desc: Test normal push_pull sync for update data. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, NormalSync008, TestSize.Level2) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1}, {k2, v2} t1 + */ + Key key1 = {'1'}; + Value value1 = {'1'}; + status = g_kvDelegatePtr->Put(key1, value1); + ASSERT_TRUE(status == OK); + + Key key2 = {'2'}; + Value value2 = {'2'}; + status = g_kvDelegatePtr->Put(key2, value2); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceB put {k1, v3} t2, t2 > t1 + */ + Value value3 = {'3'}; + g_deviceB->PutData(key1, value3, + TimeHelper::GetSysCurrentTime() + g_deviceB->GetLocalTimeOffset() + TIME_OFFSET, 0); + + /** + * @tc.steps: step3. deviceB put {k1, v4} t3, t4 PutData(key2, value4, + TimeHelper::GetSysCurrentTime() + g_deviceC->GetLocalTimeOffset() - TIME_OFFSET, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + + /** + * @tc.steps: step4. deviceA call push_pull sync + * @tc.expected: step4. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result); + ASSERT_TRUE(status == OK); + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + + /** + * @tc.expected: step4. onComplete should be called, DeviceA have {k1. v3}, {k2, v2} + * deviceB have {k1. v3}, deviceC have {k2. v2} + */ + Value value5; + g_kvDelegatePtr->Get(key1, value5); + EXPECT_EQ(value5, value3); + g_kvDelegatePtr->Get(key2, value5); + EXPECT_EQ(value5, value2); + + VirtualDataItem item1; + g_deviceB->GetData(key1, item1); + EXPECT_TRUE(item1.value == value3); + item1.value.clear(); + g_deviceB->GetData(key2, item1); + EXPECT_TRUE(item1.value == value2); + + VirtualDataItem item2; + g_deviceC->GetData(key2, item2); + EXPECT_TRUE(item2.value == value2); +} + +/** + * @tc.name: Normal Sync 009 + * @tc.desc: Test normal push_pull sync for delete data. + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, NormalSync009, TestSize.Level2) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1}, {k2, v2} t1 + */ + Key key1 = {'1'}; + Value value1 = {'1'}; + status = g_kvDelegatePtr->Put(key1, value1); + ASSERT_TRUE(status == OK); + + Key key2 = {'2'}; + Value value2 = {'2'}; + status = g_kvDelegatePtr->Put(key2, value2); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceB put {k1, delete} t2, t2 > t1 + */ + Key hashKey1; + DistributedDBToolsUnitTest::CalcHash(key1, hashKey1); + g_deviceB->PutData(hashKey1, value1, + TimeHelper::GetSysCurrentTime() + g_deviceB->GetLocalTimeOffset() + TIME_OFFSET, 1); + + /** + * @tc.steps: step3. deviceB put {k1, delete} t3, t2 < t1 + */ + Key hashKey2; + DistributedDBToolsUnitTest::CalcHash(key2, hashKey2); + g_deviceC->PutData(hashKey2, value2, + TimeHelper::GetSysCurrentTime() + g_deviceC->GetLocalTimeOffset() - TIME_OFFSET, 1); + + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step4. deviceA call push_pull sync + * @tc.expected: step4. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step4. onComplete should be called, DeviceA have {k1. delete}, {k2, v2} + * deviceB have {k2. v2}, deviceC have {k2. v2} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + + Value value3; + g_kvDelegatePtr->Get(key1, value3); + EXPECT_TRUE(value3.empty()); + value3.clear(); + g_kvDelegatePtr->Get(key2, value3); + EXPECT_TRUE(value3 == value2); + + VirtualDataItem item1; + g_deviceB->GetData(key2, item1); + EXPECT_TRUE(item1.value == value2); + + VirtualDataItem item2; + g_deviceC->GetData(key2, item2); + EXPECT_TRUE(item2.value == value2); +} + +/** + * @tc.name: Limit Data Sync 001 + * @tc.desc: Test sync limit key and value data + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, LimitDataSync001, TestSize.Level1) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + Key key1; + Value value1; + DistributedDBToolsUnitTest::GetRandomKeyValue(key1, DBConstant::MAX_KEY_SIZE + 1); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, DBConstant::MAX_VALUE_SIZE + 1); + + Key key2; + Value value2; + DistributedDBToolsUnitTest::GetRandomKeyValue(key2, DBConstant::MAX_KEY_SIZE); + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, DBConstant::MAX_VALUE_SIZE); + + /** + * @tc.steps: step1. deviceB put {k1, v1}, K1 > 1k, v1 > 4M + */ + g_deviceB->PutData(key1, value1, 0, 0); + + /** + * @tc.steps: step2. deviceB put {k2, v2}, K2 = 1k, v2 = 4M + */ + g_deviceC->PutData(key2, value2, 0, 0); + + /** + * @tc.steps: step3. deviceA call pull sync from device B + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called. + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + if (pair.first == g_deviceB->GetDeviceId()) { + EXPECT_TRUE(pair.second != OK); + } else { + EXPECT_TRUE(pair.second == OK); + } + } + + /** + * @tc.steps: step4. deviceA call pull sync from deviceC + * @tc.expected: step4. sync should return OK. + */ + devices.clear(); + result.clear(); + devices.push_back(g_deviceC->GetDeviceId()); + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step4. onComplete should be called, DeviceA have {k2. v2}, don't have {k1, v1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + + // Get value from A + Value valueRead; + EXPECT_TRUE(g_kvDelegatePtr->Get(key1, valueRead) != OK); + valueRead.clear(); + EXPECT_EQ(g_kvDelegatePtr->Get(key2, valueRead), OK); + EXPECT_TRUE(valueRead == value2); +} + +/** + * @tc.name: Device Offline Sync 001 + * @tc.desc: Test push sync when device offline + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, DeviceOfflineSync001, TestSize.Level1) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1}, {k2, v2}, {k3 delete}, {k4,v2} + */ + Key key1 = {'1'}; + Value value1 = {'1'}; + ASSERT_TRUE(g_kvDelegatePtr->Put(key1, value1) == OK); + + Key key2 = {'2'}; + Value value2 = {'2'}; + ASSERT_TRUE(g_kvDelegatePtr->Put(key2, value2) == OK); + + Key key3 = {'3'}; + Value value3 = {'3'}; + ASSERT_TRUE(g_kvDelegatePtr->Put(key3, value3) == OK); + ASSERT_TRUE(g_kvDelegatePtr->Delete(key3) == OK); + + Key key4 = {'4'}; + Value value4 = {'4'}; + ASSERT_TRUE(g_kvDelegatePtr->Put(key4, value4) == OK); + + /** + * @tc.steps: step2. deviceB offline + */ + g_deviceB->Offline(); + + /** + * @tc.steps: step3. deviceA call pull sync + * @tc.expected: step3. sync should return OK. + */ + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceB status is timeout + * deviceC has {k1, v1}, {k2, v2}, {k3 delete}, {k4,v4} + */ + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + if (pair.first == DEVICE_B) { + EXPECT_TRUE(pair.second == COMM_FAILURE); + } else { + EXPECT_TRUE(pair.second == OK); + } + } + VirtualDataItem item; + g_deviceC->GetData(key1, item); + EXPECT_TRUE(item.value == value1); + item.value.clear(); + g_deviceC->GetData(key2, item); + EXPECT_TRUE(item.value == value2); + item.value.clear(); + Key hashKey; + DistributedDBToolsUnitTest::CalcHash(key3, hashKey); + g_deviceC->GetData(hashKey, item); + EXPECT_TRUE((item.flag & VirtualDataItem::DELETE_FLAG) == 1); + item.value.clear(); + g_deviceC->GetData(key4, item); + EXPECT_TRUE(item.value == value4); +} + +/** + * @tc.name: Device Offline Sync 002 + * @tc.desc: Test pull sync when device offline + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, DeviceOfflineSync002, TestSize.Level1) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceB put {k1, v1} + */ + Key key1 = {'1'}; + Value value1 = {'1'}; + g_deviceB->PutData(key1, value1, 0, 0); + + /** + * @tc.steps: step2. deviceB offline + */ + g_deviceB->Offline(); + + /** + * @tc.steps: step3. deviceC put {k2, v2}, {k3, delete}, {k4, v4} + */ + Key key2 = {'2'}; + Value value2 = {'2'}; + g_deviceC->PutData(key2, value2, 0, 0); + + Key key3 = {'3'}; + Value value3 = {'3'}; + g_deviceC->PutData(key3, value3, 0, 1); + + Key key4 = {'4'}; + Value value4 = {'4'}; + g_deviceC->PutData(key4, value4, 0, 0); + + /** + * @tc.steps: step2. deviceA call pull sync + * @tc.expected: step2. sync should return OK. + */ + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, DeviceB status is timeout + * deviceA has {k2, v2}, {k3 delete}, {k4,v4} + */ + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + if (pair.first == DEVICE_B) { + EXPECT_TRUE(pair.second == COMM_FAILURE); + } else { + EXPECT_TRUE(pair.second == OK); + } + } + + Value value5; + EXPECT_TRUE(g_kvDelegatePtr->Get(key1, value5) != OK); + g_kvDelegatePtr->Get(key2, value5); + EXPECT_EQ(value5, value2); + EXPECT_TRUE(g_kvDelegatePtr->Get(key3, value5) != OK); + g_kvDelegatePtr->Get(key4, value5); + EXPECT_EQ(value5, value4); +} + +/** + * @tc.name: Auto Sync 001 + * @tc.desc: Verify auto sync enable function. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, AutoSync001, TestSize.Level1) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. enable auto sync + * @tc.expected: step1, Pragma return OK. + */ + bool autoSync = true; + PragmaData data = static_cast(&autoSync); + DBStatus status = g_kvDelegatePtr->Pragma(AUTO_SYNC, data); + ASSERT_EQ(status, OK); + + /** + * @tc.steps: step2. deviceA put {k1, v1}, {k2, v2} + */ + ASSERT_TRUE(g_kvDelegatePtr->Put(KEY_1, VALUE_1) == OK); + ASSERT_TRUE(g_kvDelegatePtr->Put(KEY_2, VALUE_2) == OK); + + /** + * @tc.steps: step3. sleep for data sync + * @tc.expected: step3. deviceB,C has {k1, v1}, {k2, v2} + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + VirtualDataItem item; + g_deviceB->GetData(KEY_1, item); + EXPECT_EQ(item.value, VALUE_1); + g_deviceB->GetData(KEY_2, item); + EXPECT_EQ(item.value, VALUE_2); + g_deviceC->GetData(KEY_1, item); + EXPECT_EQ(item.value, VALUE_1); + g_deviceC->GetData(KEY_2, item); + EXPECT_EQ(item.value, VALUE_2); +} + +/** + * @tc.name: Auto Sync 002 + * @tc.desc: Verify auto sync disable function. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, AutoSync002, TestSize.Level1) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. disable auto sync + * @tc.expected: step1, Pragma return OK. + */ + bool autoSync = false; + PragmaData data = static_cast(&autoSync); + DBStatus status = g_kvDelegatePtr->Pragma(AUTO_SYNC, data); + ASSERT_EQ(status, OK); + + /** + * @tc.steps: step2. deviceB put {k1, v1}, deviceC put {k2, v2} + */ + g_deviceB->PutData(KEY_1, VALUE_1, 0, 0); + g_deviceC->PutData(KEY_2, VALUE_2, 0, 0); + + /** + * @tc.steps: step3. sleep for data sync + * @tc.expected: step3. deviceA don't have k1, k2. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + Value value3; + EXPECT_TRUE(g_kvDelegatePtr->Get(KEY_1, value3) == NOT_FOUND); + EXPECT_TRUE(g_kvDelegatePtr->Get(KEY_2, value3) == NOT_FOUND); +} + +/** + * @tc.name: Block Sync 001 + * @tc.desc: Verify block push sync function. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, BlockSync001, TestSize.Level1) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + g_kvDelegatePtr->Put(KEY_1, VALUE_1); + + /** + * @tc.steps: step2. deviceA call block push sync to deviceB & deviceC. + * @tc.expected: step2. Sync return OK, devices status OK, deviceB & deivceC has {k1, v1}. + */ + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, true); + ASSERT_EQ(status, OK); + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item1; + EXPECT_EQ(g_deviceB->GetData(KEY_1, item1), OK); + EXPECT_EQ(item1.value, VALUE_1); + VirtualDataItem item2; + EXPECT_EQ(g_deviceC->GetData(KEY_1, item2), OK); + EXPECT_EQ(item2.value, VALUE_1); +} + +/** + * @tc.name: Block Sync 002 + * @tc.desc: Verify block pull sync function. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, BlockSync002, TestSize.Level1) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceB put {k1, v1}, deviceC put {k2, v2} + */ + g_deviceB->PutData(KEY_1, VALUE_1, 0, 0); + g_deviceC->PutData(KEY_2, VALUE_2, 0, 0); + + /** + * @tc.steps: step2. deviceA call block pull and pull sync to deviceB & deviceC. + * @tc.expected: step2. Sync return OK, devices status OK, deviceA has {k1, v1}, {k2, v2} + */ + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result, true); + ASSERT_EQ(status, OK); + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + Value value3; + EXPECT_TRUE(g_kvDelegatePtr->Get(KEY_1, value3) == OK); + EXPECT_TRUE(value3 == VALUE_1); + EXPECT_TRUE(g_kvDelegatePtr->Get(KEY_2, value3) == OK); + EXPECT_TRUE(value3 == VALUE_2); +} + +/** + * @tc.name: Block Sync 003 + * @tc.desc: Verify block push and pull sync function. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, BlockSync003, TestSize.Level1) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + g_kvDelegatePtr->Put(KEY_1, VALUE_1); + + /** + * @tc.steps: step2. deviceB put {k1, v1}, deviceB put {k2, v2} + */ + g_deviceB->PutData(KEY_2, VALUE_2, 0, 0); + g_deviceC->PutData(KEY_3, VALUE_3, 0, 0); + + /** + * @tc.steps: step3. deviceA call block pull and pull sync to deviceB & deviceC. + * @tc.expected: step3. Sync return OK, devices status OK, deviceA has {k1, v1}, {k2, v2} {k3, v3} + * deviceB has {k1, v1}, {k2. v2} , deviceC has {k1, v1}, {k3, v3} + */ + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result, true); + ASSERT_EQ(status, OK); + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == OK); + } + + VirtualDataItem item1; + g_deviceB->GetData(KEY_1, item1); + EXPECT_TRUE(item1.value == VALUE_1); + g_deviceB->GetData(KEY_2, item1); + EXPECT_TRUE(item1.value == VALUE_2); + + VirtualDataItem item2; + g_deviceC->GetData(KEY_1, item2); + EXPECT_TRUE(item2.value == VALUE_1); + g_deviceC->GetData(KEY_3, item2); + EXPECT_TRUE(item2.value == VALUE_3); + + Value value3; + EXPECT_TRUE(g_kvDelegatePtr->Get(KEY_1, value3) == OK); + EXPECT_TRUE(value3 == VALUE_1); + EXPECT_TRUE(g_kvDelegatePtr->Get(KEY_2, value3) == OK); + EXPECT_TRUE(value3 == VALUE_2); + EXPECT_TRUE(g_kvDelegatePtr->Get(KEY_3, value3) == OK); + EXPECT_TRUE(value3 == VALUE_3); +} + +/** + * @tc.name: Block Sync 004 + * @tc.desc: Verify block sync function invalid args. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, BlockSync004, TestSize.Level2) +{ + std::vector devices; + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + g_kvDelegatePtr->Put(KEY_1, VALUE_1); + + /** + * @tc.steps: step2. deviceA call block push sync to deviceB & deviceC. + * @tc.expected: step2. Sync return INVALID_ARGS + */ + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result, true); + EXPECT_EQ(status, INVALID_ARGS); + + /** + * @tc.steps: step3. deviceB, deviceC offlinem and push deviceA sync to deviceB and deviceC. + * @tc.expected: step3. Sync return OK, but the deviceB and deviceC are TIME_OUT + */ + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + g_deviceB->Offline(); + g_deviceC->Offline(); + + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result, true); + EXPECT_EQ(status, OK); + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == COMM_FAILURE); + } +} + +/** + * @tc.name: Block Sync 005 + * @tc.desc: Verify block sync function busy. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, BlockSync005, TestSize.Level2) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + g_kvDelegatePtr->Put(KEY_1, VALUE_1); + + /** + * @tc.steps: step2. New a thread to deviceA call block push sync to deviceB & deviceC, + * but deviceB & C is blocked + * @tc.expected: step2. Sync will be blocked util timeout, and then return OK + */ + g_deviceB->Offline(); + g_deviceC->Offline(); + thread thread([devices](){ + std::map resultInner; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, resultInner, true); + EXPECT_EQ(status, OK); + }); + thread.detach(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps: step3. sleep 1s and call sync. + * @tc.expected: step3. Sync will return BUSY. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + std::map result; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result, true); + EXPECT_EQ(status, OK); +} + +/** + * @tc.name: SyncQueue001 + * @tc.desc: Invalid args check of Pragma GET_QUEUED_SYNC_SIZE SET_QUEUED_SYNC_LIMIT and + * GET_QUEUED_SYNC_LIMIT, expect return INVALID_ARGS. + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, SyncQueue001, TestSize.Level3) +{ + /** + * @tc.steps:step1. Set PragmaCmd to be GET_QUEUED_SYNC_SIZE, and set param to be null + * @tc.expected: step1. Expect return INVALID_ARGS. + */ + int *param = nullptr; + PragmaData input = static_cast(param); + EXPECT_EQ(g_kvDelegatePtr->Pragma(GET_QUEUED_SYNC_SIZE, input), INVALID_ARGS); + + /** + * @tc.steps:step2. Set PragmaCmd to be SET_QUEUED_SYNC_LIMIT, and set param to be null + * @tc.expected: step2. Expect return INVALID_ARGS. + */ + input = static_cast(param); + EXPECT_EQ(g_kvDelegatePtr->Pragma(SET_QUEUED_SYNC_LIMIT, input), INVALID_ARGS); + + /** + * @tc.steps:step3. Set PragmaCmd to be GET_QUEUED_SYNC_LIMIT, and set param to be null + * @tc.expected: step3. Expect return INVALID_ARGS. + */ + input = static_cast(param); + EXPECT_EQ(g_kvDelegatePtr->Pragma(GET_QUEUED_SYNC_LIMIT, input), INVALID_ARGS); + + /** + * @tc.steps:step4. Set PragmaCmd to be SET_QUEUED_SYNC_LIMIT, and set param to be QUEUED_SYNC_LIMIT_MIN - 1 + * @tc.expected: step4. Expect return INVALID_ARGS. + */ + int limit = DBConstant::QUEUED_SYNC_LIMIT_MIN - 1; + input = static_cast(&limit); + EXPECT_EQ(g_kvDelegatePtr->Pragma(SET_QUEUED_SYNC_LIMIT, input), INVALID_ARGS); + + /** + * @tc.steps:step5. Set PragmaCmd to be SET_QUEUED_SYNC_LIMIT, and set param to be QUEUED_SYNC_LIMIT_MAX + 1 + * @tc.expected: step5. Expect return INVALID_ARGS. + */ + limit = DBConstant::QUEUED_SYNC_LIMIT_MAX + 1; + input = static_cast(&limit); + EXPECT_EQ(g_kvDelegatePtr->Pragma(SET_QUEUED_SYNC_LIMIT, input), INVALID_ARGS); +} + +/** + * @tc.name: SyncQueue002 + * @tc.desc: Pragma GET_QUEUED_SYNC_LIMIT and SET_QUEUED_SYNC_LIMIT + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, SyncQueue002, TestSize.Level3) +{ + /** + * @tc.steps:step1. Set PragmaCmd to be GET_QUEUED_SYNC_LIMIT, + * @tc.expected: step1. Expect return OK, limit eq QUEUED_SYNC_LIMIT_DEFAULT. + */ + int limit = 0; + PragmaData input = static_cast(&limit); + EXPECT_EQ(g_kvDelegatePtr->Pragma(GET_QUEUED_SYNC_LIMIT, input), OK); + EXPECT_EQ(limit, DBConstant::QUEUED_SYNC_LIMIT_DEFAULT); + + /** + * @tc.steps:step2. Set PragmaCmd to be SET_QUEUED_SYNC_LIMIT, and set param to be 50 + * @tc.expected: step2. Expect return OK. + */ + limit = 50; + input = static_cast(&limit); + EXPECT_EQ(g_kvDelegatePtr->Pragma(SET_QUEUED_SYNC_LIMIT, input), OK); + + /** + * @tc.steps:step3. Set PragmaCmd to be GET_QUEUED_SYNC_LIMIT, + * @tc.expected: step3. Expect return OK, limit eq 50 + */ + limit = 0; + input = static_cast(&limit); + EXPECT_EQ(g_kvDelegatePtr->Pragma(GET_QUEUED_SYNC_LIMIT, input), OK); + EXPECT_EQ(limit, 50); +} + +/** + * @tc.name: SyncQueue003 + * @tc.desc: sync queue test + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, SyncQueue003, TestSize.Level3) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps:step1. Set PragmaCmd to be GET_QUEUED_SYNC_SIZE, + * @tc.expected: step1. Expect return OK, size eq 0. + */ + int size; + PragmaData input = static_cast(&size); + EXPECT_EQ(g_kvDelegatePtr->Pragma(GET_QUEUED_SYNC_SIZE, input), OK); + EXPECT_EQ(size, 0); + + /** + * @tc.steps:step2. deviceA put {k1, v1} + */ + status = g_kvDelegatePtr->Put(KEY_1, VALUE_1); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps:step3. deviceA sync SYNC_MODE_PUSH_ONLY + */ + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, nullptr, false); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps:step4. deviceA put {k2, v2} + */ + status = g_kvDelegatePtr->Put(KEY_2, VALUE_2); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps:step5. deviceA sync SYNC_MODE_PUSH_ONLY + */ + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, nullptr, false); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps:step6. deviceB put {k3, v3} + */ + g_deviceB->PutData(KEY_3, VALUE_3, 0, 0); + + /** + * @tc.steps:step7. deviceA put {k4, v4} + */ + status = g_kvDelegatePtr->Put(KEY_4, VALUE_4); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps:step8. deviceA sync SYNC_MODE_PUSH_PULL + */ + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_PULL, nullptr, false); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps:step9. Set PragmaCmd to be GET_QUEUED_SYNC_SIZE, + * @tc.expected: step1. Expect return OK, 0 <= size <= 4 + */ + EXPECT_EQ(g_kvDelegatePtr->Pragma(GET_QUEUED_SYNC_SIZE, input), OK); + ASSERT_TRUE((size >= 0) && (size <= 4)); + + /** + * @tc.steps:step10. deviceB put {k5, v5} + */ + g_deviceB->PutData(KEY_5, VALUE_5, 0, 0); + + /** + * @tc.steps:step11. deviceA call sync and wait + * @tc.expected: step11. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step11. onComplete should be called, DeviceA,B,C have {k1,v1}~ {KEY_5,VALUE_5} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + EXPECT_TRUE(pair.second == OK); + } + VirtualDataItem item; + g_deviceB->GetData(KEY_1, item); + EXPECT_TRUE(item.value == VALUE_1); + g_deviceB->GetData(KEY_2, item); + EXPECT_TRUE(item.value == VALUE_2); + g_deviceB->GetData(KEY_3, item); + EXPECT_TRUE(item.value == VALUE_3); + g_deviceB->GetData(KEY_4, item); + EXPECT_TRUE(item.value == VALUE_4); + g_deviceB->GetData(KEY_5, item); + EXPECT_TRUE(item.value == VALUE_5); + Value value; + EXPECT_EQ(g_kvDelegatePtr->Get(KEY_3, value), OK); + EXPECT_EQ(VALUE_3, value); + EXPECT_EQ(g_kvDelegatePtr->Get(KEY_5, value), OK); + EXPECT_EQ(VALUE_5, value); +} + +/** + * @tc.name: SyncQueue004 + * @tc.desc: sync queue full test + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, SyncQueue004, TestSize.Level3) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps:step1. deviceB C block + */ + g_communicatorAggregator->SetBlockValue(true); + + /** + * @tc.steps:step2. deviceA put {k1, v1} + */ + status = g_kvDelegatePtr->Put(KEY_1, VALUE_1); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps:step3. deviceA sync QUEUED_SYNC_LIMIT_DEFAULT times + * @tc.expected: step3. Expect return OK + */ + for (int i = 0; i < DBConstant::QUEUED_SYNC_LIMIT_DEFAULT; i++) { + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, nullptr, false); + ASSERT_TRUE(status == OK); + } + + /** + * @tc.steps:step4. deviceA sync + * @tc.expected: step4. Expect return BUSY + */ + status = g_kvDelegatePtr->Sync(devices, SYNC_MODE_PUSH_ONLY, nullptr, false); + ASSERT_TRUE(status == BUSY); + g_communicatorAggregator->SetBlockValue(false); +} + +/** + * @tc.name: SyncQueue005 + * @tc.desc: block sync queue test + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, SyncQueue005, TestSize.Level3) +{ + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + /** + * @tc.steps:step1. New a thread to deviceA call block push sync to deviceB & deviceC, + * but deviceB & C is offline + * @tc.expected: step1. Sync will be blocked util timeout, and then return OK + */ + g_deviceB->Offline(); + g_deviceC->Offline(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + + /** + * @tc.steps:step2. deviceA put {k1, v1} + */ + g_kvDelegatePtr->Put(KEY_1, VALUE_1); + + std::mutex lockMutex; + std::condition_variable conditionVar; + + std::thread threadFirst([devices](){ + std::map resultInner; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, resultInner, true); + EXPECT_EQ(status, OK); + }); + threadFirst.detach(); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + /** + * @tc.steps:step3. New a thread to deviceA call block push sync to deviceB & deviceC, + * but deviceB & C is offline + * @tc.expected: step2. Sync will be blocked util timeout, and then return OK + */ + std::thread threadSecond([devices, &lockMutex, &conditionVar](){ + std::map resultInner; + DBStatus status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, resultInner, true); + EXPECT_EQ(status, OK); + std::unique_lock lockInner(lockMutex); + conditionVar.notify_one(); + }); + threadSecond.detach(); + + /** + * @tc.steps:step4. Set PragmaCmd to be GET_QUEUED_SYNC_SIZE, + * @tc.expected: step1. Expect return OK, size eq 0. + */ + int size; + PragmaData input = static_cast(&size); + EXPECT_EQ(g_kvDelegatePtr->Pragma(GET_QUEUED_SYNC_SIZE, input), OK); + EXPECT_EQ(size, 0); + + /** + * @tc.steps:step5. wait exit + */ + std::unique_lock lock(lockMutex); + auto now = std::chrono::system_clock::now(); + conditionVar.wait_until(lock, now + 2 * INT8_MAX * 1000ms); +} + +/** + * @tc.name: PermissionCheck001 + * @tc.desc: deviceA PermissionCheck not pass test, SYNC_MODE_PUSH_ONLY + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, PermissionCheck001, TestSize.Level3) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (flag & CHECK_FLAG_SEND) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step2. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call sync and wait + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, + * status == PERMISSION_CHECK_FORBID_SYNC, deviceB and deviceC do not have {k1, v1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == PERMISSION_CHECK_FORBID_SYNC); + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value.empty()); + g_deviceC->GetData(key, item); + EXPECT_TRUE(item.value.empty()); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** + * @tc.name: PermissionCheck002 + * @tc.desc: deviceA PermissionCheck not pass test, SYNC_MODE_PULL_ONLY + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, PermissionCheck002, TestSize.Level3) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (flag & CHECK_FLAG_RECEIVE) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step2. deviceB put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + g_deviceB->PutData(key, value, 0, 0); + + /** + * @tc.steps: step2. deviceB put {k2, v2} + */ + Key key2 = {'2'}; + Value value2 = {'2'}; + g_deviceC->PutData(key2, value2, 0, 0); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call pull sync + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, + * status == PERMISSION_CHECK_FORBID_SYNC, DeviceA do not have {k1, VALUE_1}, {K2. VALUE_2} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == PERMISSION_CHECK_FORBID_SYNC); + } + Value value3; + EXPECT_EQ(g_kvDelegatePtr->Get(key, value3), NOT_FOUND); + EXPECT_EQ(g_kvDelegatePtr->Get(key2, value3), NOT_FOUND); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** + * @tc.name: PermissionCheck003 + * @tc.desc: deviceA PermissionCheck not pass test, SYNC_MODE_PUSH_PULL + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, PermissionCheck003, TestSize.Level3) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (flag & (CHECK_FLAG_SEND | CHECK_FLAG_RECEIVE)) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step2. deviceA put {k1, v1} + */ + Key key1 = {'1'}; + Value value1 = {'1'}; + DBStatus status = g_kvDelegatePtr->Put(key1, value1); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceB put {k2, v2} + */ + Key key2 = {'2'}; + Value value2 = {'2'}; + g_deviceB->PutData(key2, value2, 0, 0); + + /** + * @tc.steps: step2. deviceB put {k3, v3} + */ + Key key3 = {'3'}; + Value value3 = {'3'}; + g_deviceC->PutData(key3, value3, 0, 0); + + /** + * @tc.steps: step3. deviceA call push_pull sync + * @tc.expected: step3. sync should return OK. + * onComplete should be called, status == PERMISSION_CHECK_FORBID_SYNC + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_PULL, result); + ASSERT_TRUE(status == OK); + + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == PERMISSION_CHECK_FORBID_SYNC); + } + + /** + * @tc.expected: step3. DeviceA only have {k1. v1} + * DeviceB only have {k2. v2}, DeviceC only have {k3. v3} + */ + Value value4; + EXPECT_TRUE(g_kvDelegatePtr->Get(key2, value4) == NOT_FOUND); + EXPECT_TRUE(g_kvDelegatePtr->Get(key3, value4) == NOT_FOUND); + + VirtualDataItem item1; + g_deviceB->GetData(key1, item1); + EXPECT_TRUE(item1.value.empty()); + g_deviceB->GetData(key3, item1); + EXPECT_TRUE(item1.value.empty()); + + VirtualDataItem item2; + g_deviceC->GetData(key1, item2); + EXPECT_TRUE(item1.value.empty()); + g_deviceC->GetData(key2, item2); + EXPECT_TRUE(item2.value.empty()); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** + * @tc.name: PermissionCheck004 + * @tc.desc: deviceB and deviceC PermissionCheck not pass test, SYNC_MODE_PUSH_ONLY + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, PermissionCheck004, TestSize.Level3) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (flag & CHECK_FLAG_RECEIVE) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step2. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call sync and wait + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, + * status == PERMISSION_CHECK_FORBID_SYNC, deviceB and deviceC do not have {k1, v1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == PERMISSION_CHECK_FORBID_SYNC); + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value.empty()); + g_deviceC->GetData(key, item); + EXPECT_TRUE(item.value.empty()); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** + * @tc.name: PermissionCheck005 + * @tc.desc: deviceB and deviceC PermissionCheck not pass test, SYNC_MODE_PULL_ONLY + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, PermissionCheck005, TestSize.Level3) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (flag & CHECK_FLAG_SEND) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step2. deviceB put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + g_deviceB->PutData(key, value, 0, 0); + + /** + * @tc.steps: step2. deviceB put {k2, v2} + */ + Key key2 = {'2'}; + Value value2 = {'2'}; + g_deviceC->PutData(key2, value2, 0, 0); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call pull sync + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, + * status == PERMISSION_CHECK_FORBID_SYNC, DeviceA do not have {k1, VALUE_1}, {K2. VALUE_2} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + EXPECT_TRUE(pair.second == PERMISSION_CHECK_FORBID_SYNC); + } + Value value3; + EXPECT_EQ(g_kvDelegatePtr->Get(key, value3), NOT_FOUND); + EXPECT_EQ(g_kvDelegatePtr->Get(key2, value3), NOT_FOUND); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** + * @tc.name: PermissionCheck006 + * @tc.desc: deviceA PermissionCheck deviceB not pass, deviceC pass + * @tc.type: FUNC + * @tc.require: AR000EJJOJ + * @tc.author: wangchuanqing + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, PermissionCheck006, TestSize.Level3) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (deviceId == g_deviceB->GetDeviceId()) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step2. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call sync and wait + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + + /** + * @tc.expected: step3. onComplete should be called, + * status == PERMISSION_CHECK_FORBID_SYNC, deviceB and deviceC do not have {k1, v1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + if (g_deviceB->GetDeviceId() == pair.first) { + EXPECT_TRUE(pair.second == PERMISSION_CHECK_FORBID_SYNC); + } else { + EXPECT_TRUE(pair.second == OK); + } + } + VirtualDataItem item; + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value.empty()); + g_deviceC->GetData(key, item); + EXPECT_TRUE(item.value == value); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** + * @tc.name: PermissionCheck007 + * @tc.desc: deviceA PermissionCheck, deviceB not pass, deviceC pass in SYNC_MODE_AUTO_PUSH + * @tc.type: FUNC + * @tc.require: AR000G3RLS + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, PermissionCheck007, TestSize.Level3) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (deviceId == g_deviceC->GetDeviceId() && + (flag & (CHECK_FLAG_RECEIVE | CHECK_FLAG_AUTOSYNC))) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + /** + * @tc.steps: step2. deviceA set auto sync + */ + bool autoSync = true; + PragmaData data = static_cast(&autoSync); + status = g_kvDelegatePtr->Pragma(AUTO_SYNC, data); + ASSERT_EQ(status, OK); + + /** + * @tc.steps: step3. deviceA put {k1, v1}, and sleep 1s + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + + /** + * @tc.steps: step3. check value in device B and not in device C. + */ + VirtualDataItem item; + g_deviceC->GetData(key, item); + EXPECT_TRUE(item.value.empty()); + g_deviceB->GetData(key, item); + EXPECT_TRUE(item.value == value); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** ++ * @tc.name: PermissionCheck008 ++ * @tc.desc: deviceA PermissionCheck, deviceB not pass, deviceC pass in SYNC_MODE_AUTO_PULL ++ * @tc.type: FUNC ++ * @tc.require: AR000G3RLS ++ * @tc.author: zhangqiquan ++ */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, PermissionCheck008, TestSize.Level3) +{ + /** + * @tc.steps: step1. SetPermissionCheckCallback + * @tc.expected: step1. return OK. + */ + auto permissionCheckCallback = [] (const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) -> bool { + if (deviceId == g_deviceC->GetDeviceId() && + (flag & CHECK_FLAG_SPONSOR)) { + LOGD("in RunPermissionCheck callback func, check not pass, flag:%d", flag); + return false; + } else { + LOGD("in RunPermissionCheck callback func, check pass, flag:%d", flag); + return true; + } + }; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(permissionCheckCallback), OK); + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + devices.push_back(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step2. deviceB put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + g_deviceB->PutData(key, value, 0, 0); + + /** + * @tc.steps: step2. device put {k2, v2} + */ + Key key2 = {'2'}; + Value value2 = {'2'}; + g_deviceC->PutData(key2, value2, 0, 0); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step3. deviceA call push sync + * @tc.expected: step3. sync should return OK. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PULL_ONLY, result); + ASSERT_TRUE(status == OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + + /** + * @tc.expected: step4. onComplete should be called, + * status == PERMISSION_CHECK_FORBID_SYNC, deviceB and deviceC do not have {k1, v1} + */ + ASSERT_TRUE(result.size() == devices.size()); + for (const auto &pair : result) { + LOGD("dev %s, status %d", pair.first.c_str(), pair.second); + if (g_deviceC->GetDeviceId() == pair.first) { + EXPECT_TRUE(pair.second == PERMISSION_CHECK_FORBID_SYNC); + } else { + EXPECT_TRUE(pair.second == OK); + } + } + /** + * @tc.steps: step5. check value in device A + */ + Value value4; + EXPECT_TRUE(g_kvDelegatePtr->Get(key, value4) == OK); + EXPECT_TRUE(g_kvDelegatePtr->Get(key2, value4) == NOT_FOUND); + PermissionCheckCallbackV2 nullCallback; + EXPECT_EQ(g_mgr.SetPermissionCheckCallback(nullCallback), OK); +} + +/** + * @tc.name: SaveDataNotify001 + * @tc.desc: Test SaveDataNotify function, delay < 30s should sync ok, > 36 should timeout + * @tc.type: FUNC + * @tc.require: AR000D4876 + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, SaveDataNotify001, TestSize.Level3) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. deviceA put {k1, v1} + */ + Key key = {'1'}; + Value value = {'1'}; + status = g_kvDelegatePtr->Put(key, value); + ASSERT_TRUE(status == OK); + + /** + * @tc.steps: step2. deviceB set sava data dely 5s + */ + g_deviceB->SetSaveDataDelayTime(WAIT_5_SECONDS); + + /** + * @tc.steps: step3. deviceA call sync and wait + * @tc.expected: step3. sync should return OK. onComplete should be called, deviceB sync success. + */ + std::map result; + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + ASSERT_TRUE(result.size() == devices.size()); + ASSERT_TRUE(result[DEVICE_B] == OK); + + /** + * @tc.steps: step4. deviceB set sava data dely 30s and put {k1, v1} + */ + g_deviceB->SetSaveDataDelayTime(WAIT_30_SECONDS); + status = g_kvDelegatePtr->Put(key, value); + + /** + * @tc.steps: step3. deviceA call sync and wait + * @tc.expected: step3. sync should return OK. onComplete should be called, deviceB sync success. + */ + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + ASSERT_TRUE(result.size() == devices.size()); + ASSERT_TRUE(result[DEVICE_B] == OK); + + /** + * @tc.steps: step4. deviceB set sava data dely 36s and put {k1, v1} + */ + g_deviceB->SetSaveDataDelayTime(WAIT_36_SECONDS); + status = g_kvDelegatePtr->Put(key, value); + + /** + * @tc.steps: step5. deviceA call sync and wait + * @tc.expected: step5. sync should return OK. onComplete should be called, deviceB sync TIME_OUT. + */ + status = g_tool.SyncTest(g_kvDelegatePtr, devices, SYNC_MODE_PUSH_ONLY, result); + ASSERT_TRUE(status == OK); + ASSERT_TRUE(result.size() == devices.size()); + ASSERT_TRUE(result[DEVICE_B] == TIME_OUT); +} + +/** + * @tc.name: SametimeSync001 + * @tc.desc: Test 2 device sync with each other + * @tc.type: FUNC + * @tc.require: AR000CCPOM + * @tc.author: zhangqiquan + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, SametimeSync001, TestSize.Level3) +{ + DBStatus status = OK; + std::vector devices; + devices.push_back(g_deviceB->GetDeviceId()); + + int responseCount = 0; + int requestCount = 0; + Key key = {'1'}; + Value value = {'1'}; + /** + * @tc.steps: step1. make sure deviceB send pull firstly and response_pull secondly + * @tc.expected: step1. deviceA put data when finish push task. put data should return OK. + */ + g_communicatorAggregator->RegOnDispatch([&responseCount, &requestCount, &key, &value]( + const std::string &target, DistributedDB::Message *msg) { + if (target == "real_device" && msg->GetMessageId() == DATA_SYNC_MESSAGE) { + if (msg->GetMessageType() == TYPE_RESPONSE) { + responseCount++; + if (responseCount == 1) { // 1 is the ack which B response A's push task + EXPECT_EQ(g_kvDelegatePtr->Put(key, value), DBStatus::OK); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } else if (responseCount == 2) { // 2 is the ack which B response A's response_pull task + msg->SetErrorNo(E_FEEDBACK_COMMUNICATOR_NOT_FOUND); + } + } if (msg->GetMessageType() == TYPE_REQUEST) { + requestCount++; + if (requestCount == 1) { // 1 is A push task + std::this_thread::sleep_for(std::chrono::seconds(2)); // sleep 2 sec + } + } + } + }); + /** + * @tc.steps: step2. deviceA,deviceB sync to each other at same time + * @tc.expected: step2. sync should return OK. + */ + std::map result; + std::thread subThread([]{ + g_deviceB->Sync(DistributedDB::SYNC_MODE_PULL_ONLY, true); + }); + status = g_tool.SyncTest(g_kvDelegatePtr, devices, DistributedDB::SYNC_MODE_PUSH_PULL, result); + subThread.join(); + g_communicatorAggregator->RegOnDispatch(nullptr); + + EXPECT_TRUE(status == OK); + ASSERT_TRUE(result.size() == devices.size()); + EXPECT_TRUE(result[DEVICE_B] == OK); + Value actualValue; + g_kvDelegatePtr->Get(key, actualValue); + EXPECT_EQ(actualValue, value); +} + +/** + * @tc.name: DatabaseOnlineCallback001 + * @tc.desc: check database status notify online callback + * @tc.type: FUNC + * @tc.require: AR000CQS3S SR000CQE0B + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, DatabaseOnlineCallback001, TestSize.Level1) +{ + /** + * @tc.steps: step1. SetStoreStatusNotifier + * @tc.expected: step1. SetStoreStatusNotifier ok + */ + std::string targetDev = "DEVICE_X"; + bool isCheckOk = false; + auto databaseStatusNotifyCallback = [targetDev, &isCheckOk] (std::string userId, + std::string appId, std::string storeId, const std::string deviceId, bool onlineStatus) -> void { + if (userId == USER_ID && appId == APP_ID && storeId == STORE_ID && deviceId == targetDev && + onlineStatus == true) { + isCheckOk = true; + }}; + g_mgr.SetStoreStatusNotifier(databaseStatusNotifyCallback); + /** + * @tc.steps: step2. trigger device online + * @tc.expected: step2. check callback ok + */ + g_communicatorAggregator->OnlineDevice(targetDev); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME / 20)); + EXPECT_EQ(isCheckOk, true); + StoreStatusNotifier nullCallback; + g_mgr.SetStoreStatusNotifier(nullCallback); +} + +/** + * @tc.name: DatabaseOfflineCallback001 + * @tc.desc: check database status notify online callback + * @tc.type: FUNC + * @tc.require: AR000CQS3S SR000CQE0B + * @tc.author: zhuwentao + */ +HWTEST_F(DistributedDBSingleVerP2PSyncTest, DatabaseOfflineCallback001, TestSize.Level1) +{ + /** + * @tc.steps: step1. SetStoreStatusNotifier + * @tc.expected: step1. SetStoreStatusNotifier ok + */ + std::string targetDev = "DEVICE_X"; + bool isCheckOk = false; + auto databaseStatusNotifyCallback = [targetDev, &isCheckOk] (std::string userId, + std::string appId, std::string storeId, const std::string deviceId, bool onlineStatus) -> void { + if (userId == USER_ID && appId == APP_ID && storeId == STORE_ID && deviceId == targetDev && + onlineStatus == false) { + isCheckOk = true; + }}; + g_mgr.SetStoreStatusNotifier(databaseStatusNotifyCallback); + /** + * @tc.steps: step2. trigger device offline + * @tc.expected: step2. check callback ok + */ + g_communicatorAggregator->OfflineDevice(targetDev); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME / 20)); + EXPECT_EQ(isCheckOk, true); + StoreStatusNotifier nullCallback; + g_mgr.SetStoreStatusNotifier(nullCallback); +} diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_syncer_device_manager_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_syncer_device_manager_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c4593b97fb791fcad6747071df311bbcaca147c1 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_syncer_device_manager_test.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "device_manager.h" +#include "distributeddb_tools_unit_test.h" +#include "kv_virtual_device.h" +#include "log_print.h" +#include "parcel.h" +#include "sync_types.h" +#include "virtual_communicator.h" +#include "virtual_communicator_aggregator.h" +#include "virtual_single_ver_sync_db_Interface.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace std; + +namespace { + const std::string DEVICE_B = "deviceB"; + const std::string DEVICE_C = "deviceC"; + VirtualCommunicatorAggregator* g_communicatorAggregator = nullptr; + VirtualCommunicator* g_virtualCommunicator = nullptr; + KvVirtualDevice *g_deviceB = nullptr; + KvVirtualDevice *g_deviceC = nullptr; + DeviceManager *g_deviceManager = nullptr; + const int WAIT_TIME = 1000; +} + +class DistributedDBSyncerDeviceManagerTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSyncerDeviceManagerTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Virtual Communicator. + */ + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); + int errCode; + g_virtualCommunicator = static_cast(g_communicatorAggregator->AllocCommunicator(0, errCode)); + ASSERT_TRUE(g_virtualCommunicator != nullptr); +} + +void DistributedDBSyncerDeviceManagerTest::TearDownTestCase(void) +{ + /** + * @tc.setup: Release Virtual CommunicatorAggregator. + */ + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); + g_communicatorAggregator = nullptr; +} + +void DistributedDBSyncerDeviceManagerTest::SetUp(void) +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: Init a DeviceManager and DeviceB, C + */ + g_deviceManager = new (std::nothrow) DeviceManager; + ASSERT_TRUE(g_deviceManager != nullptr); + g_deviceManager->Initialize(g_virtualCommunicator, nullptr, nullptr); + g_virtualCommunicator->RegOnConnectCallback( + std::bind(&DeviceManager::OnDeviceConnectCallback, g_deviceManager, + std::placeholders::_1, std::placeholders::_2), nullptr); + + g_deviceB = new (std::nothrow) KvVirtualDevice(DEVICE_B); + ASSERT_TRUE(g_deviceB != nullptr); + VirtualSingleVerSyncDBInterface *syncInterfaceB = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(syncInterfaceB != nullptr); + ASSERT_EQ(g_deviceB->Initialize(g_communicatorAggregator, syncInterfaceB), E_OK); + + g_deviceC = new (std::nothrow) KvVirtualDevice(DEVICE_C); + ASSERT_TRUE(g_deviceC != nullptr); + VirtualSingleVerSyncDBInterface *syncInterfaceC = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(syncInterfaceC != nullptr); + ASSERT_EQ(g_deviceC->Initialize(g_communicatorAggregator, syncInterfaceC), E_OK); +} + +void DistributedDBSyncerDeviceManagerTest::TearDown(void) +{ + /** + * @tc.setup: Release a DeviceManager and DeviceB, C + */ + if (g_deviceManager != nullptr) { + g_virtualCommunicator->RegOnConnectCallback(nullptr, nullptr); + delete g_deviceManager; + g_deviceManager = nullptr; + } + if (g_deviceB != nullptr) { + delete g_deviceB; + g_deviceB = nullptr; + } + if (g_deviceC != nullptr) { + delete g_deviceC; + g_deviceC = nullptr; + } +} + +/** + * @tc.name: Online Callback 001 + * @tc.desc: Test DeviceManager device online callback function. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSyncerDeviceManagerTest, OnlineCallback001, TestSize.Level0) +{ + bool onlineCalled = false; + + /** + * @tc.steps: step1. set device online callback + */ + g_deviceManager->RegDeviceOnLineCallBack([&onlineCalled](const std::string &targetDev) { + LOGD("DeviceManageTest online called, dev %s", targetDev.c_str()); + if (targetDev == g_deviceB->GetDeviceId()) { + LOGD("DEVICE TEST CALL ONLINE CALLBACK"); + onlineCalled = true; + } + }); + + /** + * @tc.steps: step2. deviceB online + * @tc.expected: step2, the online callback should be called. + */ + g_communicatorAggregator->OnlineDevice(g_deviceB->GetDeviceId()); + EXPECT_TRUE(onlineCalled); +} + +/** + * @tc.name: Offline Callback 001 + * @tc.desc: Test DeviceManager device offline callback function. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSyncerDeviceManagerTest, OfflineCallback001, TestSize.Level0) +{ + bool offlineCalled = false; + g_communicatorAggregator->OnlineDevice(g_deviceB->GetDeviceId()); + + /** + * @tc.steps: step1. set device offline callback + */ + g_deviceManager->RegDeviceOffLineCallBack([&offlineCalled](const std::string &targetDev) { + LOGD("DeviceManageTest offline called, dev %s", targetDev.c_str()); + if (targetDev == g_deviceB->GetDeviceId()) { + offlineCalled = true; + } + }); + + /** + * @tc.steps: step2. deviceB offline + * @tc.expected: step2, the offline callback should be called. + */ + g_communicatorAggregator->OfflineDevice(g_deviceB->GetDeviceId()); + EXPECT_TRUE(offlineCalled); +} + +/** + * @tc.name: Get Devices 001 + * @tc.desc: Test DeviceManager GetDevices function. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSyncerDeviceManagerTest, GetDevices001, TestSize.Level0) +{ + std::vector deviceList; + + /** + * @tc.steps: step1. call GetDevices + * @tc.expected: step1, GetDevices return deviceB,C + */ + g_deviceManager->GetOnlineDevices(deviceList); + int size = deviceList.size(); + ASSERT_EQ(size, 2); + EXPECT_TRUE(deviceList[0] == g_deviceB->GetDeviceId()); + EXPECT_TRUE(deviceList[1] == g_deviceC->GetDeviceId()); + g_communicatorAggregator->OfflineDevice(g_deviceC->GetDeviceId()); + + /** + * @tc.steps: step2. deiceC offline and call GetDevices + * @tc.expected: step2, GetDevices return deviceB + */ + g_deviceManager->GetOnlineDevices(deviceList); + ASSERT_TRUE(deviceList.size() == 1); + EXPECT_TRUE(deviceList[0] == g_deviceB->GetDeviceId()); +} + +/** + * @tc.name: Send BroadCast 001 + * @tc.desc: Test DeviceManager SendBroadCast function. + * @tc.type: FUNC + * @tc.require: AR000CKRTD AR000CQE0E + * @tc.author: xushaohua + */ +HWTEST_F(DistributedDBSyncerDeviceManagerTest, SendBroadCast001, TestSize.Level1) +{ + bool deviceBReviced = false; + bool deviceCReviced = false; + + /** + * @tc.steps: step1. deviceB, C set OnRemoteDataChanged callback + */ + g_deviceB->OnRemoteDataChanged([&deviceBReviced](const std::string &deviceId){ + deviceBReviced = true; + }); + g_deviceC->OnRemoteDataChanged([&deviceCReviced](const std::string &deviceId){ + deviceCReviced = true; + }); + + /** + * @tc.steps: step2. call SendBroadCast. + * @tc.expected: step2, deviceB,C OnRemoteDataChanged should be called + */ + int errCode = g_deviceManager->SendBroadCast(LOCAL_DATA_CHANGED); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + ASSERT_TRUE(errCode == E_OK); + EXPECT_TRUE(deviceBReviced); + EXPECT_TRUE(deviceCReviced); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/distributeddb_time_sync_test.cpp b/mock/distributeddb/test/unittest/common/syncer/distributeddb_time_sync_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..293d95c0420283e3b6dd63c7df02616dedb4207a --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/distributeddb_time_sync_test.cpp @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "isyncer.h" +#include "single_ver_sync_state_machine.h" +#include "single_ver_kv_sync_task_context.h" +#include "sync_types.h" +#include "virtual_single_ver_sync_db_Interface.h" +#include "virtual_time_sync_communicator.h" + +using namespace std; +using namespace testing::ext; +using namespace DistributedDB; + +namespace { + const std::string DEVICE_A = "deviceA"; + const std::string DEVICE_B = "deviceB"; + VirtualTimeSyncCommunicator *g_virtualCommunicator = nullptr; + TimeSync *g_timeSyncA = nullptr; + TimeSync *g_timeSyncB = nullptr; + VirtualSingleVerSyncDBInterface *g_syncInterfaceA = nullptr; + VirtualSingleVerSyncDBInterface *g_syncInterfaceB = nullptr; + std::shared_ptr g_metadataA = nullptr; + std::shared_ptr g_metadataB = nullptr; + SingleVerSyncTaskContext *g_syncTaskContext = nullptr; + const int NETWORK_DELAY = 100 * 1000; // 100ms +} + +class DistributedDBTimeSyncTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBTimeSyncTest::SetUpTestCase(void) +{ + /** + * @tc.setup: NA + */ +} + +void DistributedDBTimeSyncTest::TearDownTestCase(void) +{ + /** + * @tc.teardown: NA + */ +} + +void DistributedDBTimeSyncTest::SetUp(void) +{ + DistributedDBUnitTest::DistributedDBToolsUnitTest::PrintTestCaseInfo(); + /** + * @tc.setup: create the instance for virtual communicator, virtual storage component and time syncer + */ + g_virtualCommunicator = new (std::nothrow) VirtualTimeSyncCommunicator(); + ASSERT_TRUE(g_virtualCommunicator != nullptr); + + g_syncInterfaceA = new (std::nothrow) VirtualSingleVerSyncDBInterface(); + ASSERT_TRUE(g_syncInterfaceA != nullptr); + + g_metadataA = std::make_shared(); + + g_syncInterfaceB = new (std::nothrow) VirtualSingleVerSyncDBInterface; + ASSERT_TRUE(g_syncInterfaceB != nullptr); + + g_metadataB = std::make_shared(); + + g_timeSyncA = new (std::nothrow) TimeSync(); + ASSERT_TRUE(g_timeSyncA != nullptr); + + g_timeSyncB = new (std::nothrow) TimeSync(); + ASSERT_TRUE(g_timeSyncB != nullptr); + + g_syncTaskContext = new (std::nothrow) SingleVerKvSyncTaskContext(); + ASSERT_TRUE(g_syncTaskContext != nullptr); +} + +void DistributedDBTimeSyncTest::TearDown(void) +{ + /** + * @tc.teardown: delete the ptr for testing + */ + if (g_syncTaskContext != nullptr) { + RefObject::DecObjRef(g_syncTaskContext); + g_syncTaskContext = nullptr; + } + if (g_syncInterfaceA != nullptr) { + delete g_syncInterfaceA; + g_syncInterfaceA = nullptr; + } + if (g_syncInterfaceB != nullptr) { + delete g_syncInterfaceB; + g_syncInterfaceB = nullptr; + } + + g_metadataA = nullptr; + g_metadataB = nullptr; + if (g_timeSyncA != nullptr) { + delete g_timeSyncA; + g_timeSyncA = nullptr; + } + if (g_timeSyncB != nullptr) { + delete g_timeSyncB; + g_timeSyncB = nullptr; + } + if (g_virtualCommunicator != nullptr) { + delete g_virtualCommunicator; + g_virtualCommunicator = nullptr; + } +} + +/** + * @tc.name: NormalSync001 + * @tc.desc: Verify time sync function is normal between two time sync instance with different timestamp. + * @tc.type: FUNC + * @tc.require: AR000C05EP AR000CQE0G + * @tc.author: wumin + */ +HWTEST_F(DistributedDBTimeSyncTest, NormalSync001, TestSize.Level0) +{ + /** + * @tc.steps: step1. Initialize the time sync A and B + * @tc.steps: step2. Write the timestamp into virtual storage component + * @tc.expected: step1. Initialize time sync A and B successfully + * @tc.expected: step2. Write the timestamp into virtual storage component successfully. + */ + g_metadataA->Initialize(g_syncInterfaceA); + TimeOffset offsetA = 100 * 1000 * 1000; // 100 seconds + // set timestamp for A virtual storage component + g_syncInterfaceA->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1, + TimeHelper::GetSysCurrentTime() + TimeHelper::BASE_OFFSET + offsetA, 0); + int errCode; + // initialize timeSyncA + errCode = g_timeSyncA->Initialize(g_virtualCommunicator, g_metadataA, g_syncInterfaceA, DEVICE_B); + EXPECT_TRUE(errCode == E_OK); + + g_metadataB->Initialize(g_syncInterfaceB); + TimeOffset offsetB = 200 * 1000 * 1000; // 200 seconds + // set timestamp for B virtual storage component + g_syncInterfaceB->PutData(DistributedDBUnitTest::KEY_1, DistributedDBUnitTest::VALUE_1, + TimeHelper::GetSysCurrentTime() + TimeHelper::BASE_OFFSET + offsetB, 0); + // initialize timeSyncB + errCode = g_timeSyncB->Initialize(g_virtualCommunicator, g_metadataB, g_syncInterfaceB, DEVICE_A); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step3. Register the OnMessageCallback to virtual communicator + */ + g_syncTaskContext->Initialize(DEVICE_B, g_syncInterfaceA, g_metadataA, g_virtualCommunicator); + g_virtualCommunicator->SetTimeSync(g_timeSyncA, g_timeSyncB, DEVICE_A, g_syncTaskContext); + + /** + * @tc.steps: step4. Fetch timeOffset value + * @tc.expected: step4. (offsetB - offsetA ) - timeOffset < 100ms. + */ + TimeOffset timeOffset = 0; + g_timeSyncA->GetTimeOffset(timeOffset, TIME_SYNC_WAIT_TIME); + offsetB = g_metadataB->GetLocalTimeOffset(); + offsetA = g_metadataA->GetLocalTimeOffset(); + EXPECT_TRUE(abs(offsetB - offsetA - timeOffset) < NETWORK_DELAY); +} + +/** + * @tc.name: NormalSync002 + * @tc.desc: Verify time sync function is normal between two time sync instance with the same timestamp. + * @tc.type: FUNC + * @tc.require: AR000C05EP AR000CQE0G + * @tc.author: wumin + */ +HWTEST_F(DistributedDBTimeSyncTest, NormalSync002, TestSize.Level0) +{ + /** + * @tc.steps: step1. Initialize the time sync A and B + * @tc.expected: step1. Initialize time sync A and B successfully + */ + g_metadataA->Initialize(g_syncInterfaceA); + int errCode; + // initialize timeSyncA + errCode = g_timeSyncA->Initialize(g_virtualCommunicator, g_metadataA, g_syncInterfaceA, DEVICE_B); + EXPECT_TRUE(errCode == E_OK); + + g_metadataB->Initialize(g_syncInterfaceB); + // initialize timeSyncB + errCode = g_timeSyncB->Initialize(g_virtualCommunicator, g_metadataB, g_syncInterfaceB, DEVICE_A); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step2. Register the OnMessageCallback to virtual communicator + */ + g_syncTaskContext->Initialize(DEVICE_B, g_syncInterfaceA, g_metadataA, g_virtualCommunicator); + g_virtualCommunicator->SetTimeSync(g_timeSyncA, g_timeSyncB, DEVICE_A, g_syncTaskContext); + + /** + * @tc.steps: step3. Fetch timeOffset value + * @tc.expected: step3. (offsetB - offsetA ) - timeOffset < 100ms. + */ + TimeOffset timeOffset; + g_timeSyncA->GetTimeOffset(timeOffset, TIME_SYNC_WAIT_TIME); + TimeOffset offsetB = g_metadataB->GetLocalTimeOffset(); + TimeOffset offsetA = g_metadataA->GetLocalTimeOffset(); + EXPECT_TRUE(abs(offsetB - offsetA - timeOffset) < NETWORK_DELAY); +} + +/** + * @tc.name: NormalSync003 + * @tc.desc: Verify time sync function is normal between two time sync instance with different localTimeOffset. + * @tc.type: FUNC + * @tc.require: AR000C05EP AR000CQE0G + * @tc.author: wumin + */ +HWTEST_F(DistributedDBTimeSyncTest, NormalSync003, TestSize.Level0) +{ + /** + * @tc.steps: step1. Initialize the time sync A and B + * @tc.steps: step2. Write the timeOffset into time sync A and B + * @tc.expected: step1. Initialize time sync A and B successfully + * @tc.expected: step2. Write the timeOffset into time sync A and B successfully. + */ + g_metadataA->Initialize(g_syncInterfaceA); + + // set timeOffset for timeSyncA + TimeOffset offsetA = 1; + g_metadataA->SaveLocalTimeOffset(offsetA); + + int errCode; + // initialize timeSyncA + errCode = g_timeSyncA->Initialize(g_virtualCommunicator, g_metadataA, g_syncInterfaceA, DEVICE_B); + EXPECT_TRUE(errCode == E_OK); + + // set timeOffset for timeSyncA + g_metadataB->Initialize(g_syncInterfaceB); + TimeOffset offsetB = 100 * 1000 * 1000; + g_metadataB->SaveLocalTimeOffset(offsetB); + + // initialize timeSyncB + errCode = g_timeSyncB->Initialize(g_virtualCommunicator, g_metadataB, g_syncInterfaceB, DEVICE_A); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step3. Register the OnMessageCallback to virtual communicator + */ + g_syncTaskContext->Initialize(DEVICE_B, g_syncInterfaceA, g_metadataA, g_virtualCommunicator); + g_virtualCommunicator->SetTimeSync(g_timeSyncA, g_timeSyncB, DEVICE_A, g_syncTaskContext); + + /** + * @tc.steps: step4. Fetch timeOffset value + * @tc.expected: step4. (offsetB - offsetA ) - timeOffset < 100ms. + */ + TimeOffset timeOffset = 0; + g_timeSyncA->GetTimeOffset(timeOffset, TIME_SYNC_WAIT_TIME); + + TimeOffset absTimeOffset = abs(timeOffset); + EXPECT_TRUE(abs(offsetB - offsetA - absTimeOffset) < NETWORK_DELAY); +} + +/** + * @tc.name: NetDisconnetSyncTest001 + * @tc.desc: Verify time sync function return failed when the virtual communicator disabled. + * @tc.type: FUNC + * @tc.require: AR000C05EP AR000CQE0G + * @tc.author: wumin + */ +HWTEST_F(DistributedDBTimeSyncTest, NetDisconnetSyncTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. Initialize the time sync A and B + * @tc.expected: step1. Initialize time sync A and B successfully + */ + g_metadataA->Initialize(g_syncInterfaceA); + int errCode; + // initialize timeSyncA + errCode = g_timeSyncA->Initialize(g_virtualCommunicator, g_metadataA, g_syncInterfaceA, DEVICE_B); + EXPECT_TRUE(errCode == E_OK); + + g_metadataB->Initialize(g_syncInterfaceB); + // initialize timeSyncB + errCode = g_timeSyncB->Initialize(g_virtualCommunicator, g_metadataB, g_syncInterfaceB, DEVICE_A); + EXPECT_TRUE(errCode == E_OK); + + g_syncTaskContext->Initialize(DEVICE_B, g_syncInterfaceA, g_metadataA, g_virtualCommunicator); + g_virtualCommunicator->SetTimeSync(g_timeSyncA, g_timeSyncB, DEVICE_A, g_syncTaskContext); + + /** + * @tc.steps: step2. Disable the virtual communicator + */ + g_virtualCommunicator->Disable(); + + /** + * @tc.steps: step3. Start time sync function + * @tc.expected: step3. time sync return -E_PERIPHERAL_INTERFACE_FAIL + */ + errCode = g_timeSyncA->SyncStart(); + EXPECT_TRUE(errCode == -E_PERIPHERAL_INTERFACE_FAIL); +} + +/** + * @tc.name: InvalidMessgeTest001 + * @tc.desc: Verify RequestReceive() return failed with invalid input. + * @tc.type: FUNC + * @tc.require: AR000C05EP AR000CQE0G + * @tc.author: wumin + */ +HWTEST_F(DistributedDBTimeSyncTest, InvalidMessgeTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. Initialize the time sync A and B + * @tc.expected: step1. Initialize time sync A and B successfully + */ + g_metadataA->Initialize(g_syncInterfaceA); + int errCode; + // initialize timeSyncA + errCode = g_timeSyncA->Initialize(g_virtualCommunicator, g_metadataA, g_syncInterfaceA, DEVICE_B); + EXPECT_TRUE(errCode == E_OK); + + g_metadataB->Initialize(g_syncInterfaceB); + // initialize timeSyncB + errCode = g_timeSyncB->Initialize(g_virtualCommunicator, g_metadataB, g_syncInterfaceB, DEVICE_A); + EXPECT_TRUE(errCode == E_OK); + + g_virtualCommunicator->SetTimeSync(g_timeSyncA, g_timeSyncB, DEVICE_A, g_syncTaskContext); + + Message *msg = new (std::nothrow) Message(); + ASSERT_TRUE(msg != nullptr); + + /** + * @tc.steps: step2. SendMessage with id = TIME_SYNC_MESSAGE, type = TYPE_REQUEST and no data set + * @tc.expected: step2. RequestRecv() return -E_INVALID_ARGS + */ + msg->SetMessageId(TIME_SYNC_MESSAGE); + msg->SetMessageType(TYPE_REQUEST); + SendConfig conf = {false, false, 0}; + errCode = g_virtualCommunicator->SendMessage(DEVICE_B, msg, conf); + EXPECT_TRUE(errCode == -E_INVALID_ARGS); + + TimeSyncPacket data; + data.SetSourceTimeBegin(0); + data.SetSourceTimeEnd(0); + data.SetTargetTimeBegin(0); + data.SetTargetTimeEnd(0); + + /** + * @tc.steps: step3. SendMessage with id = DATA_SYNC_MESSAGE, type = TYPE_REQUEST + * @tc.expected: step3. RequestRecv() return -E_INVALID_ARGS + */ + msg = new (std::nothrow) Message(); + ASSERT_TRUE(msg != nullptr); + msg->SetMessageId(DATA_SYNC_MESSAGE); + msg->SetMessageType(TYPE_REQUEST); + msg->SetCopiedObject<>(data); + errCode = g_virtualCommunicator->SendMessage(DEVICE_B, msg, conf); + EXPECT_TRUE(errCode == -E_INVALID_ARGS); + + /** + * @tc.steps: step4. SendMessage with id = TIME_SYNC_MESSAGE, type = TYPE_RESPONSE + * @tc.expected: step4. RequestRecv() return -E_INVALID_ARGS + */ + msg = new (std::nothrow) Message(); + ASSERT_TRUE(msg != nullptr); + msg->SetMessageId(TIME_SYNC_MESSAGE); + msg->SetMessageType(TYPE_RESPONSE); + msg->SetCopiedObject<>(data); + errCode = g_virtualCommunicator->SendMessage(DEVICE_B, msg, conf); + EXPECT_TRUE(errCode == -E_INVALID_ARGS); +} + +/** + * @tc.name: InvalidMessgeTest002 + * @tc.desc: Verify AckRec() return failed with invalid input. + * @tc.type: FUNC + * @tc.require: AR000C05EP AR000CQE0G + * @tc.author: wumin + */ +HWTEST_F(DistributedDBTimeSyncTest, InvalidMessgeTest002, TestSize.Level0) +{ + /** + * @tc.steps: step1. Initialize the time sync A and B + * @tc.expected: step1. Initialize time sync A and B successfully + */ + g_metadataA->Initialize(g_syncInterfaceA); + int errCode; + // initialize timeSyncA + errCode = g_timeSyncA->Initialize(g_virtualCommunicator, g_metadataA, g_syncInterfaceA, DEVICE_B); + EXPECT_TRUE(errCode == E_OK); + + g_metadataB->Initialize(g_syncInterfaceB); + // initialize timeSyncB + errCode = g_timeSyncB->Initialize(g_virtualCommunicator, g_metadataB, g_syncInterfaceB, DEVICE_A); + EXPECT_TRUE(errCode == E_OK); + + g_syncTaskContext->Initialize(DEVICE_B, g_syncInterfaceA, g_metadataA, g_virtualCommunicator); + g_virtualCommunicator->SetTimeSync(g_timeSyncA, g_timeSyncB, DEVICE_A, g_syncTaskContext); + + Message *msg = new (std::nothrow) Message(); + ASSERT_TRUE(msg != nullptr); + + /** + * @tc.steps: step2. SendMessage with id = TIME_SYNC_MESSAGE, type = TYPE_RESPONSE and no data set + * @tc.expected: step2. AckRecv() return -E_INVALID_ARGS + */ + msg->SetMessageId(TIME_SYNC_MESSAGE); + msg->SetMessageType(TYPE_RESPONSE); + SendConfig conf = {false, false, 0}; + errCode = g_virtualCommunicator->SendMessage(DEVICE_A, msg, conf); + EXPECT_TRUE(errCode == -E_INVALID_ARGS); + + TimeSyncPacket data; + data.SetSourceTimeBegin(0); + data.SetSourceTimeEnd(0); + data.SetTargetTimeBegin(0); + data.SetTargetTimeEnd(0); + + /** + * @tc.steps: step3. SendMessage with id = DATA_SYNC_MESSAGE, type = TYPE_RESPONSE and no data set + * @tc.expected: step3. AckRecv() return -E_INVALID_ARGS + */ + msg = new (std::nothrow) Message(); + ASSERT_TRUE(msg != nullptr); + msg->SetMessageId(DATA_SYNC_MESSAGE); + msg->SetMessageType(TYPE_RESPONSE); + msg->SetCopiedObject<>(data); + errCode = g_virtualCommunicator->SendMessage(DEVICE_A, msg, conf); + EXPECT_TRUE(errCode == -E_INVALID_ARGS); + + /** + * @tc.steps: step4. SendMessage with id = TIME_SYNC_MESSAGE, type = TYPE_REQUEST and no data set + * @tc.expected: step4. AckRecv() return -E_INVALID_ARGS + */ + msg = new (std::nothrow) Message(); + ASSERT_TRUE(msg != nullptr); + msg->SetMessageId(TIME_SYNC_MESSAGE); + msg->SetMessageType(TYPE_REQUEST); + msg->SetCopiedObject<>(data); + errCode = g_virtualCommunicator->SendMessage(DEVICE_A, msg, conf); + EXPECT_TRUE(errCode == -E_INVALID_ARGS); +} + +/** + * @tc.name: SyncTimeout001 + * @tc.desc: Verify the timeout scenario for time sync. + * @tc.type: FUNC + * @tc.require: AR000C05EP AR000CQE0G + * @tc.author: wumin + */ +HWTEST_F(DistributedDBTimeSyncTest, SyncTimeout001, TestSize.Level2) +{ + // initialize timeSyncA + g_metadataA->Initialize(g_syncInterfaceA); + int errCode; + errCode = g_timeSyncA->Initialize(g_virtualCommunicator, g_metadataA, g_syncInterfaceA, DEVICE_B); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step1. Initialize the syncTaskContext + * @tc.expected: step1. Initialize syncTaskContext successfully + */ + errCode = g_syncTaskContext->Initialize(DEVICE_B, g_syncInterfaceA, g_metadataA, g_virtualCommunicator); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step2. Start the time syc task invoking StartSync() method + * @tc.expected: step2. Start the time sync task return E_TIMEOUT + */ + TimeOffset offset; + errCode = g_timeSyncA->GetTimeOffset(offset, TIME_SYNC_WAIT_TIME); + EXPECT_TRUE(errCode == -E_TIMEOUT); +} \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/generic_virtual_device.cpp b/mock/distributeddb/test/unittest/common/syncer/generic_virtual_device.cpp new file mode 100644 index 0000000000000000000000000000000000000000..338152b18ec2e23c039f8480b31030e20a27d207 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/generic_virtual_device.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "generic_virtual_device.h" + +#include "multi_ver_sync_task_context.h" +#include "single_ver_kv_sync_task_context.h" +#include "single_ver_relational_sync_task_context.h" + +namespace DistributedDB { +GenericVirtualDevice::GenericVirtualDevice(std::string deviceId) + : communicateHandle_(nullptr), + communicatorAggregator_(nullptr), + storage_(nullptr), + metadata_(nullptr), + deviceId_(std::move(deviceId)), + remoteDeviceId_("real_device"), + context_(nullptr), + onRemoteDataChanged_(nullptr), + subManager_(nullptr) +{ +} + +GenericVirtualDevice::~GenericVirtualDevice() +{ + std::mutex cvMutex; + std::condition_variable cv; + bool finished = false; + Offline(); + + if (communicateHandle_ != nullptr) { + communicateHandle_->RegOnMessageCallback(nullptr, nullptr); + communicatorAggregator_->ReleaseCommunicator(communicateHandle_); + communicateHandle_ = nullptr; + } + communicatorAggregator_ = nullptr; + + if (context_ != nullptr) { + ISyncInterface *storage = storage_; + context_->OnLastRef([storage, &cv, &cvMutex, &finished]() { + delete storage; + { + std::lock_guard lock(cvMutex); + finished = true; + } + cv.notify_one(); + }); + RefObject::KillAndDecObjRef(context_); + std::unique_lock lock(cvMutex); + cv.wait(lock, [&finished] { return finished; }); + } else { + delete storage_; + } + context_ = nullptr; + metadata_ = nullptr; + storage_ = nullptr; +} + +int GenericVirtualDevice::Initialize(VirtualCommunicatorAggregator *comAggregator, ISyncInterface *syncInterface) +{ + if ((comAggregator == nullptr) || (syncInterface == nullptr)) { + return -E_INVALID_ARGS; + } + + communicatorAggregator_ = comAggregator; + int errCode = E_OK; + communicateHandle_ = communicatorAggregator_->AllocCommunicator(deviceId_, errCode); + if (communicateHandle_ == nullptr) { + return errCode; + } + + storage_ = syncInterface; + metadata_ = std::make_shared(); + if (metadata_->Initialize(storage_) != E_OK) { + LOGE("metadata_ init failed"); + return -E_NOT_SUPPORT; + } + if (storage_->GetInterfaceType() == IKvDBSyncInterface::SYNC_SVD) { + context_ = new (std::nothrow) SingleVerKvSyncTaskContext; + subManager_ = std::make_shared(); + static_cast(context_)->SetSubscribeManager(subManager_); + } else if (storage_->GetInterfaceType() == IKvDBSyncInterface::SYNC_RELATION) { + context_ = new (std::nothrow) SingleVerRelationalSyncTaskContext; + } else { + context_ = new (std::nothrow) MultiVerSyncTaskContext; + } + if (context_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + communicateHandle_->RegOnMessageCallback( + std::bind(&GenericVirtualDevice::MessageCallback, this, std::placeholders::_1, std::placeholders::_2), []() {}); + context_->Initialize(remoteDeviceId_, storage_, metadata_, communicateHandle_); + context_->SetRetryStatus(SyncTaskContext::NO_NEED_RETRY); + context_->RegOnSyncTask(std::bind(&GenericVirtualDevice::StartResponseTask, this)); + return E_OK; +} + +void GenericVirtualDevice::SetDeviceId(const std::string &deviceId) +{ + deviceId_ = deviceId; +} + +std::string GenericVirtualDevice::GetDeviceId() const +{ + return deviceId_; +} + +int GenericVirtualDevice::MessageCallback(const std::string &deviceId, Message *inMsg) +{ + if (inMsg->GetMessageId() == LOCAL_DATA_CHANGED) { + if (onRemoteDataChanged_) { + onRemoteDataChanged_(deviceId); + delete inMsg; + inMsg = nullptr; + return E_OK; + } + delete inMsg; + inMsg = nullptr; + return -E_INVALID_ARGS; + } + + LOGD("[GenericVirtualDevice] onMessage, src %s", deviceId.c_str()); + RefObject::IncObjRef(context_); + RefObject::IncObjRef(communicateHandle_); + SyncTaskContext *context = context_; + ICommunicator *communicateHandle = communicateHandle_; + std::thread thread([context, communicateHandle, inMsg]() { + int errCode = context->ReceiveMessageCallback(inMsg); + if (errCode != -E_NOT_NEED_DELETE_MSG) { + delete inMsg; + } + RefObject::DecObjRef(context); + RefObject::DecObjRef(communicateHandle); + }); + thread.detach(); + return E_OK; +} + +void GenericVirtualDevice::OnRemoteDataChanged(const std::function &callback) +{ + onRemoteDataChanged_ = callback; +} + +void GenericVirtualDevice::Online() +{ + static_cast(communicateHandle_)->Enable(); + communicatorAggregator_->OnlineDevice(deviceId_); +} + +void GenericVirtualDevice::Offline() +{ + static_cast(communicateHandle_)->Disable(); + communicatorAggregator_->OfflineDevice(deviceId_); +} + +int GenericVirtualDevice::StartResponseTask() +{ + LOGD("[KvVirtualDevice] StartResponseTask"); + RefObject::AutoLock lockGuard(context_); + int status = context_->GetTaskExecStatus(); + if ((status == SyncTaskContext::RUNNING) || context_->IsKilled()) { + LOGD("[KvVirtualDevice] StartResponseTask status:%d", status); + return -E_NOT_SUPPORT; + } + if (context_->IsTargetQueueEmpty()) { + LOGD("[KvVirtualDevice] StartResponseTask IsTargetQueueEmpty is empty"); + return E_OK; + } + context_->SetTaskExecStatus(ISyncTaskContext::RUNNING); + context_->MoveToNextTarget(); + LOGI("[KvVirtualDevice] machine StartSync"); + context_->UnlockObj(); + int errCode = context_->StartStateMachine(); + context_->LockObj(); + if (errCode != E_OK) { + LOGE("[KvVirtualDevice] machine StartSync failed"); + context_->SetOperationStatus(SyncOperation::OP_FAILED); + } + return errCode; +} + +TimeOffset GenericVirtualDevice::GetLocalTimeOffset() const +{ + return metadata_->GetLocalTimeOffset(); +} + +int GenericVirtualDevice::Sync(SyncMode mode, bool wait) +{ + auto operation = new (std::nothrow) SyncOperation(1, {remoteDeviceId_}, mode, nullptr, wait); + if (operation == nullptr) { + return -E_OUT_OF_MEMORY; + } + operation->Initialize(); + operation->SetOnSyncFinished([operation](int id) { + operation->NotifyIfNeed(); + }); + context_->AddSyncOperation(operation); + operation->WaitIfNeed(); + RefObject::KillAndDecObjRef(operation); + return E_OK; +} + +int GenericVirtualDevice::Sync(SyncMode mode, const Query &query, bool wait) +{ + return Sync(mode, query, nullptr, wait); +} + +int GenericVirtualDevice::Sync(SyncMode mode, const Query &query, + const SyncOperation::UserCallback &callBack, bool wait) +{ + auto operation = new (std::nothrow) SyncOperation(1, {remoteDeviceId_}, mode, callBack, wait); + if (operation == nullptr) { + return -E_OUT_OF_MEMORY; + } + operation->Initialize(); + operation->SetOnSyncFinished([operation](int id) { + operation->NotifyIfNeed(); + }); + QuerySyncObject querySyncObject(query); + int errCode = querySyncObject.Init(); + if (errCode != E_OK) { + return errCode; + } + operation->SetQuery(querySyncObject); + context_->AddSyncOperation(operation); + operation->WaitIfNeed(); + RefObject::KillAndDecObjRef(operation); + return errCode; +} +} // DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/generic_virtual_device.h b/mock/distributeddb/test/unittest/common/syncer/generic_virtual_device.h new file mode 100644 index 0000000000000000000000000000000000000000..25525870f3aecc27b3bf24a333de0ebb2eff6c67 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/generic_virtual_device.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef GENERIC_VIRTUAL_DEVICE_H +#define GENERIC_VIRTUAL_DEVICE_H + +#include + +#include "ikvdb_sync_interface.h" +#include "meta_data.h" +#include "subscribe_manager.h" +#include "sync_task_context.h" +#include "store_types.h" +#include "virtual_communicator_aggregator.h" + +namespace DistributedDB { +class GenericVirtualDevice { +public: + explicit GenericVirtualDevice(std::string deviceId); + virtual ~GenericVirtualDevice(); + + int Initialize(VirtualCommunicatorAggregator *comAggregator, ISyncInterface *syncInterface); + void SetDeviceId(const std::string &deviceId); + std::string GetDeviceId() const; + int MessageCallback(const std::string &deviceId, Message *inMsg); + void OnRemoteDataChanged(const std::function &callback); + void Online(); + void Offline(); + int StartResponseTask(); + TimeOffset GetLocalTimeOffset() const; + virtual int Sync(SyncMode mode, bool wait); + virtual int Sync(SyncMode mode, const Query &query, bool wait); + virtual int Sync(SyncMode mode, const Query &query, const SyncOperation::UserCallback &callBack, bool wait); + +protected: + ICommunicator *communicateHandle_; + VirtualCommunicatorAggregator *communicatorAggregator_; + ISyncInterface *storage_; + std::shared_ptr metadata_; + std::string deviceId_; + std::string remoteDeviceId_; + SyncTaskContext *context_; + std::function onRemoteDataChanged_; + + std::shared_ptr subManager_; +}; +} +#endif // GENERIC_VIRTUAL_DEVICE_H diff --git a/mock/distributeddb/test/unittest/common/syncer/kv_virtual_device.cpp b/mock/distributeddb/test/unittest/common/syncer/kv_virtual_device.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1cfde89e9e08139e31e3d7ce3080b671306e6879 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/kv_virtual_device.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "kv_virtual_device.h" + +#include "log_print.h" +#include "virtual_multi_ver_sync_db_interface.h" + +namespace DistributedDB { +KvVirtualDevice::KvVirtualDevice(const std::string &deviceId) : GenericVirtualDevice(deviceId) +{ +} + +KvVirtualDevice::~KvVirtualDevice() +{ +} + +int KvVirtualDevice::GetData(const Key &key, VirtualDataItem &item) +{ + VirtualSingleVerSyncDBInterface *syncAble = static_cast(storage_); + return syncAble->GetSyncData(key, item); +} + +int KvVirtualDevice::GetData(const Key &key, Value &value) +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->GetData(key, value); +} + +int KvVirtualDevice::PutData(const Key &key, const Value &value, const Timestamp &time, int flag) +{ + VirtualSingleVerSyncDBInterface *syncAble = static_cast(storage_); + LOGI("dev %s put data time %" PRIu64, deviceId_.c_str(), time); + return syncAble->PutData(key, value, time, flag); +} + +int KvVirtualDevice::PutData(const Key &key, const Value &value) +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->PutData(key, value); +} + +int KvVirtualDevice::DeleteData(const Key &key) +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->DeleteData(key); +} + +int KvVirtualDevice::StartTransaction() +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->StartTransaction(); +} + +int KvVirtualDevice::Commit() +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->Commit(); +} + + +void KvVirtualDevice::SetSaveDataDelayTime(uint64_t milliDelayTime) +{ + VirtualSingleVerSyncDBInterface *syncInterface = static_cast(storage_); + syncInterface->SetSaveDataDelayTime(milliDelayTime); +} + +int KvVirtualDevice::Subscribe(QuerySyncObject query, bool wait, int id) +{ + auto operation = new (std::nothrow) SyncOperation(id, {remoteDeviceId_}, SUBSCRIBE_QUERY, nullptr, wait); + if (operation == nullptr) { + return -E_OUT_OF_MEMORY; + } + operation->Initialize(); + operation->SetOnSyncFinished([operation](int id) { + operation->NotifyIfNeed(); + }); + operation->SetQuery(query); + context_->AddSyncOperation(operation); + operation->WaitIfNeed(); + RefObject::KillAndDecObjRef(operation); + return E_OK; +} + +int KvVirtualDevice::UnSubscribe(QuerySyncObject query, bool wait, int id) +{ + auto operation = new (std::nothrow) SyncOperation(id, {remoteDeviceId_}, UNSUBSCRIBE_QUERY, nullptr, wait); + if (operation == nullptr) { + return -E_OUT_OF_MEMORY; + } + operation->Initialize(); + operation->SetOnSyncFinished([operation](int id) { + operation->NotifyIfNeed(); + }); + operation->SetQuery(query); + context_->AddSyncOperation(operation); + operation->WaitIfNeed(); + RefObject::KillAndDecObjRef(operation); + return E_OK; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/kv_virtual_device.h b/mock/distributeddb/test/unittest/common/syncer/kv_virtual_device.h new file mode 100644 index 0000000000000000000000000000000000000000..220aabc4b41d30ffb03c574df6ed69811a5e5ad0 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/kv_virtual_device.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KV_VIRTUAL_DEVICE_H +#define KV_VIRTUAL_DEVICE_H + +#include "generic_virtual_device.h" + +#include "virtual_single_ver_sync_db_Interface.h" +namespace DistributedDB { +class KvVirtualDevice final : public GenericVirtualDevice { +public: + explicit KvVirtualDevice(const std::string &deviceId); + ~KvVirtualDevice() override; + + int GetData(const Key &key, VirtualDataItem &item); + int GetData(const Key &key, Value &value); + int PutData(const Key &key, const Value &value, const Timestamp &time, int flag); + int PutData(const Key &key, const Value &value); + int DeleteData(const Key &key); + int StartTransaction(); + int Commit(); + void SetSaveDataDelayTime(uint64_t milliDelayTime); + + int Subscribe(QuerySyncObject query, bool wait, int id); + int UnSubscribe(QuerySyncObject query, bool wait, int id); +}; +} // namespace DistributedDB + +#endif // KV_VIRTUAL_DEVICE_H diff --git a/mock/distributeddb/test/unittest/common/syncer/mock_auto_launch.h b/mock/distributeddb/test/unittest/common/syncer/mock_auto_launch.h new file mode 100644 index 0000000000000000000000000000000000000000..af89a85aea477859a44b8ae9387d478ed6b1cebe --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/mock_auto_launch.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCK_AUTO_LAUNCH_H +#define MOCK_AUTO_LAUNCH_H + +#include +#include "auto_launch.h" + +namespace DistributedDB { +class MockAutoLaunch : public AutoLaunch { +public: + void SetAutoLaunchItem(const std::string &identify, const std::string &userId, AutoLaunchItem &item) + { + std::lock_guard autoLock(extLock_); + extItemMap_[identify][userId] = item; + } + + void CallExtConnectionLifeCycleCallbackTask(const std::string &identifier, const std::string &userId) + { + AutoLaunch::ExtConnectionLifeCycleCallbackTask(identifier, userId); + } + + MOCK_METHOD1(TryCloseConnection, void(AutoLaunchItem &)); +}; +} // namespace DistributedDB +#endif // #define MOCK_AUTO_LAUNCH_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/mock_communicator.h b/mock/distributeddb/test/unittest/common/syncer/mock_communicator.h new file mode 100644 index 0000000000000000000000000000000000000000..aab37dff664551735ea66bb2c2ab23787a3cd19d --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/mock_communicator.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCK_COMMUNICATOR_H +#define MOCK_COMMUNICATOR_H + +#include +#include "icommunicator.h" + +namespace DistributedDB { +class MockCommunicator : public ICommunicator { +public: + MOCK_CONST_METHOD1(GetLocalIdentity, int(std::string &)); + MOCK_CONST_METHOD2(GetRemoteCommunicatorVersion, int(const std::string &, uint16_t &)); + MOCK_METHOD3(SendMessage, int(const std::string &, const Message *, const SendConfig &)); + MOCK_METHOD4(SendMessage, int(const std::string &, const Message *, const SendConfig &, const OnSendEnd &)); + MOCK_CONST_METHOD0(GetCommunicatorMtuSize, uint32_t(void)); + MOCK_CONST_METHOD1(GetCommunicatorMtuSize, uint32_t(const std::string &)); + MOCK_CONST_METHOD0(GetTimeout, uint32_t(void)); + MOCK_CONST_METHOD1(GetTimeout, uint32_t(const std::string &)); + MOCK_METHOD2(RegOnConnectCallback, int(const OnConnectCallback &, const Finalizer &)); + MOCK_METHOD2(RegOnSendableCallback, int(const std::function &, const Finalizer &)); + MOCK_METHOD2(RegOnMessageCallback, int(const OnMessageCallback &, const Finalizer &)); + MOCK_METHOD0(Activate, void(void)); + MOCK_CONST_METHOD1(IsDeviceOnline, bool(const std::string &)); +}; +} // namespace DistributedDB +#endif // #define MOCK_COMMUNICATOR_H + diff --git a/mock/distributeddb/test/unittest/common/syncer/mock_meta_data.h b/mock/distributeddb/test/unittest/common/syncer/mock_meta_data.h new file mode 100644 index 0000000000000000000000000000000000000000..c1e83d6e1650e6722ce9d712d6e85e7b6cd6f0f9 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/mock_meta_data.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MOCK_META_DATA_H +#define MOCK_META_DATA_H + +#include +#include "meta_data.h" + +namespace DistributedDB { +class MockMetadata : public Metadata { +public: + MOCK_METHOD3(SetLastQueryTime, int(const std::string &, const std::string &, const Timestamp &)); + + MOCK_METHOD3(GetLastQueryTime, int(const std::string &, const std::string &, Timestamp &)); +}; +} // namespace DistributedDB +#endif // #define MOCK_META_DATA_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/mock_single_ver_data_sync.h b/mock/distributeddb/test/unittest/common/syncer/mock_single_ver_data_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..550ece3dbb93050b87c596a432cc3cd425a6c0a2 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/mock_single_ver_data_sync.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCK_SINGLE_VER_DATA_SYNC_H +#define MOCK_SINGLE_VER_DATA_SYNC_H + +#include +#include "single_ver_data_sync.h" + +namespace DistributedDB { +class MockSingleVerDataSync : public SingleVerDataSync { +public: + int CallRequestStart(SingleVerSyncTaskContext *context, int mode) + { + return SingleVerDataSync::RequestStart(context, mode); + } + + int CallPullRequestStart(SingleVerSyncTaskContext *context) + { + return SingleVerDataSync::PullRequestStart(context); + } + + void CallUpdateSendInfo(SyncTimeRange dataTimeRange, SingleVerSyncTaskContext *context) + { + SingleVerDataSync::UpdateSendInfo(dataTimeRange, context); + } + + int CallRemoveDeviceDataIfNeed(SingleVerSyncTaskContext *context) + { + return SingleVerDataSync::RemoveDeviceDataIfNeed(context); + } + + MOCK_METHOD1(RemoveDeviceDataIfNeed, int(SingleVerSyncTaskContext *)); +}; +} // namespace DistributedDB +#endif // #define MOCK_SINGLE_VER_DATA_SYNC_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/mock_single_ver_state_machine.h b/mock/distributeddb/test/unittest/common/syncer/mock_single_ver_state_machine.h new file mode 100644 index 0000000000000000000000000000000000000000..878dd420ce014792f1ffa4209855a7a6ec602338 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/mock_single_ver_state_machine.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCK_SINGLE_VER_STATE_MACHINE_H +#define MOCK_SINGLE_VER_STATE_MACHINE_H + +#include +#include "single_ver_sync_state_machine.h" + +namespace DistributedDB { +class MockSingleVerStateMachine : public SingleVerSyncStateMachine { +public: + void CallStepToTimeout(TimerId timerId) + { + SingleVerSyncStateMachine::StepToTimeout(timerId); + } + + int CallExecNextTask() + { + return SyncStateMachine::ExecNextTask(); + } + + int CallTimeMarkSyncRecv(const Message *inMsg) + { + return SingleVerSyncStateMachine::TimeMarkSyncRecv(inMsg); + } + + void CallDataAckRecvErrCodeHandle(int errCode, bool handleError) + { + SingleVerSyncStateMachine::DataAckRecvErrCodeHandle(errCode, handleError); + } + + bool CallStartSaveDataNotify(uint32_t sessionId, uint32_t sequenceId, uint32_t inMsgId) + { + return SingleVerSyncStateMachine::StartSaveDataNotify(sessionId, sequenceId, inMsgId); + } + + void CallStopSaveDataNotify() + { + SingleVerSyncStateMachine::StopSaveDataNotify(); + } + + MOCK_METHOD1(SwitchStateAndStep, void(uint8_t)); + + MOCK_METHOD0(PrepareNextSyncTask, int(void)); + + MOCK_METHOD3(DoSaveDataNotify, void(uint32_t, uint32_t, uint32_t)); +}; +} // namespace DistributedDB +#endif // #define MOCK_SINGLE_VER_STATE_MACHINE_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/mock_sync_task_context.h b/mock/distributeddb/test/unittest/common/syncer/mock_sync_task_context.h new file mode 100644 index 0000000000000000000000000000000000000000..95a999c6a7c41ff88922d2eb9aba1ae07437dd46 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/mock_sync_task_context.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCK_SYNC_TASK_CONTEXT_H +#define MOCK_SYNC_TASK_CONTEXT_H + +#include +#include "single_ver_kv_sync_task_context.h" + +namespace DistributedDB { +class MockSyncTaskContext : public SingleVerKvSyncTaskContext { +public: + bool CallIsCurrentSyncTaskCanBeSkipped() + { + return SingleVerKvSyncTaskContext::IsCurrentSyncTaskCanBeSkipped(); + } + + void CallSetSyncMode(int mode) + { + SingleVerKvSyncTaskContext::SetMode(mode); + } + + MOCK_CONST_METHOD0(GetTimerId, TimerId(void)); + + MOCK_CONST_METHOD0(IsTargetQueueEmpty, bool(void)); + + MOCK_METHOD0(MoveToNextTarget, void(void)); + + MOCK_CONST_METHOD0(IsCurrentSyncTaskCanBeSkipped, bool(void)); + + MOCK_METHOD1(SetOperationStatus, void(int)); + + MOCK_METHOD1(SetTaskExecStatus, void(int)); + + MOCK_METHOD0(Clear, void(void)); + + MOCK_CONST_METHOD0(GetRequestSessionId, uint32_t(void)); + + MOCK_CONST_METHOD1(GetSyncStrategy, SyncStrategy(QuerySyncObject &)); +}; +} // namespace DistributedDB +#endif // #define MOCK_SINGLE_VER_STATE_MACHINE_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/relational_virtual_device.cpp b/mock/distributeddb/test/unittest/common/syncer/relational_virtual_device.cpp new file mode 100644 index 0000000000000000000000000000000000000000..816795b6c99c9ae00020b23dc9d97758d39599b8 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/relational_virtual_device.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "relational_virtual_device.h" +#include "virtual_relational_ver_sync_db_interface.h" +namespace DistributedDB { +RelationalVirtualDevice::RelationalVirtualDevice(const std::string &deviceId) : GenericVirtualDevice(deviceId) +{ +} + +RelationalVirtualDevice::~RelationalVirtualDevice() +{ +} + +int RelationalVirtualDevice::PutData(const std::string &tableName, const std::vector &dataList) +{ + return static_cast(storage_)->PutLocalData(dataList, tableName); +} + +int RelationalVirtualDevice::GetAllSyncData(const std::string &tableName, std::vector &data) +{ + return static_cast(storage_)->GetAllSyncData(tableName, data); +} + +int RelationalVirtualDevice::GetSyncData(const std::string &tableName, + const std::string &hashKey, VirtualRowData &data) +{ + return static_cast(storage_)->GetVirtualSyncData(tableName, hashKey, data); +} + +void RelationalVirtualDevice::SetLocalFieldInfo(const std::vector &localFieldInfo) +{ + static_cast(storage_)->SetLocalFieldInfo(localFieldInfo); +} + +int RelationalVirtualDevice::Sync(SyncMode mode, bool wait) +{ + return -E_NOT_SUPPORT; +} + +void RelationalVirtualDevice::EraseSyncData(const std::string &tableName) +{ + static_cast(storage_)->EraseSyncData(tableName); +} + +void RelationalVirtualDevice::SetTableInfo(const TableInfo &tableInfo) +{ + static_cast(storage_)->SetTableInfo(tableInfo); +} +} // DistributedDB +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/relational_virtual_device.h b/mock/distributeddb/test/unittest/common/syncer/relational_virtual_device.h new file mode 100644 index 0000000000000000000000000000000000000000..9aed0bbf3988306eefc314a125fa7099b630db19 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/relational_virtual_device.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RELATIONAL_VIRTUAL_DEVICE_H +#define RELATIONAL_VIRTUAL_DEVICE_H +#ifdef RELATIONAL_STORE + +#include "data_transformer.h" +#include "generic_virtual_device.h" +#include "relational_schema_object.h" +#include "virtual_relational_ver_sync_db_interface.h" + +namespace DistributedDB { +class RelationalVirtualDevice final : public GenericVirtualDevice { +public: + explicit RelationalVirtualDevice(const std::string &deviceId); + ~RelationalVirtualDevice() override; + + int PutData(const std::string &tableName, const std::vector &dataList); + int GetAllSyncData(const std::string &tableName, std::vector &data); + int GetSyncData(const std::string &tableName, const std::string &hashKey, VirtualRowData &data); + void SetLocalFieldInfo(const std::vector &localFieldInfo); + void SetTableInfo(const TableInfo &tableInfo); + int Sync(SyncMode mode, bool wait) override; + void EraseSyncData(const std::string &tableName); +}; +} +#endif +#endif // RELATIONAL_VIRTUAL_DEVICE_H diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_communicator.cpp b/mock/distributeddb/test/unittest/common/syncer/virtual_communicator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..caed2ea2709e2b643ab19df2ea08f768f248eabe --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_communicator.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "virtual_communicator.h" + +#include "log_print.h" +#include "sync_engine.h" +#include "virtual_communicator_aggregator.h" + +namespace DistributedDB { +int VirtualCommunicator::RegOnMessageCallback(const OnMessageCallback &onMessage, const Finalizer &inOper) +{ + std::lock_guard lock(onMessageLock_); + onMessage_ = onMessage; + return E_OK; +} + +int VirtualCommunicator::RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) +{ + std::lock_guard lock(onConnectLock_); + onConnect_ = onConnect; + return E_OK; +} + +int VirtualCommunicator::RegOnSendableCallback(const std::function &onSendable, const Finalizer &inOper) +{ + return E_OK; +} + +void VirtualCommunicator::Activate() +{ +} + +int VirtualCommunicator::SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config) +{ + return SendMessage(dstTarget, inMsg, config, nullptr); +} + +int VirtualCommunicator::SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config, + const OnSendEnd &onEnd) +{ + AutoLock lock(this); + if (IsKilled()) { + return -E_OBJ_IS_KILLED; + } + if (!isEnable_) { + LOGD("[VirtualCommunicator] the VirtualCommunicator disabled!"); + return -E_PERIPHERAL_INTERFACE_FAIL; + } + if (dstTarget == deviceId_) { + delete inMsg; + inMsg = nullptr; + return E_OK; + } + communicatorAggregator_->DispatchMessage(deviceId_, dstTarget, inMsg, onEnd); + return E_OK; +} + +int VirtualCommunicator::GetRemoteCommunicatorVersion(const std::string &deviceId, uint16_t &version) const +{ + version = UINT16_MAX; + return E_OK; +} + +void VirtualCommunicator::CallbackOnMessage(const std::string &srcTarget, Message *inMsg) const +{ + std::lock_guard lock(onMessageLock_); + if (isEnable_ && onMessage_ && (srcTarget != deviceId_)) { + onMessage_(srcTarget, inMsg); + } else { + delete inMsg; + inMsg = nullptr; + } +} + +void VirtualCommunicator::CallbackOnConnect(const std::string &target, bool isConnect) const +{ + { + std::lock_guard lock(devicesMapLock_); + if (target != deviceId_) { + onlineDevicesMap_[target] = isConnect; + } + } + std::lock_guard lock(onConnectLock_); + if (isEnable_ && onConnect_) { + onConnect_(target, isConnect); + } +} + +uint32_t VirtualCommunicator::GetCommunicatorMtuSize() const +{ + return 5 * 1024 * 1024; // 5 * 1024 * 1024B +} + +uint32_t VirtualCommunicator::GetCommunicatorMtuSize(const std::string &target) const +{ + return GetCommunicatorMtuSize(); +} + +uint32_t VirtualCommunicator::GetTimeout() const +{ + return 5 * 1000; // 5 * 1000ms +} + +uint32_t VirtualCommunicator::GetTimeout(const std::string &target) const +{ + return GetTimeout(); +} + +int VirtualCommunicator::GetLocalIdentity(std::string &outTarget) const +{ + outTarget = deviceId_; + return E_OK; +} + +int VirtualCommunicator::GeneralVirtualSyncId() +{ + std::lock_guard lock(syncIdLock_); + currentSyncId_++; + return currentSyncId_; +} + +void VirtualCommunicator::Disable() +{ + isEnable_ = false; +} + +void VirtualCommunicator::Enable() +{ + isEnable_ = true; +} + +void VirtualCommunicator::SetDeviceId(const std::string &deviceId) +{ + deviceId_ = deviceId; +} + +std::string VirtualCommunicator::GetDeviceId() const +{ + return deviceId_; +} + +bool VirtualCommunicator::IsEnabled() const +{ + return isEnable_; +} + +bool VirtualCommunicator::IsDeviceOnline(const std::string &device) const +{ + bool res = true; + { + std::lock_guard lock(devicesMapLock_); + if (onlineDevicesMap_.find(device) != onlineDevicesMap_.end()) { + res = onlineDevicesMap_[device]; + } + } + return res; +} + +VirtualCommunicator::~VirtualCommunicator() +{ +} + +VirtualCommunicator::VirtualCommunicator(const std::string &deviceId, + VirtualCommunicatorAggregator *communicatorAggregator) + : deviceId_(deviceId), communicatorAggregator_(communicatorAggregator) +{ +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_communicator.h b/mock/distributeddb/test/unittest/common/syncer/virtual_communicator.h new file mode 100644 index 0000000000000000000000000000000000000000..bd68e82f584bb943bca53999b6307e9b5def35b8 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_communicator.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIRTUAL_COMMUNICATOR_H +#define VIRTUAL_COMMUNICATOR_H + +#include +#include +#include +#include +#include +#include +#include + +#include "icommunicator.h" +#include "ref_object.h" +#include "serial_buffer.h" + +namespace DistributedDB { +class VirtualCommunicatorAggregator; + +class VirtualCommunicator : public ICommunicator { +public: + VirtualCommunicator(const std::string &deviceId, VirtualCommunicatorAggregator *communicatorAggregator); + ~VirtualCommunicator() override; + + DISABLE_COPY_ASSIGN_MOVE(VirtualCommunicator); + + int RegOnMessageCallback(const OnMessageCallback &onMessage, const Finalizer &inOper) override; + int RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) override; + int RegOnSendableCallback(const std::function &onSendable, const Finalizer &inOper) override; + + void Activate() override; + + uint32_t GetCommunicatorMtuSize() const override; + uint32_t GetCommunicatorMtuSize(const std::string &target) const override; + + uint32_t GetTimeout() const override; + uint32_t GetTimeout(const std::string &target) const override; + int GetLocalIdentity(std::string &outTarget) const override; + + int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config) override; + int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config, + const OnSendEnd &onEnd) override; + + int GetRemoteCommunicatorVersion(const std::string &deviceId, uint16_t &version) const override; + + void CallbackOnMessage(const std::string &srcTarget, Message *inMsg) const; + + void CallbackOnConnect(const std::string &target, bool isConnect) const; + + int GeneralVirtualSyncId(); + + void Disable(); + + void Enable(); + + void SetDeviceId(const std::string &deviceId); + + std::string GetDeviceId() const; + + bool IsEnabled() const; + + bool IsDeviceOnline(const std::string &device) const override; + +private: + int TimeSync(); + int DataSync(); + int WaterMarkSync(); + + mutable std::mutex onMessageLock_; + OnMessageCallback onMessage_; + + mutable std::mutex onConnectLock_; + OnConnectCallback onConnect_; + mutable std::mutex devicesMapLock_; + mutable std::map onlineDevicesMap_; + + std::string remoteDeviceId_ = "real_device"; + std::mutex syncIdLock_; + int currentSyncId_ = 1000; + bool isEnable_ = true; + std::string deviceId_; + + std::mutex onAggregatorLock_; + VirtualCommunicatorAggregator *communicatorAggregator_; +}; +} // namespace DistributedDB + +#endif // VIRTUAL_COMMUNICATOR_H diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_communicator_aggregator.cpp b/mock/distributeddb/test/unittest/common/syncer/virtual_communicator_aggregator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..561a3f46c30143f53673f083978ac0ebd24091c6 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_communicator_aggregator.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "virtual_communicator_aggregator.h" + +#include +#include +#include + +#include "db_errno.h" +#include "log_print.h" +#include "runtime_context.h" + +namespace DistributedDB { +int VirtualCommunicatorAggregator::Initialize(IAdapter *inAdapter) +{ + return E_OK; +} + +void VirtualCommunicatorAggregator::Finalize() +{ +} + +// If not success, return nullptr and set outErrorNo +ICommunicator *VirtualCommunicatorAggregator::AllocCommunicator(uint64_t commLabel, int &outErrorNo) +{ + if (isEnable_) { + return AllocCommunicator(remoteDeviceId_, outErrorNo); + } + return nullptr; +} + +ICommunicator *VirtualCommunicatorAggregator::AllocCommunicator(const LabelType &commLabel, int &outErrorNo) +{ + if (isEnable_) { + return AllocCommunicator(remoteDeviceId_, outErrorNo); + } + return nullptr; +} + +void VirtualCommunicatorAggregator::ReleaseCommunicator(ICommunicator *inCommunicator) +{ + // Called in main thread only + VirtualCommunicator *communicator = static_cast(inCommunicator); + OfflineDevice(communicator->GetDeviceId()); + { + std::lock_guard lock(communicatorsLock_); + communicators_.erase(communicator->GetDeviceId()); + } + RefObject::KillAndDecObjRef(communicator); + communicator = nullptr; +} + +int VirtualCommunicatorAggregator::RegCommunicatorLackCallback(const CommunicatorLackCallback &onCommLack, + const Finalizer &inOper) +{ + onCommLack_ = onCommLack; + return E_OK; +} + +int VirtualCommunicatorAggregator::RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) +{ + onConnect_ = onConnect; + RunOnConnectCallback("deviceId", true); + return E_OK; +} + +void VirtualCommunicatorAggregator::RunCommunicatorLackCallback(const LabelType &commLabel) +{ + if (onCommLack_) { + std::string userId; + onCommLack_(commLabel, userId_); + } +} + +void VirtualCommunicatorAggregator::RunOnConnectCallback(const std::string &target, bool isConnect) +{ + if (onConnect_) { + onConnect_(target, isConnect); + } +} + +int VirtualCommunicatorAggregator::GetLocalIdentity(std::string &outTarget) const +{ + return E_OK; +} + +void VirtualCommunicatorAggregator::OnlineDevice(const std::string &deviceId) const +{ + if (!isEnable_) { + return; + } + + // Called in main thread only + for (const auto &iter : communicators_) { + VirtualCommunicator *communicatorTmp = static_cast(iter.second); + if (iter.first != deviceId) { + communicatorTmp->CallbackOnConnect(deviceId, true); + } + } +} + +void VirtualCommunicatorAggregator::OfflineDevice(const std::string &deviceId) const +{ + if (!isEnable_) { + return; + } + + // Called in main thread only + for (const auto &iter : communicators_) { + VirtualCommunicator *communicatorTmp = static_cast(iter.second); + if (iter.first != deviceId) { + communicatorTmp->CallbackOnConnect(deviceId, false); + } + } +} + +ICommunicator *VirtualCommunicatorAggregator::AllocCommunicator(const std::string &deviceId, int &outErrorNo) +{ + // Called in main thread only + VirtualCommunicator *communicator = new (std::nothrow) VirtualCommunicator(deviceId, this); + if (communicator == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + } + { + std::lock_guard lock(communicatorsLock_); + communicators_.insert(std::pair(deviceId, communicator)); + } + OnlineDevice(deviceId); + return communicator; +} + +ICommunicator *VirtualCommunicatorAggregator::GetCommunicator(const std::string &deviceId) const +{ + std::lock_guard lock(communicatorsLock_); + auto iter = communicators_.find(deviceId); + if (iter != communicators_.end()) { + VirtualCommunicator *communicator = static_cast(iter->second); + return communicator; + } + return nullptr; +} + +void VirtualCommunicatorAggregator::DispatchMessage(const std::string &srcTarget, const std::string &dstTarget, + const Message *inMsg, const OnSendEnd &onEnd) +{ + if (VirtualCommunicatorAggregator::GetBlockValue()) { + std::unique_lock lock(blockLock_); + conditionVar_.wait(lock); + } + + if (!isEnable_) { + LOGD("[VirtualCommunicatorAggregator] DispatchMessage, VirtualCommunicatorAggregator is disabled"); + delete inMsg; + inMsg = nullptr; + return CallSendEnd(-E_PERIPHERAL_INTERFACE_FAIL, onEnd); + } + std::lock_guard lock(communicatorsLock_); + auto iter = communicators_.find(dstTarget); + if (iter != communicators_.end()) { + LOGE("[VirtualCommunicatorAggregator] DispatchMessage, find dstTarget %s", dstTarget.c_str()); + VirtualCommunicator *communicator = static_cast(iter->second); + if (!communicator->IsEnabled()) { + LOGE("[VirtualCommunicatorAggregator] DispatchMessage, find dstTarget %s disabled", dstTarget.c_str()); + delete inMsg; + inMsg = nullptr; + return CallSendEnd(-E_PERIPHERAL_INTERFACE_FAIL, onEnd); + } + Message *msg = const_cast(inMsg); + msg->SetTarget(srcTarget); + RefObject::IncObjRef(communicator); + auto onDispatch = onDispatch_; + std::thread thread([communicator, srcTarget, dstTarget, msg, onDispatch]() { + if (onDispatch) { + onDispatch(dstTarget, msg); + } + communicator->CallbackOnMessage(srcTarget, msg); + RefObject::DecObjRef(communicator); + }); + thread.detach(); + CallSendEnd(E_OK, onEnd); + } else { + LOGE("[VirtualCommunicatorAggregator] DispatchMessage, can't find dstTarget %s", dstTarget.c_str()); + delete inMsg; + inMsg = nullptr; + CallSendEnd(-E_NOT_FOUND, onEnd); + } +} + +void VirtualCommunicatorAggregator::SetBlockValue(bool value) +{ + std::unique_lock lock(blockLock_); + isBlock_ = value; + if (!value) { + conditionVar_.notify_all(); + } +} + +bool VirtualCommunicatorAggregator::GetBlockValue() const +{ + return isBlock_; +} + +void VirtualCommunicatorAggregator::Disable() +{ + isEnable_ = false; +} + +void VirtualCommunicatorAggregator::Enable() +{ + LOGD("[VirtualCommunicatorAggregator] enable"); + isEnable_ = true; +} + +void VirtualCommunicatorAggregator::CallSendEnd(int errCode, const OnSendEnd &onEnd) +{ + if (onEnd) { + (void)RuntimeContext::GetInstance()->ScheduleTask([errCode, onEnd]() { + onEnd(errCode); + }); + } +} + +void VirtualCommunicatorAggregator::RegOnDispatch( + const std::function &onDispatch) +{ + onDispatch_ = onDispatch; +} + +void VirtualCommunicatorAggregator::SetCurrentUserId(const std::string &userId) +{ + userId_ = userId; +} +} // namespace DistributedDB + diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_communicator_aggregator.h b/mock/distributeddb/test/unittest/common/syncer/virtual_communicator_aggregator.h new file mode 100644 index 0000000000000000000000000000000000000000..22db8b586c7f60bce98f337dda3621bca0501baf --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_communicator_aggregator.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIRTUAL_ICOMMUNICATORAGGREGATOR_H +#define VIRTUAL_ICOMMUNICATORAGGREGATOR_H + +#include + +#include "icommunicator_aggregator.h" +#include "virtual_communicator.h" + +namespace DistributedDB { +class ICommunicator; // Forward Declaration + +class VirtualCommunicatorAggregator : public ICommunicatorAggregator { +public: + // Return 0 as success. Return negative as error + int Initialize(IAdapter *inAdapter) override; + + void Finalize() override; + + // If not success, return nullptr and set outErrorNo + ICommunicator *AllocCommunicator(uint64_t commLabel, int &outErrorNo) override; + ICommunicator *AllocCommunicator(const LabelType &commLabel, int &outErrorNo) override; + + void ReleaseCommunicator(ICommunicator *inCommunicator) override; + + int RegCommunicatorLackCallback(const CommunicatorLackCallback &onCommLack, const Finalizer &inOper) override; + int RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) override; + void RunCommunicatorLackCallback(const LabelType &commLabel); + void RunOnConnectCallback(const std::string &target, bool isConnect); + + int GetLocalIdentity(std::string &outTarget) const override; + + // online a virtual device to the VirtualCommunicator, should call in main thread + void OnlineDevice(const std::string &deviceId) const; + + // offline a virtual device to the VirtualCommunicator, should call in main thread + void OfflineDevice(const std::string &deviceId) const; + + void DispatchMessage(const std::string &srcTarget, const std::string &dstTarget, const Message *inMsg, + const OnSendEnd &onEnd); + + // If not success, return nullptr and set outErrorNo + ICommunicator *AllocCommunicator(const std::string &deviceId, int &outErrorNo); + + ICommunicator *GetCommunicator(const std::string &deviceId) const; + + void Disable(); + + void Enable(); + + void SetBlockValue(bool value); + + bool GetBlockValue() const; + + void RegOnDispatch(const std::function &onDispatch); + + void SetCurrentUserId(const std::string &userId); + + ~VirtualCommunicatorAggregator() {}; + VirtualCommunicatorAggregator() {}; + +private: + void CallSendEnd(int errCode, const OnSendEnd &onEnd); + + mutable std::mutex communicatorsLock_; + std::map communicators_; + std::string remoteDeviceId_ = "real_device"; + std::mutex blockLock_; + std::condition_variable conditionVar_; + bool isEnable_ = true; + bool isBlock_ = false; + CommunicatorLackCallback onCommLack_; + OnConnectCallback onConnect_; + std::function onDispatch_; + std::string userId_; +}; +} // namespace DistributedDB + +#endif // VIRTUAL_ICOMMUNICATORAGGREGATOR_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.cpp b/mock/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fffec2abc0ec5773be64a91f229ff68d461d339b --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "virtual_multi_ver_sync_db_interface.h" + +#include "db_common.h" +#include "kvdb_manager.h" +#include "multi_ver_natural_store_snapshot.h" + +namespace DistributedDB { +VirtualMultiVerSyncDBInterface::VirtualMultiVerSyncDBInterface() : kvStore_(nullptr), connection_(nullptr) +{ +} + +VirtualMultiVerSyncDBInterface::~VirtualMultiVerSyncDBInterface() +{ + DeleteDatabase(); +} + +int VirtualMultiVerSyncDBInterface::GetInterfaceType() const +{ + return IKvDBSyncInterface::SYNC_MVD; +} + +void VirtualMultiVerSyncDBInterface::IncRefCount() +{ + kvStore_->IncRefCount(); +} + +void VirtualMultiVerSyncDBInterface::DecRefCount() +{ + kvStore_->DecRefCount(); +} + +std::vector VirtualMultiVerSyncDBInterface::GetIdentifier() const +{ + return kvStore_->GetIdentifier(); +} + +void VirtualMultiVerSyncDBInterface::GetMaxTimestamp(Timestamp &stamp) const +{ + return kvStore_->GetMaxTimestamp(stamp); +} + +int VirtualMultiVerSyncDBInterface::GetMetaData(const Key &key, Value &value) const +{ + return kvStore_->GetMetaData(key, value); +} + +int VirtualMultiVerSyncDBInterface::PutMetaData(const Key &key, const Value &value) +{ + return kvStore_->PutMetaData(key, value); +} + +int VirtualMultiVerSyncDBInterface::DeleteMetaData(const std::vector &keys) +{ + return kvStore_->DeleteMetaData(keys); +} + +int VirtualMultiVerSyncDBInterface::GetAllMetaKeys(std::vector &keys) const +{ + return kvStore_->GetAllMetaKeys(keys); +} + +bool VirtualMultiVerSyncDBInterface::IsCommitExisted(const MultiVerCommitNode &commit) const +{ + return kvStore_->IsCommitExisted(commit); +} + +int VirtualMultiVerSyncDBInterface::GetDeviceLatestCommit(std::map &commits) const +{ + return kvStore_->GetDeviceLatestCommit(commits); +} + +int VirtualMultiVerSyncDBInterface::GetCommitTree(const std::map &inCommit, + std::vector &outCommit) const +{ + return kvStore_->GetCommitTree(inCommit, outCommit); +} + +int VirtualMultiVerSyncDBInterface::GetCommitData(const MultiVerCommitNode &commit, + std::vector &entries) const +{ + return kvStore_->GetCommitData(commit, entries); +} + +MultiVerKvEntry *VirtualMultiVerSyncDBInterface::CreateKvEntry(const std::vector &entries) +{ + return kvStore_->CreateKvEntry(entries); +} + +void VirtualMultiVerSyncDBInterface::ReleaseKvEntry(const MultiVerKvEntry *entry) +{ + return kvStore_->ReleaseKvEntry(entry); +} + +bool VirtualMultiVerSyncDBInterface::IsValueSliceExisted(const ValueSliceHash &value) const +{ + return kvStore_->IsValueSliceExisted(value); +} + +int VirtualMultiVerSyncDBInterface::GetValueSlice(const ValueSliceHash &hashValue, ValueSlice &sliceValue) const +{ + return kvStore_->GetValueSlice(hashValue, sliceValue); +} + +int VirtualMultiVerSyncDBInterface::PutValueSlice(const ValueSliceHash &hashValue, const ValueSlice &sliceValue) const +{ + return kvStore_->PutValueSlice(hashValue, sliceValue); +} + +int VirtualMultiVerSyncDBInterface::PutCommitData(const MultiVerCommitNode &commit, + const std::vector &entries, const std::string &deviceName) +{ + return kvStore_->PutCommitData(commit, entries, deviceName); +} + +int VirtualMultiVerSyncDBInterface::MergeSyncCommit(const MultiVerCommitNode &commit, + const std::vector &commits) +{ + return kvStore_->MergeSyncCommit(commit, commits); +} + +int VirtualMultiVerSyncDBInterface::TransferSyncCommitDevInfo(MultiVerCommitNode &commit, const std::string &devId, + bool isSyncedIn) const +{ + return kvStore_->TransferSyncCommitDevInfo(commit, devId, isSyncedIn); +} + +int VirtualMultiVerSyncDBInterface::Initialize(const std::string &deviceId) +{ + std::string dir; + testTool_.TestDirInit(dir); + KvDBProperties prop; + prop.SetStringProp(KvDBProperties::USER_ID, "sync_test"); + prop.SetStringProp(KvDBProperties::APP_ID, "sync_test"); + prop.SetStringProp(KvDBProperties::STORE_ID, deviceId); + std::string identifier = DBCommon::TransferHashString("sync_test-sync_test-" + deviceId); + + prop.SetStringProp(KvDBProperties::IDENTIFIER_DATA, identifier); + std::string identifierDir = DBCommon::TransferStringToHex(identifier); + prop.SetStringProp(KvDBProperties::IDENTIFIER_DIR, identifierDir); + prop.SetStringProp(KvDBProperties::DATA_DIR, dir + "/commitstore"); + prop.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE); + prop.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true); + + int errCode = E_OK; + IKvDB *kvDB = KvDBManager::OpenDatabase(prop, errCode); + if (errCode != E_OK) { + LOGE("[VirtualMultiVerSyncDBInterface] db create failed path, err %d", errCode); + return errCode; + } + kvStore_ = static_cast(kvDB); + IKvDBConnection *conn = kvDB->GetDBConnection(errCode); + if (errCode != E_OK) { + LOGE("[VirtualMultiVerSyncDBInterface] connection get failed path, err %d", errCode); + RefObject::DecObjRef(kvStore_); + kvStore_ = nullptr; + return errCode; + } + RefObject::DecObjRef(kvStore_); + connection_ = static_cast(conn); + return E_OK; +} + +int VirtualMultiVerSyncDBInterface::GetData(const Key &key, Key &value) +{ + IKvDBSnapshot *snapshot = nullptr; + int errCode = connection_->GetSnapshot(snapshot); + if (errCode != E_OK) { + LOGE("[VirtualMultiVerSyncDBInterface] GetSnapshot failed err %d", errCode); + return errCode; + } + errCode = snapshot->Get(key, value); + connection_->ReleaseSnapshot(snapshot); + return errCode; +} + +int VirtualMultiVerSyncDBInterface::PutData(const Key &key, const Key &value) +{ + IOption option; + return connection_->Put(option, key, value); +} + +int VirtualMultiVerSyncDBInterface::DeleteData(const Key &key) +{ + IOption option; + return connection_->Delete(option, key); +} + +int VirtualMultiVerSyncDBInterface::StartTransaction() +{ + return connection_->StartTransaction(); +} + +int VirtualMultiVerSyncDBInterface::Commit() +{ + return connection_->Commit(); +} + +int VirtualMultiVerSyncDBInterface::DeleteDatabase() +{ + if (connection_ != nullptr) { + KvDBProperties prop = kvStore_->GetMyProperties(); + int errCode = connection_->Close(); + if (errCode != E_OK) { + return errCode; + } + connection_ = nullptr; + kvStore_ = nullptr; + return KvDBManager::RemoveDatabase(prop); + } + return -E_NOT_FOUND; +} + +const KvDBProperties &VirtualMultiVerSyncDBInterface::GetDbProperties() const +{ + return properties_; +} + +int VirtualMultiVerSyncDBInterface::DeleteMetaDataByPrefixKey(const Key &keyPrefix) const +{ + return -E_NOT_SUPPORT; +} +} // namespace DistributedDB + diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.h b/mock/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..e9d985be9ff3d038ad4229c1524b3860432c1a9a --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIRTUAL_MULTI_VER_SYNC_INTERFACE_H +#define VIRTUAL_MULTI_VER_SYNC_INTERFACE_H + +#include "distributeddb_tools_unit_test.h" +#include "multi_ver_natural_store.h" +#include "multi_ver_natural_store_connection.h" + +namespace DistributedDB { +class VirtualMultiVerSyncDBInterface final : public MultiVerKvDBSyncInterface { +public: + VirtualMultiVerSyncDBInterface(); + ~VirtualMultiVerSyncDBInterface() override; + + int GetInterfaceType() const override; + + void IncRefCount() override; + + void DecRefCount() override; + + std::vector GetIdentifier() const override; + + void GetMaxTimestamp(Timestamp &stamp) const override; + + int GetMetaData(const Key &key, Value &value) const override; + + int PutMetaData(const Key &key, const Value &value) override; + + // Delete multiple meta data records in a transaction. + int DeleteMetaData(const std::vector &keys) override; + + // Delete multiple meta data records with key prefix in a transaction. + int DeleteMetaDataByPrefixKey(const Key &keyPrefix) const override; + + int GetAllMetaKeys(std::vector &keys) const override; + + bool IsCommitExisted(const MultiVerCommitNode &) const override; + + int GetDeviceLatestCommit(std::map &) const override; + + int GetCommitTree(const std::map &, + std::vector &) const override; + + int GetCommitData(const MultiVerCommitNode &commit, std::vector &entries) const override; + + MultiVerKvEntry *CreateKvEntry(const std::vector &) override; + + void ReleaseKvEntry(const MultiVerKvEntry *entry) override; + + bool IsValueSliceExisted(const ValueSliceHash &value) const override; + + int GetValueSlice(const ValueSliceHash &hashValue, ValueSlice &sliceValue) const override; + + int PutValueSlice(const ValueSliceHash &hashValue, const ValueSlice &sliceValue) const override; + + int PutCommitData(const MultiVerCommitNode &commit, const std::vector &entries, + const std::string &deviceName) override; + + int MergeSyncCommit(const MultiVerCommitNode &commit, const std::vector &commits) override; + + void NotifyStartSyncOperation() override {}; + + void NotifyFinishSyncOperation() override {}; + + int TransferSyncCommitDevInfo(MultiVerCommitNode &commit, const std::string &devId, bool isSyncedIn) const override; + + int Initialize(const std::string &deviceId); + + int GetData(const Key &key, Key &value); + + int PutData(const Key &key, const Key &value); + + int DeleteData(const Key &key); + + int StartTransaction(); + + int Commit(); + + int DeleteDatabase(); + + const KvDBProperties &GetDbProperties() const override; + +private: + DistributedDBUnitTest::DistributedDBToolsUnitTest testTool_; + MultiVerNaturalStore *kvStore_; + MultiVerNaturalStoreConnection *connection_; + KvDBProperties properties_; +}; +} // namespace DistributedDB + +#endif // VIRTUAL_MULTI_VER_SYNC_INTERFACE \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_relational_ver_sync_db_interface.cpp b/mock/distributeddb/test/unittest/common/syncer/virtual_relational_ver_sync_db_interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c91bf9dc12b67ce45200d999fe1af6ea22b8b9cf --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_relational_ver_sync_db_interface.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "db_common.h" +#include "virtual_relational_ver_sync_db_interface.h" +#include "generic_single_ver_kv_entry.h" +#include "virtual_single_ver_sync_db_Interface.h" + +namespace DistributedDB { +namespace { + int GetEntriesFromItems(std::vector &entries, const std::vector &dataItems) + { + int errCode = E_OK; + for (auto &item : dataItems) { + auto entry = new (std::nothrow) GenericSingleVerKvEntry(); + if (entry == nullptr) { + LOGE("Create entry failed."); + errCode = -E_OUT_OF_MEMORY; + break; + } + DataItem storageItem; + storageItem.key = item.key; + storageItem.value = item.value; + storageItem.flag = item.flag; + storageItem.timestamp = item.timestamp; + storageItem.writeTimestamp = item.writeTimestamp; + storageItem.hashKey = item.hashKey; + entry->SetEntryData(std::move(storageItem)); + entries.push_back(entry); + } + if (errCode != E_OK) { + LOGD("[GetEntriesFromItems] failed:%d", errCode); + for (auto &kvEntry : entries) { + delete kvEntry; + kvEntry = nullptr; + } + entries.clear(); + } + LOGD("[GetEntriesFromItems] size:%zu", dataItems.size()); + return errCode; + } + + std::string GetStr(const std::vector &vec) + { + std::string str; + DBCommon::VectorToString(vec, str); + return str; + } +} + +int VirtualRelationalVerSyncDBInterface::PutSyncDataWithQuery(const QueryObject &object, + const std::vector &entries, const std::string &deviceName) +{ + LOGD("[PutSyncData] size %zu", entries.size()); + std::vector dataItems; + for (auto itemEntry : entries) { + auto *entry = static_cast(itemEntry); + if (entry != nullptr) { + DataItem item; + item.origDev = entry->GetOrigDevice(); + item.flag = entry->GetFlag(); + item.timestamp = entry->GetTimestamp(); + item.writeTimestamp = entry->GetWriteTimestamp(); + entry->GetKey(item.key); + entry->GetValue(item.value); + entry->GetHashKey(item.hashKey); + dataItems.push_back(item); + } + } + OptTableDataWithLog optTableDataWithLog; + optTableDataWithLog.tableName = object.GetTableName(); + int errCode = DataTransformer::TransformDataItem(dataItems, localFieldInfo_, + localFieldInfo_, optTableDataWithLog); + if (errCode != E_OK) { + return errCode; + } + for (const auto &optRowDataWithLog : optTableDataWithLog.dataList) { + VirtualRowData virtualRowData; + virtualRowData.logInfo = optRowDataWithLog.logInfo; + size_t index = 0; + for (const auto &optItem : optRowDataWithLog.optionalData) { + if (index >= localFieldInfo_.size()) { + break; + } + DataValue dataValue = std::move(optItem); + LOGD("type:%d", static_cast(optItem.GetType())); + virtualRowData.objectData.PutDataValue(localFieldInfo_[index].GetFieldName(), dataValue); + index++; + } + syncData_[object.GetTableName()][GetStr(virtualRowData.logInfo.hashKey)] = virtualRowData; + } + LOGD("tableName %s", optTableDataWithLog.tableName.c_str()); + return errCode; +} + +int VirtualRelationalVerSyncDBInterface::PutLocalData(const std::vector &dataList, + const std::string &tableName) +{ + for (const auto &item : dataList) { + localData_[tableName][GetStr(item.logInfo.hashKey)] = item; + } + return E_OK; +} + +int VirtualRelationalVerSyncDBInterface::GetSyncData(QueryObject &query, + const SyncTimeRange &timeRange, const DataSizeSpecInfo &dataSizeInfo, + ContinueToken &continueStmtToken, std::vector &entries) const +{ + if (localData_.find(query.GetTableName()) == localData_.end()) { + LOGD("[GetSyncData] No Data Return"); + return E_OK; + } + std::vector dataItemList; + TableDataWithLog tableDataWithLog = {query.GetTableName(), {}}; + for (const auto &[hashKey, virtualData] : localData_[query.GetTableName()]) { + if (virtualData.logInfo.timestamp < timeRange.beginTime || + virtualData.logInfo.timestamp >= timeRange.endTime) { + LOGD("ignore hashkey %s", hashKey.c_str()); + continue; + } + RowDataWithLog rowData; + for (const auto &field : localFieldInfo_) { + DataValue dataValue; + (void)virtualData.objectData.GetDataValue(field.GetFieldName(), dataValue); + rowData.rowData.push_back(std::move(dataValue)); + } + rowData.logInfo = virtualData.logInfo; + tableDataWithLog.dataList.push_back(rowData); + } + + int errCode = DataTransformer::TransformTableData(tableDataWithLog, localFieldInfo_, dataItemList); + if (errCode != E_OK) { + return errCode; + } + continueStmtToken = nullptr; + return GetEntriesFromItems(entries, dataItemList); +} + +RelationalSchemaObject VirtualRelationalVerSyncDBInterface::GetSchemaInfo() const +{ + return schemaObj_; +} + +int VirtualRelationalVerSyncDBInterface::GetDatabaseCreateTimestamp(Timestamp &outTime) const +{ + return E_OK; +} + +int VirtualRelationalVerSyncDBInterface::GetBatchMetaData(const std::vector &keys, + std::vector &entries) const +{ + int errCode = E_OK; + for (const auto &key : keys) { + Entry entry; + entry.key = key; + errCode = GetMetaData(key, entry.value); + if (errCode != E_OK) { + return errCode; + } + entries.push_back(entry); + } + return errCode; +} + +int VirtualRelationalVerSyncDBInterface::PutBatchMetaData(std::vector &entries) +{ + int errCode = E_OK; + for (const auto &entry : entries) { + errCode = PutMetaData(entry.key, entry.value); + if (errCode != E_OK) { + return errCode; + } + } + return errCode; +} + +std::vector VirtualRelationalVerSyncDBInterface::GetTablesQuery() +{ + return {}; +} + +int VirtualRelationalVerSyncDBInterface::LocalDataChanged(int notifyEvent, std::vector &queryObj) +{ + return E_OK; +} + +int VirtualRelationalVerSyncDBInterface::GetInterfaceType() const +{ + return SYNC_RELATION; +} + +void VirtualRelationalVerSyncDBInterface::IncRefCount() +{ +} + +void VirtualRelationalVerSyncDBInterface::DecRefCount() +{ +} + +std::vector VirtualRelationalVerSyncDBInterface::GetIdentifier() const +{ + return {}; +} + +void VirtualRelationalVerSyncDBInterface::GetMaxTimestamp(Timestamp &stamp) const +{ + for (const auto &item : syncData_) { + for (const auto &entry : item.second) { + if (stamp < entry.second.logInfo.timestamp) { + stamp = entry.second.logInfo.timestamp; + } + } + } + LOGD("VirtualSingleVerSyncDBInterface::GetMaxTimestamp time = %" PRIu64, stamp); +} + +int VirtualRelationalVerSyncDBInterface::GetMetaData(const Key &key, Value &value) const +{ + auto iter = metadata_.find(key); + if (iter != metadata_.end()) { + value = iter->second; + return E_OK; + } + return -E_NOT_FOUND; +} + +int VirtualRelationalVerSyncDBInterface::PutMetaData(const Key &key, const Value &value) +{ + metadata_[key] = value; + return E_OK; +} + +int VirtualRelationalVerSyncDBInterface::DeleteMetaData(const std::vector &keys) +{ + for (const auto &key : keys) { + (void)metadata_.erase(key); + } + return E_OK; +} + +int VirtualRelationalVerSyncDBInterface::DeleteMetaDataByPrefixKey(const Key &keyPrefix) const +{ + size_t prefixKeySize = keyPrefix.size(); + for (auto iter = metadata_.begin();iter != metadata_.end();) { + if (prefixKeySize <= iter->first.size() && + keyPrefix == Key(iter->first.begin(), std::next(iter->first.begin(), prefixKeySize))) { + iter = metadata_.erase(iter); + } else { + ++iter; + } + } + return E_OK; +} + +int VirtualRelationalVerSyncDBInterface::GetAllMetaKeys(std::vector &keys) const +{ + for (auto &iter : metadata_) { + keys.push_back(iter.first); + } + LOGD("GetAllMetaKeys size %zu", keys.size()); + return E_OK; +} + +const KvDBProperties &VirtualRelationalVerSyncDBInterface::GetDbProperties() const +{ + return properties_; +} + +void VirtualRelationalVerSyncDBInterface::SetLocalFieldInfo(const std::vector &localFieldInfo) +{ + localFieldInfo_.clear(); + localFieldInfo_ = localFieldInfo; +} + +int VirtualRelationalVerSyncDBInterface::GetAllSyncData(const std::string &tableName, + std::vector &data) +{ + if (syncData_.find(tableName) == syncData_.end()) { + return -E_NOT_FOUND; + } + for (const auto &entry : syncData_[tableName]) { + data.push_back(entry.second); + } + return E_OK; +} + +int VirtualRelationalVerSyncDBInterface::GetVirtualSyncData(const std::string &tableName, + const std::string &hashKey, VirtualRowData &data) +{ + if (syncData_.find(tableName) == syncData_.end()) { + return -E_NOT_FOUND; + } + if (syncData_.find(hashKey) == syncData_.end()) { + return -E_NOT_FOUND; + } + data = syncData_[tableName][hashKey]; + return E_OK; +} + +void VirtualRelationalVerSyncDBInterface::EraseSyncData(const std::string &tableName) +{ + if (syncData_.find(tableName) == syncData_.end()) { + return; + } + syncData_.erase(tableName); +} + +int VirtualRelationalVerSyncDBInterface::CreateDistributedDeviceTable(const std::string &device, + const RelationalSyncStrategy &syncStrategy) +{ + return E_OK; +} + +int VirtualRelationalVerSyncDBInterface::RegisterSchemaChangedCallback(const std::function &onSchemaChanged) +{ + return E_OK; +} + +void VirtualRelationalVerSyncDBInterface::SetTableInfo(const TableInfo &tableInfo) +{ + schemaObj_.AddRelationalTable(tableInfo); +} + +int VirtualRelationalVerSyncDBInterface::GetMaxTimestamp(const std::string &tableName, Timestamp ×tamp) const +{ + (void)tableName; + timestamp = 0; + return E_OK; +} + +void ObjectData::PutDataValue(const std::string &fieldName, const DataValue &value) +{ + fieldData[fieldName] = value; +} + +int ObjectData::GetDataValue(const std::string &fieldName, DataValue &value) const +{ + if (fieldData.find(fieldName) == fieldData.end()) { + return -E_NOT_FOUND; + } + value = fieldData[fieldName]; + return E_OK; +} +} +#endif \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_relational_ver_sync_db_interface.h b/mock/distributeddb/test/unittest/common/syncer/virtual_relational_ver_sync_db_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..c2f30c829c2d02287d33fc5dad8ec5b6e8de6a75 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_relational_ver_sync_db_interface.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef VIRTUAL_RELATIONAL_VER_SYNC_DB_INTERFACE_H +#define VIRTUAL_RELATIONAL_VER_SYNC_DB_INTERFACE_H +#ifdef RELATIONAL_STORE + +#include "data_transformer.h" +#include "relational_db_sync_interface.h" +#include "sqlite_single_ver_continue_token.h" +#include "relational_schema_object.h" + +namespace DistributedDB { +struct ObjectData { +public: + void PutDataValue(const std::string &fieldName, const DataValue &value); + int GetDataValue(const std::string &fieldName, DataValue &value) const; +private: + mutable std::map fieldData; +}; + +struct VirtualRowData { + LogInfo logInfo; + ObjectData objectData; +}; + +class VirtualRelationalVerSyncDBInterface : public RelationalDBSyncInterface { +public: + VirtualRelationalVerSyncDBInterface() = default; + ~VirtualRelationalVerSyncDBInterface() override = default; + + int PutSyncDataWithQuery(const QueryObject &query, const std::vector &entries, + const std::string &deviceName) override; + + int PutLocalData(const std::vector &dataList, const std::string &tableName); + + RelationalSchemaObject GetSchemaInfo() const override; + + int GetDatabaseCreateTimestamp(Timestamp &outTime) const override; + + int GetBatchMetaData(const std::vector &keys, std::vector &entries) const override; + + int PutBatchMetaData(std::vector &entries) override; + + std::vector GetTablesQuery() override; + + int LocalDataChanged(int notifyEvent, std::vector &queryObj) override; + + int GetSyncData(QueryObject &query, const SyncTimeRange &timeRange, + const DataSizeSpecInfo &dataSizeInfo, ContinueToken &continueStmtToken, + std::vector &entries) const override; + + int GetInterfaceType() const override; + + void IncRefCount() override; + + void DecRefCount() override; + + std::vector GetIdentifier() const override; + + void GetMaxTimestamp(Timestamp &stamp) const override; + + // Get the max timestamp of one table. + int GetMaxTimestamp(const std::string &tableName, Timestamp ×tamp) const override; + + int GetMetaData(const Key &key, Value &value) const override; + + int PutMetaData(const Key &key, const Value &value) override; + + int DeleteMetaData(const std::vector &keys) override; + + int DeleteMetaDataByPrefixKey(const Key &keyPrefix) const override; + + int GetAllMetaKeys(std::vector &keys) const override; + + const KvDBProperties &GetDbProperties() const override; + + void SetLocalFieldInfo(const std::vector &localFieldInfo); + + int GetAllSyncData(const std::string &tableName, std::vector &data); + + int GetVirtualSyncData(const std::string &tableName, const std::string &hashKey, VirtualRowData &data); + + int InterceptData(std::vector &entries, + const std::string &sourceID, const std::string &targetID) const override + { + return E_OK; + } + + int CheckAndInitQueryCondition(QueryObject &query) const override + { + return E_OK; + } + + int CreateDistributedDeviceTable(const std::string &device, const RelationalSyncStrategy &syncStrategy) override; + + int RegisterSchemaChangedCallback(const std::function &onSchemaChanged) override; + + void EraseSyncData(const std::string &tableName); + + void SetTableInfo(const TableInfo &tableInfo); + +private: + mutable std::map, std::vector> metadata_; + std::map> syncData_; + mutable std::map> localData_; + std::string schema_; + RelationalSchemaObject schemaObj_; + std::vector localFieldInfo_; + KvDBProperties properties_; + SecurityOption secOption_; +}; +} +#endif +#endif // VIRTUAL_RELATIONAL_VER_SYNC_DB_INTERFACE_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.cpp b/mock/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f4641e5d594b39c1bf6d9026b8680a41d0205231 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.cpp @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "virtual_single_ver_sync_db_Interface.h" + +#include +#include + +#include "db_common.h" +#include "db_errno.h" +#include "generic_single_ver_kv_entry.h" +#include "intercepted_data_impl.h" +#include "log_print.h" +#include "query_object.h" +#include "securec.h" + +namespace DistributedDB { +namespace { + int GetEntriesFromItems(std::vector &entries, const std::vector &dataItems) + { + int errCode = E_OK; + for (auto &item : dataItems) { + auto entry = new (std::nothrow) GenericSingleVerKvEntry(); + if (entry == nullptr) { + LOGE("Create entry failed."); + errCode = -E_OUT_OF_MEMORY; + break; + } + DataItem storageItem; + storageItem.key = item.key; + storageItem.value = item.value; + storageItem.flag = item.flag; + storageItem.timestamp = item.timestamp; + storageItem.writeTimestamp = item.writeTimestamp; + entry->SetEntryData(std::move(storageItem)); + entries.push_back(entry); + } + if (errCode != E_OK) { + for (auto &kvEntry : entries) { + delete kvEntry; + kvEntry = nullptr; + } + entries.clear(); + } + return errCode; + } +} +int VirtualSingleVerSyncDBInterface::GetInterfaceType() const +{ + return SYNC_SVD; +} + +void VirtualSingleVerSyncDBInterface::IncRefCount() +{ +} + +void VirtualSingleVerSyncDBInterface::DecRefCount() +{ +} + +std::vector VirtualSingleVerSyncDBInterface::GetIdentifier() const +{ + std::vector identifier; + return identifier; +} + +int VirtualSingleVerSyncDBInterface::GetMetaData(const Key &key, Value &value) const +{ + auto iter = metadata_.find(key); + if (iter != metadata_.end()) { + value = iter->second; + return E_OK; + } + return -E_NOT_FOUND; +} + +int VirtualSingleVerSyncDBInterface::PutMetaData(const Key &key, const Value &value) +{ + if (busy_) { + return -E_BUSY; + } + metadata_[key] = value; + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::DeleteMetaData(const std::vector &keys) +{ + for (const auto &key : keys) { + (void)metadata_.erase(key); + } + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::GetAllMetaKeys(std::vector &keys) const +{ + for (auto iter = metadata_.begin(); iter != metadata_.end(); ++iter) { + keys.push_back(iter->first); + } + LOGD("GetAllMetaKeys size %zu", keys.size()); + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::GetSyncData(Timestamp begin, Timestamp end, std::vector &dataItems, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const +{ + return -E_NOT_SUPPORT; +} + +int VirtualSingleVerSyncDBInterface::GetSyncDataNext(std::vector &dataItems, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const +{ + return -E_NOT_SUPPORT; +} + +void VirtualSingleVerSyncDBInterface::ReleaseContinueToken(ContinueToken& continueStmtToken) const +{ + return; +} + +SchemaObject VirtualSingleVerSyncDBInterface::GetSchemaInfo() const +{ + return schemaObj_; +} + +bool VirtualSingleVerSyncDBInterface::CheckCompatible(const std::string& schema, uint8_t type) const +{ + if (schema_.empty() && schema.empty() && ReadSchemaType(type) != SchemaType::UNRECOGNIZED) { + return true; + } + return (schemaObj_.CompareAgainstSchemaString(schema) == -E_SCHEMA_EQUAL_EXACTLY); +} + +int VirtualSingleVerSyncDBInterface::PutData(const Key &key, const Value &value, const Timestamp &time, int flag) +{ + VirtualDataItem item; + item.key = key; + item.value = value; + item.timestamp = time; + item.writeTimestamp = time; + item.flag = flag; + item.isLocal = true; + dbData_.push_back(item); + return E_OK; +} + +void VirtualSingleVerSyncDBInterface::GetMaxTimestamp(Timestamp& stamp) const +{ + for (auto iter = dbData_.begin(); iter != dbData_.end(); ++iter) { + if (stamp < iter->writeTimestamp) { + stamp = iter->writeTimestamp; + } + } + LOGD("VirtualSingleVerSyncDBInterface::GetMaxTimestamp time = %" PRIu64, stamp); +} + +int VirtualSingleVerSyncDBInterface::RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) +{ + std::lock_guard autoLock(deviceDataLock_); + deviceData_.erase(deviceName); + LOGD("RemoveDeviceData FINISH"); + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::GetSyncData(const Key &key, VirtualDataItem &dataItem) +{ + auto iter = std::find_if(dbData_.begin(), dbData_.end(), + [key](const VirtualDataItem& item) { return item.key == key; }); + if (iter != dbData_.end()) { + dataItem.key = iter->key; + dataItem.value = iter->value; + dataItem.timestamp = iter->timestamp; + dataItem.writeTimestamp = iter->writeTimestamp; + dataItem.flag = iter->flag; + dataItem.isLocal = iter->isLocal; + return E_OK; + } + return -E_NOT_FOUND; +} + +int VirtualSingleVerSyncDBInterface::GetSyncData(Timestamp begin, Timestamp end, + std::vector &entries, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const +{ + std::vector dataItems; + int errCode = GetSyncData(begin, end, dataSizeInfo.blockSize, dataItems, continueStmtToken); + if (errCode != E_OK) { + LOGE("[VirtualSingleVerSyncDBInterface][GetSyncData] GetSyncData failed err %d", errCode); + return errCode; + } + return GetEntriesFromItems(entries, dataItems); +} + +int VirtualSingleVerSyncDBInterface::GetSyncDataNext(std::vector &entries, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const +{ + if (continueStmtToken == nullptr) { + return -E_NOT_SUPPORT; + } + return 0; +} + +int VirtualSingleVerSyncDBInterface::GetSyncData(Timestamp begin, Timestamp end, uint32_t blockSize, + std::vector &dataItems, ContinueToken &continueStmtToken) const +{ + for (const auto &data : dbData_) { + if (data.isLocal) { + if (data.writeTimestamp >= begin && data.writeTimestamp < end) { + dataItems.push_back(data); + } + } + } + continueStmtToken = nullptr; + LOGD("dataItems size %zu", dataItems.size()); + return E_OK; +} + +void VirtualSingleVerSyncDBInterface::SetSaveDataDelayTime(uint64_t milliDelayTime) +{ + saveDataDelayTime_ = milliDelayTime; +} + +int VirtualSingleVerSyncDBInterface::GetSyncDataNext(std::vector& dataItems, + uint32_t blockSize, ContinueToken& continueStmtToken) const +{ + if (continueStmtToken == nullptr) { + return -E_NOT_SUPPORT; + } + return 0; +} + +int VirtualSingleVerSyncDBInterface::PutSyncData(std::vector& dataItems, + const std::string &deviceName) +{ + for (auto iter = dataItems.begin(); iter != dataItems.end(); ++iter) { + LOGD("PutSyncData"); + auto dbDataIter = std::find_if(dbData_.begin(), dbData_.end(), + [iter](VirtualDataItem item) { return item.key == iter->key; }); + if ((dbDataIter != dbData_.end()) && (dbDataIter->writeTimestamp < iter->writeTimestamp)) { + // if has conflict, compare writeTimestamp + LOGI("conflict data time local %" PRIu64 ", remote %" PRIu64, dbDataIter->writeTimestamp, + iter->writeTimestamp); + dbDataIter->key = iter->key; + dbDataIter->value = iter->value; + dbDataIter->timestamp = iter->timestamp; + dbDataIter->writeTimestamp = iter->writeTimestamp; + dbDataIter->flag = iter->flag; + dbDataIter->isLocal = false; + } else { + LOGI("PutSyncData, use remote data %" PRIu64, iter->timestamp); + VirtualDataItem dataItem; + dataItem.key = iter->key; + dataItem.value = iter->value; + dataItem.timestamp = iter->timestamp; + dataItem.writeTimestamp = iter->writeTimestamp; + dataItem.flag = iter->flag; + dataItem.isLocal = false; + dbData_.push_back(dataItem); + } + } + return E_OK; +} + +void VirtualSingleVerSyncDBInterface::SetSchemaInfo(const std::string& schema) +{ + schema_ = schema; + SchemaObject emptyObj; + schemaObj_ = emptyObj; + schemaObj_.ParseFromSchemaString(schema); +} + +const KvDBProperties &VirtualSingleVerSyncDBInterface::GetDbProperties() const +{ + return properties_; +} + +int VirtualSingleVerSyncDBInterface::GetSecurityOption(SecurityOption &option) const +{ + option = secOption_; + return E_OK; +} + +bool VirtualSingleVerSyncDBInterface::IsReadable() const +{ + return true; +} + +void VirtualSingleVerSyncDBInterface::SetSecurityOption(SecurityOption &option) +{ + secOption_ = option; +} + +void VirtualSingleVerSyncDBInterface::NotifyRemotePushFinished(const std::string &targetId) const +{ +} + +int VirtualSingleVerSyncDBInterface::GetDatabaseCreateTimestamp(Timestamp &outTime) const +{ + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::GetSyncData(QueryObject &query, const SyncTimeRange &timeRange, + const DataSizeSpecInfo &dataSizeInfo, ContinueToken &continueStmtToken, + std::vector &entries) const +{ + const auto &startKey = query.GetPrefixKey(); + Key endKey = startKey; + endKey.resize(DBConstant::MAX_KEY_SIZE, UCHAR_MAX); + + std::vector dataItems; + for (const auto &data : dbData_) { + // Only get local data. + if (!data.isLocal) { + continue; + } + + if ((data.flag & VirtualDataItem::DELETE_FLAG) != 0) { + if (data.timestamp >= timeRange.deleteBeginTime && data.timestamp < timeRange.deleteEndTime) { + dataItems.push_back(data); + } + } else { + if (data.timestamp >= timeRange.beginTime && data.timestamp < timeRange.endTime && + data.key >= startKey && data.key <= endKey) { + dataItems.push_back(data); + } + } + } + + LOGD("dataItems size %zu", dataItems.size()); + return GetEntriesFromItems(entries, dataItems); +} + +int VirtualSingleVerSyncDBInterface::DeleteMetaDataByPrefixKey(const Key &keyPrefix) const +{ + size_t prefixKeySize = keyPrefix.size(); + for (auto iter = metadata_.begin(); iter != metadata_.end();) { + if (prefixKeySize <= iter->first.size() && + keyPrefix == Key(iter->first.begin(), std::next(iter->first.begin(), prefixKeySize))) { + iter = metadata_.erase(iter); + } else { + ++iter; + } + } + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::GetCompressionOption(bool &needCompressOnSync, uint8_t &compressionRate) const +{ + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::GetCompressionAlgo(std::set &algorithmSet) const +{ + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::PutSyncData(const DataItem &item) +{ + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::CheckAndInitQueryCondition(QueryObject &query) const +{ + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::InterceptData(std::vector &entries, + const std::string &sourceID, const std::string &targetID) const +{ + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::PutSyncDataWithQuery(const QueryObject &query, + const std::vector &entries, const std::string &deviceName) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(saveDataDelayTime_)); + std::vector dataItems; + for (auto kvEntry : entries) { + auto genericKvEntry = static_cast(kvEntry); + VirtualDataItem item; + genericKvEntry->GetKey(item.key); + genericKvEntry->GetValue(item.value); + item.timestamp = genericKvEntry->GetTimestamp(); + item.writeTimestamp = genericKvEntry->GetWriteTimestamp(); + item.flag = genericKvEntry->GetFlag(); + item.isLocal = false; + dataItems.push_back(item); + } + return PutSyncData(dataItems, deviceName); +} + +int VirtualSingleVerSyncDBInterface::AddSubscribe(const std::string &subscribeId, const QueryObject &query, + bool needCacheSubscribe) +{ + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::RemoveSubscribe(const std::string &subscribeId) +{ + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::RemoveSubscribe(const std::vector &subscribeIds) +{ + return E_OK; +} + +void VirtualSingleVerSyncDBInterface::SetBusy(bool busy) +{ + busy_ = busy; +} + +void VirtualSingleVerSyncDBInterface::PutDeviceData(const std::string &deviceName, const Key &key, const Value &value) +{ + std::lock_guard autoLock(deviceDataLock_); + deviceData_[deviceName][key] = value; +} + +void VirtualSingleVerSyncDBInterface::GetDeviceData(const std::string &deviceName, const Key &key, Value &value) +{ + std::lock_guard autoLock(deviceDataLock_); + value = deviceData_[deviceName][key]; +} +} // namespace DistributedDB diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.h b/mock/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.h new file mode 100644 index 0000000000000000000000000000000000000000..43a7a9c928ca66660ce4422e509043f5f7dafa18 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KVDB_SYNCABLE_TEST_H +#define KVDB_SYNCABLE_TEST_H + +#include +#include +#include + +#include "single_ver_kvdb_sync_interface.h" +#include "query_object.h" +#include "store_types.h" + +namespace DistributedDB { +struct VirtualDataItem { + Key key; + Value value; + Timestamp timestamp = 0; + Timestamp writeTimestamp = 0; + uint64_t flag = 0; + bool isLocal = true; + static const uint64_t DELETE_FLAG = 0x01; + static const uint64_t LOCAL_FLAG = 0x02; +}; +class VirtualSingleVerSyncDBInterface : public SingleVerKvDBSyncInterface { +public: + int GetInterfaceType() const override; + + void IncRefCount() override; + + void DecRefCount() override; + + std::vector GetIdentifier() const override; + + int GetMetaData(const Key& key, Value& value) const override; + + int PutMetaData(const Key& key, const Value& value) override; + + int DeleteMetaData(const std::vector &keys) override; + // Delete multiple meta data records with key prefix in a transaction. + int DeleteMetaDataByPrefixKey(const Key &keyPrefix) const override; + + int GetAllMetaKeys(std::vector& keys) const override; + + int GetSyncData(Timestamp begin, Timestamp end, std::vector &dataItems, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const override; + + int GetSyncDataNext(std::vector &dataItems, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const override; + + void ReleaseContinueToken(ContinueToken& continueStmtToken) const override; + + void GetMaxTimestamp(Timestamp& stamp) const override; + + int RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) override; + + int GetSyncData(const Key& key, VirtualDataItem& dataItem); + + int PutSyncData(const DataItem& item); + + int PutData(const Key &key, const Value &value, const Timestamp &time, int flag); + + int GetSyncData(Timestamp begin, Timestamp end, std::vector &entries, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const override; + + int GetSyncData(QueryObject &query, const SyncTimeRange &timeRange, const DataSizeSpecInfo &dataSizeInfo, + ContinueToken &continueStmtToken, std::vector &entries) const override; + + int GetSyncDataNext(std::vector &entries, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const override; + + int PutSyncDataWithQuery(const QueryObject &query, const std::vector &entries, + const std::string &deviceName) override; + + SchemaObject GetSchemaInfo() const override; + + bool CheckCompatible(const std::string& schema, uint8_t type) const override; + + void SetSchemaInfo(const std::string& schema); + + const KvDBProperties &GetDbProperties() const override; + + void SetSaveDataDelayTime(uint64_t milliDelayTime); + + int GetSecurityOption(SecurityOption &option) const override; + + bool IsReadable() const override; + + void SetSecurityOption(SecurityOption &option); + + void NotifyRemotePushFinished(const std::string &targetId) const override; + + int GetDatabaseCreateTimestamp(Timestamp &outTime) const override; + + int GetCompressionOption(bool &needCompressOnSync, uint8_t &compressionRate) const override; + int GetCompressionAlgo(std::set &algorithmSet) const override; + + // return E_OK if subscribe is legal, ERROR on exception. + int CheckAndInitQueryCondition(QueryObject &query) const override; + + int InterceptData(std::vector &entries, const std::string &sourceID, + const std::string &targetID) const override; + + int AddSubscribe(const std::string &subscribeId, const QueryObject &query, bool needCacheSubscribe) override; + + int RemoveSubscribe(const std::string &subscribeId) override; + + int RemoveSubscribe(const std::vector &subscribeIds) override; + + void SetBusy(bool busy); + + void PutDeviceData(const std::string &deviceName, const Key &key, const Value &value); + + void GetDeviceData(const std::string &deviceName, const Key &key, Value &value); +private: + int GetSyncData(Timestamp begin, Timestamp end, uint32_t blockSize, std::vector& dataItems, + ContinueToken& continueStmtToken) const; + + int GetSyncDataNext(std::vector& dataItems, + uint32_t blockSize, ContinueToken& continueStmtToken) const; + + int PutSyncData(std::vector& dataItems, const std::string &deviceName); + + mutable std::map, std::vector> metadata_; + std::vector dbData_; + std::string schema_; + SchemaObject schemaObj_; + KvDBProperties properties_; + uint64_t saveDataDelayTime_ = 0; + SecurityOption secOption_; + bool busy_ = false; + + std::mutex deviceDataLock_; + std::map> deviceData_; +}; +} // namespace DistributedDB + +#endif // KVDB_SYNCABLE_TEST_H \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.cpp b/mock/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c42b4261a144e82f75e3b01e9cb004b26c071bf --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "virtual_time_sync_communicator.h" + +#include "log_print.h" + +namespace DistributedDB { +VirtualTimeSyncCommunicator::VirtualTimeSyncCommunicator() + : srcTimeSync_(nullptr), + dstTimeSync_(nullptr), + timeOffset_(0), + deviceID_(""), + syncTaskcontext_(nullptr), + isEnable_(true) +{ +} + +VirtualTimeSyncCommunicator::~VirtualTimeSyncCommunicator() {} + +int VirtualTimeSyncCommunicator::RegOnMessageCallback(const OnMessageCallback &onMessage, const Finalizer &inOper) +{ + return 0; +} + +int VirtualTimeSyncCommunicator::RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) +{ + return 0; +} + +int VirtualTimeSyncCommunicator::RegOnSendableCallback(const std::function &onSendable, + const Finalizer &inOper) +{ + return 0; +} + +void VirtualTimeSyncCommunicator::Activate() +{ +} + +// return maximum allowed data size +uint32_t VirtualTimeSyncCommunicator::GetCommunicatorMtuSize() const +{ + return 0; +} + +uint32_t VirtualTimeSyncCommunicator::GetCommunicatorMtuSize(const std::string &target) const +{ + return GetCommunicatorMtuSize(); +} + +uint32_t VirtualTimeSyncCommunicator::GetTimeout() const +{ + return 0; +} + +uint32_t VirtualTimeSyncCommunicator::GetTimeout(const std::string &target) const +{ + return 0; +} + +bool VirtualTimeSyncCommunicator::IsDeviceOnline(const std::string &device) const +{ + return true; +} + +// Get local target name for identify self +int VirtualTimeSyncCommunicator::GetLocalIdentity(std::string &outTarget) const +{ + return 0; +} + +int VirtualTimeSyncCommunicator::SendMessage(const std::string &dstTarget, const Message *inMsg, + const SendConfig &config) +{ + return SendMessage(dstTarget, inMsg, config, nullptr); +} + +int VirtualTimeSyncCommunicator::SendMessage(const std::string &dstTarget, const Message *inMsg, + const SendConfig &config, const OnSendEnd &onEnd) +{ + if (!isEnable_) { + LOGD("[VirtualTimeSyncCommunicator]the VirtualTimeSyncCommunicator disabled!"); + return -E_PERIPHERAL_INTERFACE_FAIL; + } + LOGD("VirtualTimeSyncCommunicator::sendMessage dev = %s, syncid = %d", dstTarget.c_str(), inMsg->GetSequenceId()); + int errCode; + if (dstTarget == deviceID_) { + if (srcTimeSync_ == nullptr) { + LOGD("srcTimeSync_ = nullprt"); + return -E_INVALID_ARGS; + } + if (syncTaskcontext_ == nullptr) { + LOGD("syncTaskcontext_ = nullprt"); + return -E_INVALID_ARGS; + } + errCode = srcTimeSync_->AckRecv(inMsg); + } else { + if (dstTimeSync_ == nullptr) { + LOGD("dstTimeSync_ is nullprt"); + return -E_INVALID_ARGS; + } + Message *msgTmp = const_cast(inMsg); + errCode = dstTimeSync_->RequestRecv(msgTmp); + } + if (inMsg != nullptr) { + delete inMsg; + inMsg = nullptr; + } + return errCode; +} + +int VirtualTimeSyncCommunicator::GetRemoteCommunicatorVersion(const std::string &deviceId, uint16_t &version) const +{ + version = 0; + return E_OK; +} + +void VirtualTimeSyncCommunicator::SetTimeSync(TimeSync *srcTimeSync, TimeSync *dstTimeSync, + const std::string &deviceID, SyncTaskContext *syncTaskcontext) +{ + srcTimeSync_ = srcTimeSync; + dstTimeSync_ = dstTimeSync; + deviceID_ = deviceID; + syncTaskcontext_ = syncTaskcontext; +} + +void VirtualTimeSyncCommunicator::GetTimeOffset(TimeOffset &timeOffset) const +{ + timeOffset = timeOffset_; +} + +void VirtualTimeSyncCommunicator::Disable() +{ + isEnable_ = false; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/mock/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.h b/mock/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.h new file mode 100644 index 0000000000000000000000000000000000000000..e3165a464c9ce9ae7ee7a8cf1b69df4ba7305ee6 --- /dev/null +++ b/mock/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIRTUAL_TIME_SYNC_COMMUNICATOR_H +#define VIRTUAL_TIME_SYNC_COMMUNICATOR_H + +#include +#include +#include +#include +#include +#include + +#include "db_types.h" +#include "communicator_aggregator.h" +#include "icommunicator.h" +#include "ref_object.h" +#include "serial_buffer.h" +#include "time_sync.h" + +namespace DistributedDB { +class VirtualTimeSyncCommunicator : public ICommunicator { +public: + VirtualTimeSyncCommunicator(); + ~VirtualTimeSyncCommunicator(); + + DISABLE_COPY_ASSIGN_MOVE(VirtualTimeSyncCommunicator); + + int RegOnMessageCallback(const OnMessageCallback &onMessage, const Finalizer &inOper) override; + int RegOnConnectCallback(const OnConnectCallback &onConnect, const Finalizer &inOper) override; + int RegOnSendableCallback(const std::function &onSendable, const Finalizer &inOper) override; + + void Activate() override; + + // return maximum allowed data size + uint32_t GetCommunicatorMtuSize() const override; + uint32_t GetCommunicatorMtuSize(const std::string &target) const override; + + // return timeout + uint32_t GetTimeout() const override; + uint32_t GetTimeout(const std::string &target) const override; + + bool IsDeviceOnline(const std::string &device) const override; + + // Get local target name for identify self + int GetLocalIdentity(std::string &outTarget) const override; + + int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config) override; + int SendMessage(const std::string &dstTarget, const Message *inMsg, const SendConfig &config, + const OnSendEnd &onEnd) override; + + int GetRemoteCommunicatorVersion(const std::string &deviceId, uint16_t &version) const override; + + void SetTimeSync(TimeSync *srcTimeSync, TimeSync *dstTimeSync, + const std::string &deviceID, SyncTaskContext *syncTaskcontext); + + void GetTimeOffset(TimeOffset &timeOffset) const; + + void Disable(); + +private: + TimeSync *srcTimeSync_; + TimeSync *dstTimeSync_; + TimeOffset timeOffset_; + std::string deviceID_; + SyncTaskContext *syncTaskcontext_; + bool isEnable_ = true; +}; +} // namespace DistributedDB + +#endif // VIRTUAL_TIME_SYNC_COMMUNICATOR_H diff --git a/mock/include/CMakeLists.txt b/mock/include/CMakeLists.txt index 8768389e3c93461e2e90cfe8e20fc68bd79d9e0d..8cc2ad18ac86cb33c46b8fa29544830db89bf1db 100644 --- a/mock/include/CMakeLists.txt +++ b/mock/include/CMakeLists.txt @@ -94,6 +94,7 @@ include_directories(${MOCK_DIR}/innerkits/storage_service/storage_manager_sa_pro include_directories(${MOCK_DIR}/innerkits/distributeddatamgr/distributeddata_inner/include) include_directories(${MOCK_DIR}/innerkits/distributeddatamgr/rdb/include) include_directories(${MOCK_DIR}/innerkits/distributeddatamgr/dfx) +include_directories(${MOCK_DIR}/innerkits/distributeddatamgr/objectstore) include_directories(${MOCK_DIR}/innerkits/appverify/libhapverify/include/provision) include_directories(${MOCK_DIR}/innerkits/appverify/libhapverify/include/interfaces) include_directories(${MOCK_DIR}/innerkits/appverify/libhapverify/include/common) diff --git a/mock/innerkits/distributeddatamgr/dfx/reporter.h b/mock/innerkits/distributeddatamgr/dfx/reporter.h index 8adb5bd6a16c0fc961066e0d63ba1f6d418c1776..4c286e9dd97bad9f423c1c94c40daff9ea19e786 100644 --- a/mock/innerkits/distributeddatamgr/dfx/reporter.h +++ b/mock/innerkits/distributeddatamgr/dfx/reporter.h @@ -38,7 +38,7 @@ public: KVSTORE_API StatisticReporter* TrafficStatistic(); KVSTORE_API StatisticReporter* ApiPerformanceStatistic(); - KVSTORE_API BehaviourReporter* BehaviourReporter(); + KVSTORE_API BehaviourReporter* GetBehaviourReporter(); }; } // namespace DistributedDataDfx } // namespace OHOS diff --git a/mock/innerkits/distributeddatamgr/objectstore/iobject_callback.h b/mock/innerkits/distributeddatamgr/objectstore/iobject_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..bd49a8e41a7071bae5e583472d4008ac0470fff5 --- /dev/null +++ b/mock/innerkits/distributeddatamgr/objectstore/iobject_callback.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef I_OBJECT_CALLBACK_H +#define I_OBJECT_CALLBACK_H + +#include +#include "iremote_broker.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" +#include "types.h" + +namespace OHOS { +namespace DistributedObject { +using namespace DistributedKv; +class IObjectSaveCallback : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedObject.IObjectSaveCallback"); + virtual void Completed(const std::map &results) = 0; +}; + +class ObjectSaveCallbackStub : public IRemoteStub { +public: + int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; +}; + +class ObjectSaveCallbackProxy : public IRemoteProxy { +public: + explicit ObjectSaveCallbackProxy(const sptr &impl); + ~ObjectSaveCallbackProxy() = default; + void Completed(const std::map &results) override; + +private: + static inline BrokerDelegator delegator_; +}; + +class IObjectRevokeSaveCallback : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedObject.IObjectRevokeSaveCallback"); + virtual void Completed(int32_t status) = 0; +}; + +class ObjectRevokeSaveCallbackStub : public IRemoteStub { +public: + int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; +}; + +class ObjectRevokeSaveCallbackProxy : public IRemoteProxy { +public: + explicit ObjectRevokeSaveCallbackProxy(const sptr &impl); + ~ObjectRevokeSaveCallbackProxy() = default; + void Completed(int32_t status) override; + +private: + static inline BrokerDelegator delegator_; +}; + +class IObjectRetrieveCallback : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedObject.IObjectRetrieveCallback"); + virtual void Completed(const std::map> &results) = 0; +}; + +class ObjectRetrieveCallbackStub : public IRemoteStub { +public: + int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; +}; + +class ObjectRetrieveCallbackProxy : public IRemoteProxy { +public: + explicit ObjectRetrieveCallbackProxy(const sptr &impl); + ~ObjectRetrieveCallbackProxy() = default; + void Completed(const std::map> &results) override; + +private: + static inline BrokerDelegator delegator_; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // I_KVSTORE_SYNC_CALLBACK_H diff --git a/mock/innerkits/distributeddatamgr/objectstore/iobject_service.h b/mock/innerkits/distributeddatamgr/objectstore/iobject_service.h new file mode 100644 index 0000000000000000000000000000000000000000..b31ecc8e2aa69d2c86cac9da50ed91054d6ccabf --- /dev/null +++ b/mock/innerkits/distributeddatamgr/objectstore/iobject_service.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDATAFWK_IOBJECT_SERVICE_H +#define DISTRIBUTEDDATAFWK_IOBJECT_SERVICE_H + +#include + +#include +#include "object_service.h" + +namespace OHOS::DistributedObject { +class IObjectService : public ObjectService, public IRemoteBroker { +public: + enum { + OBJECTSTORE_SAVE, + OBJECTSTORE_REVOKE_SAVE, + OBJECTSTORE_RETRIEVE, + OBJECTSTORE_SERVICE_CMD_MAX + }; + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedObject.IObjectService"); +}; +} // namespace OHOS::DistributedRdb +#endif diff --git a/mock/innerkits/distributeddatamgr/objectstore/object_service.h b/mock/innerkits/distributeddatamgr/objectstore/object_service.h new file mode 100644 index 0000000000000000000000000000000000000000..6dedb80fbc24637e2ad6e00d0d0c0967cd5dbd17 --- /dev/null +++ b/mock/innerkits/distributeddatamgr/objectstore/object_service.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTED_OBJECT_SERVICE_H +#define DISTRIBUTED_OBJECT_SERVICE_H + +#include +#include +#include + +#include "iobject_callback.h" +namespace OHOS::DistributedObject { +class ObjectService { +public: + virtual int32_t ObjectStoreSave(const std::string &bundleName, const std::string &sessionId, + const std::vector &deviceList, const std::map> &data, + sptr callback) = 0; + virtual int32_t ObjectStoreRetrieve( + const std::string &bundleName, const std::string &sessionId, sptr callback) = 0; + virtual int32_t ObjectStoreRevokeSave( + const std::string &bundleName, const std::string &sessionId, sptr callback) = 0; +}; +} // namespace OHOS::DistributedObject +#endif diff --git a/mock/innerkits/distributeddatamgr/objectstore/object_service_proxy.h b/mock/innerkits/distributeddatamgr/objectstore/object_service_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..788884c60ba6114381cd191ce16c1a325e26690b --- /dev/null +++ b/mock/innerkits/distributeddatamgr/objectstore/object_service_proxy.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTED_OBJECT_SERVICE_PROXY_H +#define DISTRIBUTED_OBJECT_SERVICE_PROXY_H + +#include +#include +#include +#include "iobject_service.h" + +namespace OHOS::DistributedObject { +class ObjectServiceProxy : public IRemoteProxy { +public: + explicit ObjectServiceProxy(const sptr &impl); + ~ObjectServiceProxy() = default; + int32_t ObjectStoreSave(const std::string &bundleName, const std::string &sessionId, + const std::vector &deviceList, const std::map> &data, + sptr callback) override; + int32_t ObjectStoreRetrieve( + const std::string &bundleName, const std::string &sessionId, sptr callback) override; + int32_t ObjectStoreRevokeSave(const std::string &bundleName, const std::string &sessionId, + sptr callback) override; + +private: + static inline BrokerDelegator delegator_; +}; +} // namespace OHOS::DistributedRdb +#endif diff --git a/mock/src/mock_reporter.cpp b/mock/src/mock_reporter.cpp index e33ae53f7a106f446597106e6e745d24fd3ac42f..df4ccf03bae49394d0d151efa35771bc08a63a5d 100644 --- a/mock/src/mock_reporter.cpp +++ b/mock/src/mock_reporter.cpp @@ -110,7 +110,7 @@ StatisticReporter *Reporter::ApiPerformanceStatistic() return &reporter; } -BehaviourReporter *Reporter::BehaviourReporter() +BehaviourReporter *Reporter::GetBehaviourReporter() { class IBehaviourReporter : public BehaviourReporter { public: diff --git a/preferences/CMakeLists.txt b/preferences/CMakeLists.txt index 8d8d6a0c037765ce21f41e961b16f4c8fb453b9c..262041bf413f1bd7ccaa672c82ed42d4dce4a065 100644 --- a/preferences/CMakeLists.txt +++ b/preferences/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS "-std=c++1y -fno-rtti -fvisibility=default -D_GNU_SOURCE") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -fPIC -fpic -ffunction-sections -D_GLIBC_MOCK") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-as-needed -ldl") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=0") set(MOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../mock) add_definitions(-DNAPI_EXPERIMENTAL) @@ -18,6 +19,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../utils_native/base/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../utils_native/safwk/native/include) include(${MOCK_DIR}/include/CMakeLists.txt OPTIONAL) -set(links secure mock relational_store) +set(links secure mock relational_store xml2) add_library(preferences SHARED ${preferences_src}) target_link_libraries(preferences ${links}) \ No newline at end of file diff --git a/relational_store/CMakeLists.txt b/relational_store/CMakeLists.txt index ed590cca6b7cd7d151f19814c70661c8f7e729f5..915232eb30f8aecddfccea315f6751b04dc591fa 100644 --- a/relational_store/CMakeLists.txt +++ b/relational_store/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS "-std=c++1y -fno-rtti -fvisibility=default -D_GNU_SOURCE") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -fPIC -fpic -ffunction-sections -D_GLIBC_MOCK") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-as-needed -ldl") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=0") set(MOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../mock) add_definitions(-DNAPI_EXPERIMENTAL) diff --git a/utils_native/CMakeLists.txt b/utils_native/CMakeLists.txt index 7b9fce5568b57f758feedbd9ad1e662db86c0386..230865d8bb656a56509fe6253847163a802a35b8 100644 --- a/utils_native/CMakeLists.txt +++ b/utils_native/CMakeLists.txt @@ -9,4 +9,6 @@ aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/base/src/securec secureSrc) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/base/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/base/src) -add_library(secure SHARED ${secureSrc}) \ No newline at end of file +set(links rt) +add_library(secure SHARED ${secureSrc}) +target_link_libraries(secure ${links}) \ No newline at end of file